Marshaling byval C-structure as return value in C#

丶灬走出姿态 提交于 2019-12-07 09:00:01

问题


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've declare C# prototype for 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 calling GetFoo from C# code I alway have MarshalDirectiveException- Method's type signature is not PInvoke compatible. How I should declare C# prototype?


回答1:


Yes, functions that return a structure tend to be difficult to interop with. Such a structure has to be blittable so the pinvoke marshaller can pass a pointer to the function, ready for it to write the return value. Being "blittable" means that the structure layout in managed code needs to be identical to the unmanaged layout of the structure. If it is not then a copy needs to be made, the pinvoke marshaller does not want to make that copy in the specific case of a return value.

The bool type is an interop problem, different runtimes made different choices. It tends to be 4 bytes in C (compare to the Windows BOOL type, also the default for pinvoke), 2 bytes in COM interop (aka VARIANT_BOOL), 1 byte in C++, 1 byte in the CLR. Since the target runtime is unknown, the CLR cannot guess which choice is right. BOOL is the default, 4 bytes.

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

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;  
    f.a = 42;
    f.b = true;
    f.c = 101;
    return f;   
}  


来源:https://stackoverflow.com/questions/4845128/marshaling-byval-c-structure-as-return-value-in-c-sharp

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!