Cast native pointer to a C++\\CLI managed object r

2019-02-25 17:26发布

问题:

I have a callback that is called through a delegate. Inside it I will need to treat the buffer data that arrive from a record procedure. Normally in a unmanaged context I could do a reinterpret_cast on dwParam1 to get the object reference. But in a manged context how can I cast a DWORD_PTR to a managed object ref?

    static void WaveInProc(HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) 
    {
        ControlLib::SoundDevice^ soudDevice = ?cast_native2managed?(dwParam1);

回答1:

Here ya go, more or less what gcroot (per my comment above) does:

using namespace System;
using namespace System::Runtime::InteropServices;

// track an object by a normal (not pinned) handle, encoded in a DWORD_PTR
DWORD_PTR ParamFromObject(Object^ obj)
{
    return reinterpret_cast<DWORD_PTR>(GCHandle::ToIntPtr(GCHandle::Alloc(obj)).ToPointer());
}

static void WaveInProc(PVOID hwi, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) 
{
    // unwrap the encoded handle...
    GCHandle h = GCHandle::FromIntPtr(IntPtr(reinterpret_cast<void*>(dwParam1)));
    try
    {
        // and cast your object back out
        SoundDevice^ x = safe_cast<SoundDevice^>(h.Target);
    }
    finally
    {
        // remember to free the handle when you're done, otherwise your object will never be collected
        GCHandle::Free(h);
    }
}


回答2:

I'm not a C++/CLI expert but at a glance I don't think this is possible. If it were possible it would be very much unsafe. The basic problem here is that managed references and native pointers are not the same thing and don't support the same set of operations.

What makes me think this is not possible is that managed objects can move around in memory. Garbage collection operations for instance compact and move memory which correspondingly changes the addresses of objects. Hence it's not possible to have a raw pointer / address value of a managed object because any given GC could invalidate it.

This is not strictly true as you can pin an object in memory. But even then I don't think there is a way to get C++ to treat an arbitrary address as a managed handle.

I think a much safer approach would be the following.

  1. Wrap the managed object inside of a native object
  2. Pass the address of the native object throughout your API's
  3. Use reinterpret_cast to get back to the native type and then safely access your managed handle.