简体   繁体   English

将C语言结构编组为C#中的返回值

[英]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've declare C# prototype for GetFoo function: 我已经为GetFoo函数声明了C#原型:

    [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. 但是当我从C#代码调用GetFoo时,我总是有MarshalDirectiveException- Method的类型签名不兼容PInvoke。 How I should declare C# prototype? 我该如何申报C#原型?

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. 这样的结构必须是blittable,因此pinvoke编组器可以传递指向函数的指针,准备好它写入返回值。 Being "blittable" means that the structure layout in managed code needs to be identical to the unmanaged layout of the structure. “blittable”意味着托管代码中的结构布局需要与结构的非托管布局相同。 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. 如果不是,则需要进行复制,pinvoke marshaller不希望在返回值的特定情况下制作该副本。

The bool type is an interop problem, different runtimes made different choices. bool类型是一个互操作问题,不同的运行时间做出了不同的选择。 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. 它往往是C中的4个字节(与Windows BOOL类型相比,也是pinvoke的默认值),COM interop中的2个字节(又名VARIANT_BOOL),C ++中的1个字节,CLR中的1个字节。 Since the target runtime is unknown, the CLR cannot guess which choice is right. 由于目标运行时未知,因此CLR无法猜测哪个选项正确。 BOOL is the default, 4 bytes. BOOL是默认值,4个字节。

Even using [MarshalAs(UnmanagedType.U1)] to force an exact match does not make it blittable. 即使使用[MarshalAs(UnmanagedType.U1)]强制完全匹配也不会使它变得模糊。 Which is pretty odd, I consider this a CLR bug. 这很奇怪,我认为这是一个CLR错误。 A good workaround is to replace it with byte , you can use a property to wrap it back to a bool. 一个很好的解决方法是用byte替换它,你可以使用一个属性将它包装回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;   
}  

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM