我有非托管代码:
...
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/