我有一个C ++ dll,使用以下方法:
//C++ dll method (external)
GetServerInterface(ServerInterface* ppIF /*[OUT]*/)
{
//The method will set ppIF
}
//ServerInterface is defined as:
typedef void * ServerInterface;
要从C#项目访问dll,我创建了一个C ++ / CLI项目并声明了一个托管类,如下所示:
public ref class ComWrapperManager
{
//
//
ServerInterface _serverInterface;
void Connect();
//
//
}
我使用Connect()方法调用GetServerInterface,如下所示。第一次通话有效,第二次通话无效。有人可以解释原因吗?我需要将该指针持久化为托管类中的成员变量。有没有更好的方法呢?
void Connect()
{
ServerInterface localServerInterface;
GetServerInterface(&localServerInterface); //THIS WORKS
GetServerInterface(&_serverInterface); //THIS DOESNT
//Error 1 error C2664: 'ServerInterface ' :
//cannot convert parameter 1 from //'cli::interior_ptr<Type>'
//to 'ServerInterface *'
}
答案 0 :(得分:6)
您正在将指针传递给托管对象的成员。这样的指针是特殊的,称为内部指针。它们由垃圾收集器跟踪,它会在GC压缩堆时移动托管对象时修改指针值。
问题是,您将该指针传递给非托管代码。 GC无法修改本机代码正在使用的指针值的副本。现在,当另一个线程触发垃圾收集时,灾难就会发生,就像本机代码正在执行并取消引用指针一样。该对象不再存在于原始地址。非常非常糟糕。并且极难诊断,因为它不太可能发生。
编译器可以看到你犯了这个错误。并抱怨C2664。
解决方法是传递一个指针,该指针存储在一个不会被GC移动的内存位置。这样的位置很容易得到,本地变量有资格。它存储在堆栈中,不会被移动。所以让它看起来像这样:
void Connect()
{
ServerInterface temp;
GetServerInterface(&temp);
this->_serverInterface = temp;
// etc..
}
您已经发现了自己,只是不要忘记分配班级成员。
答案 1 :(得分:1)
这就是为什么你不能做第二个:_serverInterface
是一个void指针,它是托管类的一部分。想想垃圾收集器做了什么......它允许在内存中移动托管对象但是它想要,所以void指针的地址可以随时改变。因此,使用该地址无效。
有两种解决方案:
在这两个解决方案中,我更喜欢#1,你已经实现的解决方案:解决方案#2在垃圾收集器想要重新排列的空间中间引入了一块不可移动的内存。如果有选择,我宁愿不要对垃圾收集器施加阻碍。