Marshaling Delegates as Struct/Class Members

B

Bob

I understand delegates do not need to be pinned, but its the class object
itself I need pinned. Take this code:


[StructLayout(LayoutKind.Sequential)]
public class TestObject
{
[MarshalAs(UnmanagedType.FunctionPtr)]
public AddDelegate Add;
[MarshalAs(UnmanagedType.FunctionPtr)]
public SubtractDelegate Subtract;

public delegate Int32 AddDelegate(Int32 a, Int32 b);
public delegate Int32 SubtractDelegate(Int32 a, Int32 b);
}


public class Program
{
private static void Main(
String[] args)
{
TestObject testObject = new TestObject();
GCHandle gcHandle = GCHandle.Alloc(testObject,
GCHandleType.Pinned);
}
}


The GCHandle.Alloc fails with:


System.ArgumentException was unhandled
Message="Object contains non-primitive or non-blittable data."
Source="mscorlib"
StackTrace:
at System.Runtime.InteropServices.GCHandle.InternalAlloc(Object
value, GCHandleType type)
at System.Runtime.InteropServices.GCHandle.Alloc(Object value,
GCHandleType type)


Is there an easy way to marshal delegates as part of a struct? Isn't that
what UnmanagedType.FunctionPtr is for? I don't want to manage multiple
objects and manually marshal stuff using
Marshal.GetFunctionPointerForDelegate? I -really- want to avoid that route
in this particular project.

Thanks
 
B

Bob

Furthermore, why can I marshal this class but I can't pin it?

Int32 size = Marshal.SizeOf(typeof(TestObject));
IntPtr ptr = Marshal.AllocCoTaskMem(size);
Marshal.StructureToPtr(testObject, ptr, false);
Marshal.FreeCoTaskMem(ptr);

I would think anything on the managed heap that can be marshalled could be
pinned. Is this wrong?

Thanks
 
N

Nicholas Paldino [.NET/C# MVP]

Bob,

Yes, this is. See my reply to your initial post for why.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Bob said:
Furthermore, why can I marshal this class but I can't pin it?

Int32 size = Marshal.SizeOf(typeof(TestObject));
IntPtr ptr = Marshal.AllocCoTaskMem(size);
Marshal.StructureToPtr(testObject, ptr, false);
Marshal.FreeCoTaskMem(ptr);

I would think anything on the managed heap that can be marshalled could be
pinned. Is this wrong?

Thanks

Bob said:
I understand delegates do not need to be pinned, but its the class object
itself I need pinned. Take this code:


[StructLayout(LayoutKind.Sequential)]
public class TestObject
{
[MarshalAs(UnmanagedType.FunctionPtr)]
public AddDelegate Add;
[MarshalAs(UnmanagedType.FunctionPtr)]
public SubtractDelegate Subtract;

public delegate Int32 AddDelegate(Int32 a, Int32 b);
public delegate Int32 SubtractDelegate(Int32 a, Int32 b);
}


public class Program
{
private static void Main(
String[] args)
{
TestObject testObject = new TestObject();
GCHandle gcHandle = GCHandle.Alloc(testObject,
GCHandleType.Pinned);
}
}


The GCHandle.Alloc fails with:


System.ArgumentException was unhandled
Message="Object contains non-primitive or non-blittable data."
Source="mscorlib"
StackTrace:
at System.Runtime.InteropServices.GCHandle.InternalAlloc(Object
value, GCHandleType type)
at System.Runtime.InteropServices.GCHandle.Alloc(Object value,
GCHandleType type)


Is there an easy way to marshal delegates as part of a struct? Isn't
that what UnmanagedType.FunctionPtr is for? I don't want to manage
multiple objects and manually marshal stuff using
Marshal.GetFunctionPointerForDelegate? I -really- want to avoid that
route in this particular project.

Thanks
 
N

Nicholas Paldino [.NET/C# MVP]

Bob,

See inline:
Is there an easy way to marshal delegates as part of a struct?

Yes, there is. Delegates will be marshaled to unmanaged memory as
function pointers. However, you are trying to pin an class instance in
managed code. These are two VERY different operations. When you marshal a
class to unmanaged code with delegates, it will create a thunk for the
delegates which holds a reference to them.
Isn't that what UnmanagedType.FunctionPtr is for?

It's for marshaling delegates, not for pinning managed objects. While
things are pinned when they are sent to unmanaged code, this applies to
blittable types only. When you marshal certain structures/classes (with
non-blittable fields), they are copied into an unmanaged buffer, the actual
reference is not passed.
I don't want to manage multiple objects and manually marshal stuff using
Marshal.GetFunctionPointerForDelegate? I -really- want to avoid that
route in this particular project.

You haven't described what you are trying to do? Based on what I have
seen, my guess is that you are passing a structure with function pointers to
a piece of unmanaged code, which is held, and then called intermittently at
various times. If this is the case, then you don't have to worry about
pinning the class instance/structure, the P/Invoke layer will take care of
it for you.

Basically, you would create an instance of your class, and then pass it
to your unmanaged function. The marshaler will marshal the function
pointers correctly. The key here is keeping that managed instance alive for
as long as the unmanaged function will call those delegates (I assume there
is a function to unregister the functions as well). To do this, you could
place it in a field on an object, or a static field, if it will exist
throughout the lifetime of the app (or almost that long).

Hope this helps.
 
B

Bob

You haven't described what you are trying to do? Based on what I have
seen, my guess is that you are passing a structure with function pointers
to a piece of unmanaged code,

You are close to what I am trying to accomplish. I need to marshal an
object which contains a pointer to the object which has function pointers.
So I am trying to pin the object which contains function pointers, use
GCHandle.AddrOfPinnedObject to get the address of the object, and set the
pointer in my primary object which needs to be marshalled:

obj1 <-- marshal this object
[Int32]
[Int32]
[IntPtr] <-- ptr to obj2
....

obj2 <-- pin this object
[IntPtr]
[IntPtr]
[Int32]
[Int32]
[FunctionPtr]
[FunctionPtr]
....

I've worked around this by allocating a buffer and marshalling obj2
manually. I don't really want to do that in this project, for various
reasons.

Thanks for your response.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top