16

I have run in to this problem of converting a C++/CLI pointer to a native C++ pointer. Heres the background: I'm writing a windows forms application using C++/CLI. The application makes call into a number of COM Interfaces. When creating an instance (inside of a C++/CLR class) of a object through the COM interface, i pass in (void**)(&provider) as the last argument to CoCreateInstance, as in:

HRESULT CoCreateInstance(rclsid, pUnkOuter, dwClsContext, riid, (void**)(&provider));

However, i get the compiler error: cannot convert from cli::interior_ptr<Type> to void ** . I've done som research into this, and it looks like it is a problem of different types of pointers in C++ and C++/CLI. Anyone has any knowledge about this, and maybe a tip on how it could be fixed? Thanx in advance!

First, thanx for all your help!

As Freich suggested, i tried to use the pin_ptr, but this instead made the compiler complaining about problems converting from interior_ptr to pin_ptr. If i instead try to use the interior_ptr as in:

pin_ptr<void *> pinnedPtr = &provider;
CoCreateInstance(CLSID_MSPRProvider, NULL, CLSCTX_LOCAL_SERVER, IID_IMSPRProvider, ( void** )pinnedPtr);

I get cannot convert from interior_ptr to interior_ptr. It all boils down to the problem of converting the interior_ptr to void**. Like this: (void**)interiorPtr, where interiorPtr of course is an interior_ptr. Any ideas in this?

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
Tomas Vinter
  • 2,690
  • 8
  • 36
  • 45

3 Answers3

12

I believe you have to mark the pointer as 'pinned' (in the managed code) and then copy the bytes over to some unmanaged memory region, and then use the pointer to that. For instance, here's a piece of code I once got somewhere which converts a pointer to a managed System::String to an UTF-8 encoded unmanaged std::string:

std::string managedStringToStlString( System::String ^s )
{
    Encoding ^u8 = Encoding::UTF8;
    array<unsigned char> ^bytes = u8->GetBytes( s );
    pin_ptr<unsigned char> pinnedPtr = &bytes[0];
    return string( (char*)pinnedPtr );
}

Note how I've got to get a 'pinned' pointer to the bytes array returned by the GetBytes() function, and then cast that to char* before passing it to the std::string constructor. I believe this is necessary to avoid that the managed memory subsystem moves the bytes array around in memory while I'm copying the bytes into the STL string.

In your case, try replacing

CoCreateInstance(rclsid, pUnkOuter, dwClsContext, riid, (void**)(&provider)); 

with

pin_ptr<void *> pinnedPtr = &provider;
CoCreateInstance(rclsid, pUnkOuter, dwClsContext, riid, (void**)pinnedPtr); 

and see whether that works.

Frerich Raabe
  • 90,689
  • 19
  • 115
  • 207
  • This seems to be dangerous! I had a similar (or same) situation before: I created a pinned pointer and returned the native pointer (like you do), but then the object was moved because the function ended and the pinned pointer was deleted, so the object was no longer pinned and the native pointer was invalid! This may or may not be seen during debugging, but for sure it will happen in your release version when you don't expect it. – Tobias Knauss Jan 16 '18 at 06:29
  • @TobiasKnauss the `std::string::string(const char *)` constructor creates a copy of the referenced data, so it's okay if the referenced memory is no longer pinned when the function exits. – Frerich Raabe Jan 16 '18 at 11:28
4

It's quite some time that I used C++/CLI, but I think you have to pin the pointer.

Achim
  • 15,415
  • 15
  • 80
  • 144
  • There is a convenience template called cli::pin_ptr that might be what the OP needs as it's designed to ensure that a pointer to a managed object is not being moved around by the garbage collector. – Timo Geusch Aug 13 '09 at 10:21
4

Is the memory pointed to expected to be valid beyond the call to CoCreateInstance()?

As Frerich and Achim alluded to, you can get a raw pointer to the memory, without fear of the memory being relocated, by "pinning" it and getting an interior pointer. You usually do this with the pin_ptr template (see Frerich post for an example).

However, pin_ptr is deliberately only usable on the stack. You cannot use it as member variable or a global, and you can't pass it as an argument or return value. The memory is unpinned when it leaves scope.

So if the memory is only required to be valid during the call to CoCreateInstance() then pin_ptr is the way to go. If not then you will have to take a copy of the memory and pass that on to CoCreateInstance()

philsquared
  • 22,403
  • 12
  • 69
  • 98