在C++/CLI中传递非托管指针

6
我正在创建一个依赖于许多C++静态库的C++/CLI包装器DLL。一些函数调用需要传递非托管指针,请问如何正确传递它们?
另外,其他函数需要将“this指针”作为void*传入。传递“this”的正确方法是什么?
下面是我的类定义...
public ref class RTPClient
{
    public:
        RTPClient();
        ~RTPClient();

        bool Connect();
        void Disconnect();

    private:
        CIsmaClient* mClient;
};

这是我的用法,其中涉及到的指针如下...
RTPClient::RTPClient():
    mClient(NULL)
{
    CIsmaClient::Create(&mClient, NULL, &AllocBuffer, &GetDataPointer, this);
}
&mClient和"this"的使用导致以下编译器错误... 1>.\VBLoadSimulatorDll.cpp(40):error C2664:'CIsmaClient::Create':无法将参数1从'cli::interior_ptr'转换为'CIsmaClient **' 1> with 1> [ 1> Type=CIsmaClient * 1> ]

1>.\VBLoadSimulatorDll.cpp(40):error C2664:'CIsmaClient::Create':无法将参数5从'VBLoadSimulator :: RTPClient ^const'转换为'VOID *'

2个回答

10
如果您正在传递指向托管类的指针,则很容易将 ^ 引用转换为指针,但必须固定托管对象,以便 GC 不会在内存中移动它(从而使指针无效)。
使用 pin_ptr 很简单。
但是,您的代码正在执行两个不起作用的操作。
RTPClient::RTPClient():
        mClient(NULL)
{
    CIsmaClient::Create(
        &mClient,          // 1
        NULL, 
        &AllocBuffer, 
        &GetDataPointer, 
        this);            //2
}

1) 您正在尝试获取托管堆上某个东西的地址(指针mClient的指针位置在托管堆上)。

因此,它可以在内存中移动,因此编译器提供了内部指针(其值在GC操作期间维护)。这需要固定,只有在Create函数不希望在作用域结束后使用指针时才能正常工作(如果将其传递到其他地方以存储它,这将导致错误)。

2) 您正在传递句柄(有趣的帽子符号)而不是指针。(阅读维基百科关于这些的部分,它们是很好的概述)这不能被非托管代码理解。

我能想到的这个参数在这种情况下的唯一原因是作为传递给后续函数回调的显式状态变量(如果我错了,请纠正我)。在这种情况下,'this'永远不会正常工作,因为一旦pin_ptr超出范围,它就可以在内存中移动。

考虑到这一点,这里是一个(部分)已更正的实现,清楚地说明了可以和不能修复的内容。

RTPClient::RTPClient():
        mClient(NULL)
{
    // make it clear you want the address of the instance variable
    pin_ptr<CIsmaClient*> pinnedClient = &this->mClient; 
    CIsmaClient::Create(
        pinnedClient,          // fixed
        NULL, 
        &AllocBuffer, 
        &GetDataPointer, 
        x /* pass something else in */);            //2
}

如果您提供更多关于最后一个参数用途的信息,我可以建议可能的解决方案。

如果它是未托管的,那么只传递一个普通的 C++ 指针就可以了。我不明白问题出在哪里.... - ShuggyCoUk
我遇到了这个错误...错误 C2664:“CIsmaClient::Create”:无法将参数 1 从“cli::interior_ptr<Type>”转换为“CIsmaClient **”。我传递的指针是我的托管类的成员变量,但它是指向非托管类的指针。 - cjserio
这是一个内部指针(即它指向由GC控制的内存中包含的值),因此您必须将其固定(这会固定包含对象)。只需使用pin_ptr(但请记住,生成的普通指针仅在堆栈上的pin_ptr处于作用域时有效)。 - ShuggyCoUk
实际上,重新阅读后我认为你可能有一个更大的问题。编辑你的问题,包括类代码和在编译器错误出现的使用代码。 - ShuggyCoUk

2

我认为这是通过void指针传递托管引用的最简单方法:

void SomeFunction(void* input)
{
  gcroot<ManagedClass^>* pointer = (gcroot<ManagedClass^>*)(input);
  (*pointer)->ManagedFunction();
}

void Example()
{
  ManagedClass^ handle = gcnew ManagedClass();
  gcroot<ManagedClass^>* pointer = new gcroot<ManagedClass^>(handle);
  SomeFunction((void*)pointer);
  delete pointer;
}

你为什么要使用 newdelete - Ben Voigt
因为您不能获取gcroot的地址。如果您想要,应该直接使用GCHandle。此外,在许多情况下,如果底层Object^在堆栈上,不要害怕使用Obect^*。所以,这里:SomeFunction(&handle)。输入必须强制转换为ManagedClass^*。 - reuns

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接