.NET Framework - Unmanaged to managed callback, and GCHandle / gcroot

Asked By DaTurk on 18-Jul-07 03:12 PM
Hi,

I'm coding up an application that has a native c++ layer,
asynchronously calling callback in a CLI layer.

We originally did this with static inline methods in the CLI layer,
but this solution only works with singleton objects.  So I have to
explore other solutions.

So beside pinning pointers, I've been looking at GCHandle.  I was
looking at this example from MSDN.

http://msdn2.microsoft.com/en-us/library/367eeye0(VS.80).aspx

They have two examples, one where the GCHandle has class scope, and
then another where it is static, and it's global.

My problem is that I need each CLI instance of this object to have
it's own GCHandle to it's own calback into the native world, will a
instance variable of GCHandle cover this?




Ben Voigt [C++ MVP] replied on 19-Jul-07 05:56 PM
Just use gcroot instead of GCHandle, it takes care of all the details.  You
can treat it like a T^ (tracking reference) except it is a member of a native
class instead of a managed class.
DaTurk replied on 24-Jul-07 01:23 PM
I cannot use gcroot, because I am using this reference in a unmanaged
class i.e. #pragma unmanaged.  And so it does not recognize the CLI
delegate reference.
Ben Voigt [C++ MVP] replied on 24-Jul-07 01:31 PM
Your shim will have to be #pragma managed, native class I believe (not ref
class).
DaTurk replied on 30-Jul-07 12:13 PM
I cannot though.  IT has to be a pragma unmanaged, because the classes
are callback classes that get activated via a strait c++ third party
orb.
Ben Voigt [C++ MVP] replied on 02-Aug-07 09:17 AM
Does this third-party code generator create source code, or binaries?  If
source code, no problem, compile with /clr and you can use unmanaged classes
all day.  If binaries, then you have bigger problems than .NET
compatibility -- to wit, lockin to a particular compiler version and
particular compile options.
DaTurk replied on 06-Aug-07 11:10 AM
Um, I'm not entirely sure about code generation.  We're talking about
CORBA here, all I know is I pass these "callback" c++ #pragma
unmanaged classes to the POA_MANAGER, and it "activates" them so that
I can use them to receive callbacks from the ORB.  So, I'm not sure if
that explains it really.  But because I'm plugging these classes into
a black box, third party, ORB magic thingy .... I'm reluctant to mix
too much tech.
Ben Voigt [C++ MVP] replied on 06-Aug-07 01:10 PM
What exactly do you give the manager?  Source code?  Binary interface
pointers?
A K replied to DaTurk on 22-Jul-10 06:53 AM
Something tells me that you tried to accomplish the same thing I am working on right now.



I have to create a .NET-wrapper around omniORB-stubs using managed and unmanage c++ code.



In order to enable callbacks from unmanaged to managed objects some unmanaged objects have to have references to managed objects.



For this purpose I used the gcroot<T> way storing a handle to these managed objects.



My objection is that calling methods on on of these gcroot<T> handles fails ...the assignment to the gcroot<T> member variable fails meaning that the handle references some weird uninitialized memory location...



Can you perhaps help me out with that? How did you solve these problems?
Matthew Magdits replied to A K on 22-Jul-10 01:31 PM
GCrooting didn't work in my situation either.  The solution I came up with is a bit long winded but it works.



First: Create a managed delegate, inside your managed class, with the same signature as the native method.  Obviously since we're mixing types we're in the CLI, or MC++ layer; I'm using CLI for this.



Second: Create a variable of that delegate type in the class.



Third: Creat a GCHandle variable in the same class.  We will use this variable to "root" the delegate in our managed land.



Fourth: Create a managed method with the same signature as the delegate we've created; this will be our managed callback method the native class will call into.



Fifth: Now we're ready to go, instantiate the delegate variable passing in the method you created in step 4.



Sixth: Allocate a handle to the above delegate storing the handle in the handle variable from step 3.

handleVar_ = GCHandle::Alloc(delegateVar_, GCHandle::Normal);



Seventh: Now we're ready to register our callback with the native class.  You should have a function ptr in the native class to hold the callback.  If you don't know how to do this there's loads of info about function ptr's.  You can create a functionPtr out of our managed delegate like this.



IntPtr ip = Marshal::GetFunctionPointerForDelegate(delegateVar_);

nativeClass_->RegisterCallback(static_cast<void(__stdcall *)(args)>(ip.ToPointer()));



This is assuming you have a register method in your native class, which you should.  You won't lose track of your managed callback in native land because it's been rooted via GCHandle.  We use this pattern all over the place and it works very well.



Hope this helps
Matthew Magdits replied to A K on 22-Jul-10 01:33 PM
Matthew Magdits == Daturk :)