通过互操作调用方法,该方法返回结构的实例

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

本文关键字:方法 返回 结构 实例 互操作 调用      更新时间:2023-10-16

我是互操作的新手,需要从 C# 调用一个托管C++方法,该方法返回以下struct的实例:

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

返回实例的 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()的召唤抛出

类型的未处理异常 "System.Runtime.InteropServices.MarshalDirectiveException"发生在 控制台应用程序1.exe

其他信息:方法的类型签名不是 PInvoke 相容。

基于这个问题,我尝试将 data 的声明更改为 public fixed byte data[10240] 并将结构标记为 unsafe ,但随后该方法返回一个实例,numberOfBytesstartAddr 设置为 0,随后对 sendPassword() 的调用失败(请注意,在此答案中,后续调用使用指向结构的指针而不是实例本身, 就像我的情况一样(。那么我应该如何从 C# 调用方法呢?

该项目面向 .NET 3.5 和 x86。

提前感谢任何帮助。

struct很好 - 它满足在 P/Invoke 中用作返回值的所有规则。

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

一些编译器还使用了一个额外的优化,其中大结构(例如您的(通过引用传递,而不是返回。可以在 C# 中复制它,如下所示:

static public extern void getDefaultPass(out DataBlock data);

为了完整起见并补充 Luaan 的答案,由于问题中的C++方法没有参数,我想介绍方法确实有参数的情况,特别是当该方法采用 2 个或更多参数时,它与out参数的位置有关。

考虑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);