Marshaling byval C-structure as return value in C #

I have unmanaged code:

... typedef struct foo { int a; bool b int c; } FOO,*LPFOO; .... __declspec(dllexport) FOO __stdcall GetFoo() { FOO f; <some work> return f; } .... 

I am declaring a C # prototype for the GetFoo function:

  [StructLayout(LayoutKind.Sequential, Pack = 1)] private struct Foo { public int a; public bool b public int c; }; [DllImport("foo.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)] [return:MarshalAs( UnmanagedType.Struct)] private static extern Foo GetFoo(); 

But when I call GetFoo from C # code, I always have a MarshalDirectiveException. Method type is not compatible with PInvoke. How can I declare a C # prototype?

+4
source share
1 answer

Yes, functions that return a structure are generally difficult to interact with. Such a structure should be blittable, so that the pinvoke marker can pass a pointer to a function ready to write the return value. Being "blittable" means that the structure of the structure in managed code must be identical to the unmanaged location of the structure. If this is not so, then you need to make a copy, the pinwack marshaller does not want to make this copy in the specific case of the return value.

The bool type is an interoperability problem, and different execution modes make different options. It has 4 bytes in C (compared to the Windows BOOL type, also the default for pinvoke), 2 bytes in COM communication (aka VARIANT_BOOL), 1 byte in C ++, 1 byte in CLR. Since the target runtime is unknown, the CLR cannot figure out which choice is the right one. BOOL by default is 4 bytes.

Even using [MarshalAs(UnmanagedType.U1)] to force an exact match does not make it softer. Which is rather strange, I consider this a CLR bug. A good workaround is to replace it with byte , you can use the property to wrap it back in bool. Remember that there were a lot of mistakes in the published fragment, I made this version:

 using System; using System.Runtime.InteropServices; class Program { static void Main(string[] args) { Foo value = GetFoo(); } [StructLayout(LayoutKind.Sequential)] private struct Foo { public int a; private byte _b; public bool b { get { return _b != 0; } } public int c; }; [DllImport(@"c:\projects\consoleapplication3\debug\cpptemp10.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, EntryPoint = " _GetFoo@0 ")] private static extern Foo GetFoo(/*int CoreIndex*/); } 

 typedef struct foo { int a; bool b; int c; } FOO,*LPFOO; extern "C" __declspec(dllexport) FOO __stdcall GetFoo() { FOO f; fa = 42; fb = true; fc = 101; return f; } 
+8
source

Source: https://habr.com/ru/post/1337688/


All Articles