【问题标题】:Deleting an unmanaged object from Finalize throws an AccessViolationException从 Finalize 中删除非托管对象会引发 AccessViolationException
【发布时间】:2017-12-17 11:28:36
【问题描述】:

我正在使用 C++/CLI 为非托管 C++ 项目编写一个包装库。目标是将此库公开给 C# 应用程序。这是我目前所拥有的。

#pragma once

#include "Settings.h"
#include "Settings/SettingsPrivate.h"

public ref class Settings
{
public:
    Settings();
    virtual ~Settings();

protected:
    !Settings();

public:
    unsigned char GetModel(int iNumber);


private:
    CSettings* m_pSettings;
};

#include "stdafx.h"
#include "Managed/Settings.h"

Settings::Settings()
{
    // Pointer to unmanaged object
    m_pSettings = new CSettings();
}

Settings::~Settings()
{
    this->!Settings();
}

Settings::!Settings()
{
    if (m_pSettings)
    {
        delete m_pSettings;
        m_pSettings = NULL;         
    }
}

unsigned char Settings::GetModel(int iNumber)
{
    return m_pSettingss->GetModel(iNumber);
}

代码在我编写的测试应用程序中执行良好。函数调用成功。问题是,当 GC 终结这个对象时,它会抛出一个异常。

Wrapper.dll 中出现“System.AccessViolationException”类型的未处理异常

附加信息:试图读取或写入受保护的内存。这通常表明其他内存已损坏。

我看不出引发此异常的任何明显原因。我尝试通过从我的 C# 应用程序调用 Dispose 来显式处理对象。它仍然抛出相同的异常。

这是测试应用程序:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace WrapperTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Settings settings = new Settings();
            byte b = settings.GetModel(0);

            settings.Dispose();

            return;
        }
    }
}

有人能指出我做错了什么吗?

【问题讨论】:

  • 没有复制,看不出问题。我认为问题出在 SettingsPrivate,我猜是堆损坏。
  • 如果您在调试器中启用第一次机会 AV 中断(“调试 -> 异常 -> Win32 异常 -> 访问冲突”;您可能希望在“工具 -> 中禁用“只是我的代码”选项 -> 调试”),正在访问什么(您还需要在 C# 项目中启用本机调试,因为我假设您正在使用它启动)?这个 sn-p 并没有让我觉得有什么不对。
  • @Hans:你昨晚的评论让我走上了解决问题的正确轨道。如果您发布答案,我会将这个问题归功于您。
  • 继续,发布您的答案以显示实际情况并将其标记为答案。每个编写非托管代码的人都可以从调试堆损坏问题中学到一些东西。

标签: c# visual-studio-2008 c++-cli


【解决方案1】:

这是一个项目配置错误。代码实际上在发布模式下运行良好。

在调试模式下,当我应该静态链接调试库时,我链接了一些发布 DLL。为什么这会导致内存损坏我还没有调查,但它已经解决了问题。

否则上面贴的代码是正确的。

【讨论】:

  • 这让我后来想起了 DLLMain 的问题。您不得抢占运行时(即在 CLR 引导之前没有运行代码)。
【解决方案2】:

您应该启用更详细的堆调试功能。我测试了你的代码,它对我来说并没有失败。我不得不使用 malloc/free 而不是 new/delete,因为你没有定义 CSettings,但是效果应该是一样的。

我添加了这个以确保我有足够的堆搅动来触发失败,如果它们有任何损坏;

unsigned char Settings::GetModel(int iNumber)
{   
    for(int i=0; i < iNumber; i++)
        free(malloc(1024));
    return iNumber;
}

对我来说,您的代码没有失败。您应该查看一些编译设置,是否链接到 CSettings 的外部库?如果是这样,您需要确保 CRT 是相同的版本,等等。您还知道您必须使用 DLL 运行时不是 /MT 或 /MTd 之一。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-01
    • 2019-05-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-29
    相关资源
    最近更新 更多