FusterCluck
FusterCluck

Reputation: 503

Why can't I do Marshal.SizeOf() for this C# struct?

I try to call the code int size = Marshal.SizeOf(typeof(MyStruct)) but it throws the following exception:

Type 'MyStruct' cannot be marshaled as an unmanaged structure; no meaningful size or offset can be computed.

My struct is as follows:

[StructLayout(LayoutKind.Sequential)]
public struct MyStruct
{
    [MarshalAs(UnmanagedType.U4)]
    public UInt32 version;
    [MarshalAs(UnmanagedType.FunctionPtr)]
    public IntPtr Start;
    [MarshalAs(UnmanagedType.FunctionPtr)]
    public IntPtr Stop;
    // And a bunch more IntPtr, all declared the same way.
}

The struct is supposed to be passed to C-land, where the C code will use its contents as function pointers. I can't see how computing a size would fail, can anyone help?

Upvotes: 0

Views: 5999

Answers (1)

Hans Passant
Hans Passant

Reputation: 941545

UnmanagedType.FunctionPtr requires the field to be a delegate type. It will be a function pointer on the C-side after the structure is marshaled. Using [MarshalAs] is superfluous, a delegate already gets marshaled like that. So, roughly:

[StructLayout(LayoutKind.Sequential)]
public struct MyStruct
{
    [MarshalAs(UnmanagedType.U4)]
    public UInt32 version;
    public Action Start;
    public Func<bool> Stop;
    // etc..
}

Change the delegate types to match the function signature of the corresponding C function pointer. You often have to declare your own delegate type so you can give it the [UnmanagedFunctionPointer] attribute to match the calling convention of the C function. Usually CallingConvention.Cdecl, not the default of Stdcall.

You have to be very careful when you initialize a structure like this. The delegate objects you create and assign to the fields must be referenced elsewhere to prevent them from getting garbage collected. Either by storing them in a class object that's guaranteed to live as long as the C code can make calls, by storing them in a static variable or by explicitly adding a reference with GCHandle.Alloc()

Lots of ways to shoot your foot, good luck with it :)

Upvotes: 4

Related Questions