通过 Interop 调用一个方法,其中 returns 一个结构的实例

Call a method through Interop which returns an instance of a struct

我是 Interop 的新手,需要从 C# 调用托管 C++ 方法,其中 returns 以下 struct 的实例:

typedef struct DataBlock_ {
  unsigned char data[10240];
  unsigned int numberOfBytes;
  unsigned long int startAddr;    
} DataBlock;

returns实例声明的C++方法如下:

__declspec(dllexport) DataBlock getDefaultPass( void ) 
{
    DataBlock default_pass = {
        {
            (char)0xFF,(char)0xFF,(char)0xFF,(char)0xFF,
            (char)0xFF,(char)0xFF,(char)0xFF,(char)0xFF,
            (char)0xFF,(char)0xFF,(char)0xFF,(char)0xFF,
            (char)0xFF,(char)0xFF,(char)0xFF,(char)0xFF,
            (char)0xFF,(char)0xFF,(char)0xFF,(char)0xFF,
            (char)0xFF,(char)0xFF,(char)0xFF,(char)0xFF,
            (char)0xFF,(char)0xFF,(char)0xFF,(char)0xFF,
            (char)0xFF,(char)0xFF,(char)0xFF,(char)0xFF
        },
        32,
        0xFFE0
    };
    return default_pass;
}

我在 C# 中声明了结构和方法如下:

public static partial class My
{
    [StructLayout(LayoutKind.Sequential)]
    public struct DataBlock
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10240)]
        public byte[] data;
        //public fixed byte data[10240]; <-- this requires 'unsafe' but still doesn't work      
        public UInt32 numberOfBytes;
        public UInt32 startAddr;
    }

    [DllImport("my.dll")]
    static public extern DataBlock getDefaultPass( );

    [DllImport("my.dll")]
    static public extern byte sendPassword(DataBlock data);
}

我从 C# 调用方法如下:

var defaultPassword = My.getDefaultPass();
var response = My.sendPassword(defaultPassword);

但是对 getDefaultPass() 的调用抛出

An unhandled exception of type 'System.Runtime.InteropServices.MarshalDirectiveException' occurred in ConsoleApplication1.exe

Additional information: Method's type signature is not PInvoke compatible.

基于this question, I tried changing the declaration of data to public fixed byte data[10240] and marked the struct as unsafe, but then the method returns an instance with numberOfBytes and startAddr set to 0, and the subsequent call to sendPassword() fails (note that in this answer,后续调用使用指向结构的指针而不是实例本身,就像我的情况一样)。那么我应该如何从 C# 调用方法?

该项目面向 .NET 3.5 和 x86。

在此先感谢您的帮助。

struct 很好 - 它满足在 P/Invoke.

中用作 return 值的所有规则

您需要使用正确的调用约定(在您的情况下,CallingConvention.Cdecl)。

还有一些编译器使用的额外优化,其中大结构(例如您的结构)通过引用传递,而不是 returned。您可以像这样在 C# 中复制它:

static public extern void getDefaultPass(out DataBlock data);

为了完整性和补充 Luaan 的回答,由于问题中的 C++ 方法没有参数,我想涵盖方法确实有参数的情况,特别是因为它与 out 参数,当方法接受 2 个或更多参数时。

考虑 C++ 方法

__declspec(dllexport) DataBlock readText(char * dataArray , int bytesToRead)

在 C# 方法中 out 参数应该是第一个还是最后一个并不是很明显。与框架约定将out参数放在最后一个参数(如TryParse)相反,这里必须是第一个参数,否则会调用失败:

[DllImport("my.dll", CallingConvention = CallingConvention.Cdecl)]
static public extern void readText(out DataBlock dataBlock, string dataArray, int bytesToRead);