在 C# 中将 byval C 结构编组为返回值
我有非托管代码:
...
typedef struct foo
{
int a;
bool b
int c;
} FOO,*LPFOO;
....
__declspec(dllexport) FOO __stdcall GetFoo()
{
FOO f;
<some work>
return f;
}
....
我已经为 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();
但是当我从 C# 代码调用 GetFoo 时,我总是遇到 MarshalDirectiveException - 方法的类型签名与 PInvoke 不兼容。我应该如何声明 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:
[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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
是的,返回结构的函数往往难以互操作。这样的结构必须是可 blittable 的,以便 pinvoke 编组器可以将指针传递给函数,准备好写入返回值。 “blittable”意味着托管代码中的结构布局需要与结构的非托管布局相同。如果不是,则需要制作副本,pinvoke 编组器不希望在返回值的特定情况下制作该副本。
bool
类型是一个互操作问题,不同的运行时会做出不同的选择。在 C 中通常为 4 个字节(与 Windows BOOL 类型相比,也是 pinvoke 的默认类型),在 COM 互操作(又名 VARIANT_BOOL)中为 2 个字节,在 C++ 中为 1 个字节,在 CLR 中为 1 个字节。由于目标运行时未知,CLR 无法猜测哪个选择是正确的。 BOOL 是默认值,4 个字节。即使使用 [MarshalAs(UnmanagedType.U1)] 强制精确匹配也不会使其可直接传送。这很奇怪,我认为这是一个 CLR 错误。一个好的解决方法是将其替换为 byte,您可以使用属性将其包装回 bool。请注意,发布的代码片段中有很多错误,我使这个版本可以工作:
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 withbyte
, 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: