c# - 在 C# 中将 byval C 结构编码(marshal)为返回值

标签 c# marshalling unmanaged managed

我有非托管代码:

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

我已经为 GetFoo 函数声明了 C# 原型(prototype):

    [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();

但是当我从 C# 代码调用 GetFoo 时,我总是遇到 MarshalDirectiveException- 方法的类型签名与 PInvoke 不兼容。我应该如何声明 C# 原型(prototype)?

最佳答案

是的,返回结构的函数往往难以互操作。这样的结构必须是blittable 以便 pinvoke 编码器可以将指针传递给函数,准备好写入返回值。 “blittable”意味着托管代码中的结构布局需要与结构的非托管布局相同。如果不是,则需要制作副本,pinvoke 编码器不想在返回值的特定情况下制作该副本。

bool 类型是一个互操作问题,不同的运行时做出不同的选择。它在 C 中往往是 4 个字节(与 Windows BOOL 类型相比,也是 pinvoke 的默认值),在 COM interop(又名 VARIANT_BOOL)中是 2 个字节,在 C++ 中是 1 个字节,在 CLR 中是 1 个字节。由于目标运行时未知,CLR 无法猜测哪个选择是正确的。 BOOL 是默认值,4 个字节。

即使使用 [MarshalAs(UnmanagedType.U1)] 强制完全匹配也不会使其成为 blittable。这很奇怪,我认为这是一个 CLR 错误。一个好的解决方法是将其替换为 byte,您可以使用属性将其包装回 bool。请注意,发布的代码片段中有很多错误,我使这个版本有效:

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;   
}  

关于c# - 在 C# 中将 byval C 结构编码(marshal)为返回值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4845128/

相关文章:

c# - 如何将 C# 字符串数组编码为 VB6 数组?

c# - 使用结构数组和大小参数索引编码(marshal) C# 结构

c# - 带有 RGiesecke.DllExport 的 C# DLL 中没有函数

c# - 如何检查我的解决方案是否包含非托管代码?

c# - 如何从 Windows 窗体中删除默认控制按钮

c# - 具有程序集依赖性的 NGen 错误

c# - 创建包含类列表的类列表

c# - 非常量开关的替代方案

json - 停止 json.Marshal() 从 float 中去除尾随零

c# - 将默认无参数构造函数添加到结构的解决方法