通过Interop调用一个方法,它返回一个struct的实例

时间:2016-02-03 13:01:23

标签: c# c++ .net interop

我是Interop的新手,需要从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'   ConsoleApplication1.exe中

     

附加信息:方法的类型签名不是PInvoke   兼容。

基于this question,我尝试将data的声明更改为public fixed byte data[10240]并将结构标记为unsafe,但该方法返回一个{{1}的实例并且numberOfBytes设置为0,随后对startAddr的调用失败(请注意,在this answer中,后续调用使用指向结构的指针而不是实例本身,因为我的情况)。那么我应该如何调用C#中的方法?

该项目的目标是.NET 3.5和x86。

提前感谢您的帮助。

2 个答案:

答案 0 :(得分:1)

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

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

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

static public extern void getDefaultPass(out DataBlock data);

答案 1 :(得分:0)

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

考虑C ++方法

out

__declspec(dllexport) DataBlock readText(char * dataArray , int bytesToRead) 参数在C#方法中应该是第一个还是最后一个,这一点并不明显。与框架将out参数作为最后一个参数(例如out)的惯例相反,此处必须是第一个参数,否则调用将失败:

TryParse