Converting System.Object^ to void*

  • Thread starter Thread starter Dilip
  • Start date Start date
D

Dilip

What is the best way to get a void* out of System.Object^?

Right now this is the way we go:

MyRefClass^ myreftype = ; // from somewhere
void* opaqueObj =
GCHandle::ToIntPtr(GCHandle::Alloc(myreftype)).ToPointer();

when we unpack:

void* p = //........
GCHandle ourWrapper = GCHandle::FromIntPtr(safe_cast<IntPtr>(p));
MyRefClass^ ourObject = ourWrapper.Target;

I am wondering if I can eliminate GCHandle from the picture and get to
what I want? There is a deeper problem w.r.t passing GCHandle between
appdomains and I was wondering if there is a simple out of this mess.
 
Dilip said:
What is the best way to get a void* out of System.Object^?

For what reason? If you need to pass the address of a managed object to
unmanaged code, you must pin it.

You can use pin_ptr<>, or if it's absolutely necessary,
GCHandle::Allocate with GCHandleType::Pinned passed to it.

If you don't pin the object, its address is free to change at any moment.

Tom
 
For what reason? If you need to pass the address of a managed object to
unmanaged code, you must pin it.

Tamas, I suspected that I would have to pin it. In my case I have
some code (lets call it 'rude_code' so that I can refer back to it
later) running on a secondary (non-default) appdomain that registers a
callback with unmanaged code. While registering the callback we have
to pass in the address of a managed object as a void*. When the
callback happens I get back the same void* which I have to break apart
and retrieve the System.Object^ type. When I have a GCHandle one of
the steps involved in breaking apart a void* is calling
GCHandle::FromIntPtr().

However the problem is I get a "GCHandle cannot be passed across
appdomains" exception on the FromIntPtr call, whenever rude_code is
made to run on the secondary appdomain. If I run rude_code in the
default appdomain everything works just fine!

Thats why I thought I'd circumvent the issue by not using a GCHandle
at all while converting a System.Object^ to a void* and still be able
to run rude_code in a secondary appdomain.

At this point my questions are mostly academic since I was forced to
back out this dual appdomain approach for lack of time but I am still
curious to know why I got that exception.

When unmanaged code calls back into managed code doesn't it know which
appdomain to select to execute?
 
Dilip said:
Tamas, I suspected that I would have to pin it. In my case I have
some code (lets call it 'rude_code' so that I can refer back to it
later) running on a secondary (non-default) appdomain that registers a
callback with unmanaged code. While registering the callback we have
to pass in the address of a managed object as a void*. When the
callback happens I get back the same void* which I have to break apart
and retrieve the System.Object^ type. When I have a GCHandle one of
the steps involved in breaking apart a void* is calling
GCHandle::FromIntPtr().

However the problem is I get a "GCHandle cannot be passed across
appdomains" exception on the FromIntPtr call, whenever rude_code is
made to run on the secondary appdomain. If I run rude_code in the
default appdomain everything works just fine!

Thats why I thought I'd circumvent the issue by not using a GCHandle
at all while converting a System.Object^ to a void* and still be able
to run rude_code in a secondary appdomain.
That will not work. You *must* either allocate a GCHandle or pin the object,
otherwise the object is eligible for garbage collection.

You can't really "convert" a managed reference to an unmanaged pointer. You
can only temporarily create unmanaged pointers to managed objects.
At this point my questions are mostly academic since I was forced to
back out this dual appdomain approach for lack of time but I am still
curious to know why I got that exception.

When unmanaged code calls back into managed code doesn't it know which
appdomain to select to execute?

In general, no, it doesn't -- you must use delegates for that. See
http://lambert.geek.nz/2007/05/29/unmanaged-appdomain-callback/
 
That will not work. You *must* either allocate a GCHandle or pin the object,
otherwise the object is eligible for garbage collection.

Gotcha Jeroen. I understand perfectly. FWIW, the code I inherited
just allocates a GCHandle and never pins it (using that Pinned
enumeration Tamas was talking about elsethread). I wonder how it has
been working all these years.
You can't really "convert" a managed reference to an unmanaged pointer. You
can only temporarily create unmanaged pointers to managed objects.



In general, no, it doesn't -- you must use delegates for that. Seehttp://lambert.geek.nz/2007/05/29/unmanaged-appdomain-callback/

Wow! Thats *exactly* what I ran into. Thanks Jeroen! How in the
world do you guys keep track of things like this??!! Seems like an
obscure problem to me...
 
Dilip said:
Gotcha Jeroen. I understand perfectly. FWIW, the code I inherited
just allocates a GCHandle and never pins it (using that Pinned
enumeration Tamas was talking about elsethread). I wonder how it has
been working all these years.
Allocating a GCHandle will prevent GC as well. Pinning is only necessary if
you need the address of the object in unmanaged code. The code fragment you
showed doesn't: it actually retrieves opaque handles when it calls
..ToIntPtr(), not direct pointers. These handles can be converted back and
passed to managed code for as long as the GCHandle remains allocated.
Wow! Thats *exactly* what I ran into. Thanks Jeroen! How in the
world do you guys keep track of things like this??!! Seems like an
obscure problem to me...

I didn't keep track; I've never seen this problem before (I've worked with
callbacks into unmanaged code and separate appdomains, just not in
combination). I simply googled for "delegate unmanaged appdomain". The
supplied site is the first hit. Knowing that delegates should probably be
involved is a guess, granted.

Now that I have seen it, however, I will keep track, in case anyone around
me ever runs into it. Repeat until all-knowing! :-)
 
Back
Top