将托管类成员传递给c ++方法

时间:2013-09-09 11:06:56

标签: pointers c++-cli

我有一个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 *'


    }

2 个答案:

答案 0 :(得分:6)

您正在将指针传递给托管对象的成员。这样的指针是特殊的,称为内部指针。它们由垃圾收集器跟踪,它会在GC压缩堆时移动托管对象时修改指针值。

问题是,您将该指针传递给非托管代码。 GC无法修改本机代码正在使用的指针值的副本。现在,当另一个线程触发垃圾收集时,灾难就会发生,就像本机代码正在执行并取消引用指针一样。该对象不再存在于原始地址。非常非常糟糕。并且极难诊断,因为它不太可能发生。

编译器可以看到你犯了这个错误。并抱怨C2664。

解决方法是传递一个指针,该指针存储在一个不会被GC移动的内存位置。这样的位置很容易得到,本地变量有资格。它存储在堆栈中,不会被移动。所以让它看起来像这样:

void Connect()
{
    ServerInterface temp;
    GetServerInterface(&temp);
    this->_serverInterface = temp;
    // etc..
}

您已经发现了自己,只是不要忘记分配班级成员。

答案 1 :(得分:1)

这就是为什么你不能做第二个:_serverInterface是一个void指针,它是托管类的一部分。想想垃圾收集器做了什么......它允许在内存中移动托管对象但是它想要,所以void指针的地址可以随时改变。因此,使用该地址无效。

有两种解决方案:

  1. 如您所述,您可以将堆栈变量的地址传递给非托管方法。与托管对象不同,当垃圾收集器执行其操作时,堆栈不会移动,因此地址不会更改。然后,您可以获取存储在堆栈变量中的数据并将其复制到类字段,这样可以正常工作,因为您没有处理它的地址。
  2. 正如其他回答者所说,您可以将托管对象锁定在内存中。一旦它无法移动,您可以毫无问题地获取void指针字段的地址。 (他正在展示C#语法,你正在寻找C ++ / CLI语法。我不是在编译器上检查,但我相信C ++ / CLI语法不一样。)
  3. 在这两个解决方案中,我更喜欢#1,你已经实现的解决方案:解决方案#2在垃圾收集器想要重新排列的空间中间引入了一块不可移动的内存。如果有选择,我宁愿不要对垃圾收集器施加阻碍。

相关问题