Why DllImport for C bool as UnmanagedType.I1 throws, but works as a byte

Here is a simple C code (VS 2013 C ++ project, "compiled as C"):

typedef struct { bool bool_value; } BoolContainer; BoolContainer create_bool_container (bool bool_value) { return (BoolContainer) { bool_value }; } 

Here is my P / Invoke wrapper

 public partial class NativeMethods { [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)] public static extern BoolContainer create_bool_container([MarshalAs(UnmanagedType.I1)] bool bool_value); } 

The following are managed versions of BoolContainer :

MarshalDirectiveException: Method type signature is not PInvoke compatible. thrown MarshalDirectiveException: Method type signature is not PInvoke compatible. :

 public struct BoolContainer // Marshal.SizeOf(typeof(BoolContainer)) = 1 { [MarshalAs(UnmanagedType.I1)] // same with UnmanagedType.U1 public bool bool_value; } 

Second job:

 public struct BoolContainer // Marshal.SizeOf(typeof(BoolContainer)) = 1 { public byte bool_value; } 

Test code:

 BoolContainer boolContainer = NativeMethods.create_bool_container(true); 

DllImport ignore MarshalAs for any logical members in the returned structure. Is there a way to keep bool in a managed declaration?

+3
source share
1 answer

Structures as return types are rather complicated in interop; they do not correspond to the CPU register. This is processed in native code by reserving the space on its stack stack for the return value and passing a pointer to the called party. Which then copies the return value through a pointer. In general, a rather unpleasant feature, different C compilers have different strategies for passing this pointer. The pinvoke router assumes MSVC behavior.

This is a problem with structures that are not blittable, in other words, when its layout is no longer an exact match with an unmanaged structure that needs its own code. This requires an additional step to convert the unmanaged structure to a managed structure equivalent to Marshal.PtrToStructure (). You see this, for example, if you add a string field to your structure. This makes it non-blittable, since the string C must be converted to System.String, you will get the same exception.

A similar problem in your case, bool is also a very complex type to interact with. Called by a typeless C language, and it is added later with a very incompatible choice. These are 1 byte in C ++ and CLR, 2 bytes in COM, 4 bytes in C and winapi. The pinvoke router has chosen the winapi version as the standard, which makes it the same as BOOL, the most common reason for pinvoke. This made you add the [MarshalAs] attribute and also require additional work, which again will be done using the equivalent of Marshal.PtrToStructure ().

That the pinvoke marker is missing is support for this additional step, a custom marshaling of the return value after the call. Not sure if this is a project constraint or resource constraint. Probably, both of them, the possession of non-blittable structure elements is completely undefined in unmanaged languages ​​and guessing on it, especially as a return value, very rarely turns out well. Thus, they could well make the call that working on a function simply did not cost a low chance of success. Of course, this is an assumption.

Using a byte instead is a great workaround.

+4
source

All Articles