Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing Unmanaged pointers in C++/CLI

I'm creating a C++/CLI wrapper DLL that depends on numerous C++ static libraries. Some of the function calls expect unmanaged pointers to be passed in. How do i pass them properly?

Also, other functions expect a "this pointer" to be passed in as a void*. What's the right way to pass "this"?

Here's my class definition...

public ref class RTPClient
{
    public:
        RTPClient();
        ~RTPClient();

        bool Connect();
        void Disconnect();

    private:
        CIsmaClient* mClient;
};

Here's my usage where the pointers in question are used...

RTPClient::RTPClient():
    mClient(NULL)
{
    CIsmaClient::Create(&mClient, NULL, &AllocBuffer, &GetDataPointer, this);
}

The usage of &mClient and "this" cause the following compiler errors... 1>.\VBLoadSimulatorDll.cpp(40) : error C2664: 'CIsmaClient::Create' : cannot convert parameter 1 from 'cli::interior_ptr' to 'CIsmaClient **' 1> with 1> [ 1> Type=CIsmaClient * 1> ]

1>.\VBLoadSimulatorDll.cpp(40) : error C2664: 'CIsmaClient::Create' : cannot convert parameter 5 from 'VBLoadSimulator::RTPClient ^const ' to 'VOID *'

like image 717
cjserio Avatar asked Feb 20 '09 14:02

cjserio


2 Answers

If you are passing a pointer to a managed class then it is easy to convert the ^ reference into a pointer but you must pin the managed object so that the GC doesn't move it about in memory (thus invalidating the pointer)

This is simple with pin_ptr

However your code is doing two things which won't work

RTPClient::RTPClient():
        mClient(NULL)
{
    CIsmaClient::Create(
        &mClient,          // 1
        NULL, 
        &AllocBuffer, 
        &GetDataPointer, 
        this);            //2
}

1) You are trying to take the address of something on the managed heap (the location of the pointer to the pointer mClient is on the managed heap.

As such it can move in memory, thus the compiler supplier interior pointer (whose value is maintained over GC operations). This needs to be pinned and this will only work if the Create function does not expect to use the pointer after it's scope has ended (if it passes it anywhere else to store it this will lead to bugs).

2) You are passing a handle (the funny hat symbol) rather than a pointer. (Read the wikipedia section on these they are a good overview) This is not (and cannot) be understood by the unmanaged code.

The only reason I can think of for this parameter in this context is as an explicit state variable passed to subsequent function callbacks (correct me if I'm wrong). 'this' in this context is NEVER going to work properly since this can move about in memory once the pin_ptr goes out of scope.

With that in mind here is a (partially) corrected implementation making it clear what can and can't be fixed.

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
}

If you supply more information on what the last parameter is used for I can suggest possible solutions

like image 169
ShuggyCoUk Avatar answered Oct 06 '22 01:10

ShuggyCoUk


I think you'll that this is the simplest way of passing a managed reference via a void pointer:

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;
}
like image 24
John Stewien Avatar answered Oct 06 '22 00:10

John Stewien