views:

526

answers:

2

hi, 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. Normaly 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);
A: 

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.
JaredPar
You can certainly get a raw pointer to a managed object, however it is unsafe. The three step approach you're talking about here is already implemented by the gcroot/auto_gcroot<> templates. Effectively you're just passing around a GCHandle by IntPtr value.
Nathan Howell
+1  A: 

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);
    }
}
Nathan Howell
to the momment I decided to make the audio class in pure c++ but the info can be handy.thanks by the help!
Ruben Trancoso