[英]Marshaling byval C-structure as return value in 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- Method的类型签名不兼容PInvoke。 我该如何申报C#原型?
是的,返回结构的函数往往难以互操作。 这样的结构必须是blittable,因此pinvoke编组器可以传递指向函数的指针,准备好它写入返回值。 “blittable”意味着托管代码中的结构布局需要与结构的非托管布局相同。 如果不是,则需要进行复制,pinvoke marshaller不希望在返回值的特定情况下制作该副本。
bool
类型是一个互操作问题,不同的运行时间做出了不同的选择。 它往往是C中的4个字节(与Windows BOOL类型相比,也是pinvoke的默认值),COM interop中的2个字节(又名VARIANT_BOOL),C ++中的1个字节,CLR中的1个字节。 由于目标运行时未知,因此CLR无法猜测哪个选项正确。 BOOL是默认值,4个字节。
即使使用[MarshalAs(UnmanagedType.U1)]
强制完全匹配也不会使它变得模糊。 这很奇怪,我认为这是一个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;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.