views:

425

answers:

2

Here is a C# program that tries Marshal.SizeOf on a few different types:

using System;
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential)]
class AClass { }

[StructLayout(LayoutKind.Sequential)] 
struct AStruct { }

[StructLayout(LayoutKind.Sequential)]
class B { AClass value; }

[StructLayout(LayoutKind.Sequential)]
class C<T> { T value; }

class Program
{
    static void M(object o) { Console.WriteLine(Marshal.SizeOf(o)); }

    static void Main()
    {
        M(new AClass());
        M(new AStruct());
        M(new B());
        M(new C<AStruct>());
        M(new C<AClass>());
    }
}

The first four calls to M() succeed, but on the last one, SizeOf throws an ArgumentException:

"Type 'C`1[AClass]' cannot be marshaled as an unmanaged structure; no meaningful size or offset can be computed."

Why? Specifically, why does SizeOf choke on C<AClass>, but not on B or on C<AStruct>?


EDIT: Because it was asked about in the comments, here's the "real-world" problem that inspired this mostly-academic question: I'm calling into a C API that is basically one C function that operates on (pointers to) lots of different types of simple C structures. All contain a common header followed by one field, but the type of that field is different in different structures. A flag in the header indicates the type of the field. (Strange, yes, but that's what I have to work with).

If I could define a single generic type C<T> and a single C# extern declaration M(C<T>), and then call M(C<int>) on one line, and M(C<double>) on another, I'd have a short and sweet interop solution. But given JaredPar's answer, it appears that I have to make a separate C# type for each structure (though inheritance can provide the common header).

A: 

It is not know what size would aggregated object T have (it would be size of pointer if T is referenece type and mostly any value if it is value type).

I think you can solve this problem by setting MarshalAs attribute on field 'value' specifying most matching type (E.g. Unmanagedtype.SysInt). Note that it still won't work for so-called non-mappable types (i.e. types for which fields offsets and sizes can't be deduced easily).

But AFAIK, it's not recommended to use generics in interop.

elder_george
+2  A: 

Generics as a rule are not supported in any interop scenario. Both PInvoke and COM Interop will fail if you attempt to Marshal a generic type or value. Hence I would expect Marshal.SizeOf to be untested or unsupported for this scenario as it is a Marshal specific function.

JaredPar