【问题标题】:Tracking Reference to Native Reference problem - C# to C++/CLI to C++ AccessViolationException跟踪对 Native Reference 问题的引用 - C# to C++/CLI to C++ AccessViolationException
【发布时间】:2011-08-05 22:38:38
【问题描述】:

我正在尝试使用 VS2010 针对我只能访问头文件和 dll/lib 文件的 C++ API 编写 C++/CLI 包装器。

我遇到了将本机引用从本机代码冒泡备份到 C# 代码中的跟踪引用的问题。我得到一个 SystemAccessViolation。代码如下:

用于返回对创建以及修改 CustomObject 的引用的本机函数的非托管本机 C++ 标头:

class ManageCustomObject;
    _declspec(dllimport) errorCode CreateCustomObject(CustomObject& customObject);
    _declspec(dllimport) errorCode ModifyCustomObject(CustomObject& customObject);

关于 CustomObject 需要注意的重要一点是它只有私有构造函数:

CustomObject(const CustomObject&) { }
CustomObject& operator=(const CustomObject&) { return *this; }

现在,我的 C++/CLI 层方法如下所示:

public ref class WrapperManageCustomObject {
public:
        errorCode WrapperCreateCustomObject(CustomObject% customObject)
        {
            return CreateCustomObject(customObject);
       }

        errorCode WrapperModifyCustomObject(CustomObject% customObject)
        {
            return ModifyCustomObject(customObject);
        }
};

CustomObject 的包装器如下所示:

public class WrapperCustomObject : public CustomObject {
    public:
        WrapperCustomObject(const CustomObject&) {};
};

在 C# 中,代码如下:

WrapperCustomObject wrapperCustomObject;
WrapperManageCustomObject wrapperManageCustomObject = new WrapperManageCustomObject();

long result;

// this works great
result = wrapperManageCustomObject.CreateCustomObject(ref wrapperCustomObject);

// can utilize wrapperCustomObject here with other native functions no problem, so long as I don't try to modify it...
...

// this throws an AccessViolationException was unhandled message
// essentially I am modifying / returning a different native customObject (though I'm not 100% sure what it does as I do not have access to the code)
result = wrapperManageCustomObject.ModifyCustomObject(ref wrapperCustomObject);

正如您在我的 cmets 中看到的那样,尝试通过引用修改该对象会引发“AccessViolationException 未处理:尝试读取或写入受保护的内存。这通常表明其他内存已损坏。”

现在,如果我从这个等式中消除 C# 和跟踪引用,并在 C++ 或 C++/CLI 中执行所有这些操作(只要不存在跟踪引用),就可以正常工作。我认为这与本机代码试图访问更新现在在托管区域中的引用有关。

有没有什么办法可以从本质上使这段代码在 C# 中“工作”?我知道,没有办法传递对 C# 的本机引用。

【问题讨论】:

  • 如果 CustomObject 有一个私有构造函数并且没有工厂函数,你应该如何在 C++ 领域创建一个 CustomObject?您需要已经有一个 CustomObject 实例才能调用 CreateCustomObject 辅助函数。
  • 你是如何编译的?您正在返回一个 native 对象指针,但 C# 代码将其视为 managed 包装器。是的,当你使用它时,它会很大。本机对象指针应该是包装器的私有成员。
  • Stu - 我也可以将这些作为“out”参数,但仅此而已。参考参数是 C# 的东西。
  • Hans - C++/CLI 允许跟踪引用。信不信由你,这确实有效。
  • Adam - 如果函数已经返回对一个的引用,您不必在 C++ 领域创建 CustomObject - 意思是,我假设我看不到的 .cpp 文件是创建实例的位置并举行。您只需要一个容器来保存该引用。即:自定义对象自定义对象;在调用该函数之前。

标签: c# c++ c++-cli pass-by-reference


【解决方案1】:

托管引用与原生引用完全不同。托管引用更像本机指针。据我所知,你不能做像public class WrapperCustomObject : public CustomObject (其中CustomObject 是一个本地类)这样的事情,并且期望它能够工作。创建托管类时,您是在告诉 CLR,您的类在内存中移动时表现良好。如果CustomObject 完全用指针做任何事情,那将不再是真的。 CustomObject 不使用托管引用,因此运行时无法知道在/何时移动该类实例时更新客户端的位置。

更糟糕的是,您的包装器允许在不通过工厂方法的情况下实例化该类。因此,如果工厂方法CreateCustomObject 对您返回的对象做了任何重要的事情,那么您就违反了您正在调用的代码的约定,该约定要求您仅通过工厂方法创建该对象的实例。

【讨论】:

  • 奇怪的是,您实际上可以将公共类 WrapperCustomObject 作为托管派生类从公共 CustomObject(非托管本机类)继承,它实际上会“工作”。但是,正如您所指出的,我同意 - 限制会迅速增加,我同意您所说的。
  • 也许我现在正在寻找的是任何可能的方法来绕过这种方法,使其“可能正常工作”。当 CreateCustomObject 仅通过引用返回该对象时,我似乎找不到任何有用的方法 - 这是我可以有效创建或保存它的唯一方法。使用引用非常困难 - 除了可能的 C++ 静态单数实例之外,您根本无法存储它们,或者,正如我所展示的 - 通过托管 C# 保存对它的跟踪引用。
  • @Brian:不,它实际上并没有“工作”——仅仅因为它编译并不意味着它在运行时不会失败(事实上它确实如此)。它可以编译是因为 C++ 类可能 是无状态的,或者可能 是无指针的,在这种情况下它会起作用。但是鉴于您不知道该类的实现,它不是您可以依赖的东西。至于使用“困难”的引用,只有当您尝试将本机引用视为托管引用时,它们才会变得困难。他们彼此没有任何关系。如果您想要托管参考,请创建一个。
  • @Brian:您不能将托管引用转换为本地引用,反之亦然。时期。如果你想要一个包装器,你实际上必须制作一个包装器,并为本地方法公开托管等效项。
  • 同意。我所有的研究都指出无法从托管/跟踪引用中检索本机引用。但是,我仍然注意到,一旦向外,来自 C++/CLI 的跟踪引用可以“工作”——甚至向内返回到非托管代码,只要目标引用对象在非托管代码中没有改变——因为非托管代码无法访问托管堆栈。也同意,进一步 - 它取决于创建的该类的无状态实例。此处的解决方案是仅在本机代码中保存任何本机引用 - 不要涉及跟踪引用
猜你喜欢
  • 2013-01-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多