简体   繁体   English

通过结构指针将结构数组从 C# 传递给 C

[英]Pass array of struct to C from C# via struct pointer

I want to pass an array of struct from C# to C dll.我想将一个结构数组从 C# 传递给 C dll。

The struct definition in C: C中的结构定义:

typedef struct{
    BYTE name[32];
    DWORD age;
}DATA;

I marshalled the struct as follow:我将结构编组如下:

[StructLayout(LayoutKind.Sequential)]
public struct DATA
{
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
    public byte[] name;
    public int age;
}

The C function definition that I want to pass on:我要传递的 C 函数定义:

void test_data(DATA * pStruct, DWORD length){
    _tprintf("test start\n");
    
    if (!pData)
    {
        _tprintf("pData is NULL\n");
    }
    unsigned int i,j;
    
    for(i = 0; i < length; i++){
        _tprintf("name: ");
        for(j = 0; j < 32; j++){
            _tprintf("%x ",pData[i].name[j]);
        }
        _tprintf("\n");
        _tprintf("age: %d",pData[i].age);
        _tprintf("\n");
    }

    _tprintf("test finish\n");
}

The function signature in C#: C#中的函数签名:

[DllImport("a.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void test_data(ref DATA pData, int length);

The setup in C#: C#中的设置:

int length = 10;
DATA[] arr = new DATA[length];
for(int i = 0; i < length; i++){
  arr[i].name = new byte[32];
  arr[i].age = 10;
}

test_data(ref arr[0], length);

But I got these output:但我得到了这些输出:

test start
name: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
age: 10
name: 7c 67 9a 2 a 0 0 0 1 0 0 0 2 0 0 0 eb 9 77 67 a4 eb 8f 0 0 0 0 0 2 0 0 0
age: 43673448
test finish

The first one is correct, but the second one seems weird, and I think there is something wrong with how I passed the reference or doing the setup.第一个是正确的,但第二个似乎很奇怪,我认为我通过引用或进行设置的方式有问题。

I was inspired from the pinvoke web in SCardTransmit function here where it passed the byte array in C# to the byte * in C using ref rapdu[0]我的灵感来自 SCardTransmit 函数中pinvoke 网络,它使用ref rapdu[0]将 C# 中的字节数组传递给 C 中的byte *

How to correctly do this ?如何正确地做到这一点?

That's not how you marshal an array of structures from managed code to native code.这不是您将结构数组从托管代码编组为本机代码的方式。

In your P/Invoke declaration:在您的 P/Invoke 声明中:

[DllImport("a.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void test_data(ref DATA pData, int length);

ref DATA pData marshals to a pointer to a single DATA object. ref DATA pData编组到指向单个DATA对象的指针。 There is no guarantee that the rest of the array will be sent along with it.无法保证阵列的其余部分将与它一起发送。 It could have worked if your structure was blittable , because then marshalling it would simply have involved pinning the array and then passing its address as is to the C function.如果您的结构是blittable ,它可能会起作用,因为然后编组它只涉及固定数组,然后将其地址按原样传递给 C 函数。 But, alas, the structure contains an array, which is a reference type.但是,唉,该结构包含一个数组,它是一个引用类型。 When you pass it to the C function, the marshaler has to do a copy to get a struct with the right layout.当您将它传递给 C 函数时,封送拆收器必须进行复制以获取具有正确布局的结构。 So, when you pass ref arr[0] , you're only sending one copy, and then your C code walks right off the end of the buffer and you hit undefined behaviour.因此,当您通过ref arr[0]时,您只会发送一份副本,然后您的 C 代码会直接离开缓冲区的末尾,并且您会遇到未定义的行为。 And, on a more philosophical level, it's also less clear that pData is supposed to be an array.而且,在更哲学的层面上, pData应该是一个数组也不太清楚。

So instead, to send the whole batch, just declare your argument as an array, as explained in the documentation , and the marshaler will do the rest:因此,要发送整个批次,只需将您的参数声明为数组, 如文档中所述,其余的将由编组器完成:

[DllImport("a.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void test_data(DATA[] pData, int length);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM