问题描述
如何使用指向数组的指针和指向 C# 中的指针成员的指针正确编组此 C 结构以与 3rd 方 dll 一起使用?
C:
typedef struct SomeStruct {
uint8_t *data[8];
int size[8];
uint8_t **extended_data;
};
这一切只是IntPtr
,然后您需要分配未管理的内存,将数据复制到其中并固定吗?如果是这样,你会怎么做?该结构体通过 dll 中的函数进行初始化。
在 python 中,这是我包装和使用这个结构的方式:
Python:
class SomeStruct(Structure):
_fields_ = [
('data',POINTER(c_uint8) * 8),('size',c_int * 8),('extended_data',POINTER(POINTER(c_uint8)))
]
# example use 1
dll = ctypes.CDLL("lib.dll")
some_struct = SomeStruct()
dll.init_struct(ctypes.byref(some_struct))
# or example 2
alloc_struct_fce = dll.alloc_struct
alloc_struct_fce.restype = ctypes.POINTER(SomeStruct) # specify return type
some_struct_ptr = alloc_struct_fce() # this gets passed to other dll functions
dll.some_processing(some_struct_ptr)
some_struct = some_struct_ptr.contents # dereference the pointer
试图找到与此代码等效的 C#。
extended_data
如果你知道如何处理它是一个奖励,它是动态大小,我不确定如何获得它的大小。
一个真实的例子是
- struct AVFrame 和 AVFrame *av_frame_alloc(void)
- struct AVPacket 与 void av_init_packet(AVPacket *pkt) 和 AVPacket *av_packet_alloc(void)
.dll 提供了分配和释放这些结构的方法。
解决方法
根据您的结构,我们可以:
struct SomeStruct
{
[MarshalAs(UnmanagedType.ByValArray,SizeConst = 8)]
public IntPtr[] data;
[MarshalAs(UnmanagedType.ByValArray,SizeConst = 8)]
public int[] size;
public IntPtr extended_data;
}
// I don't know the calling convention of your C method... You didn't show
// us its signature. It could be cdecl or stdcall
[DllImport(@"somedll.dll",CallingConvention = CallingConvention.Cdecl /* or StdCall */)]
public static extern void SomeStructMethod(out SomeStruct someStruct);
那么:
SomeStruct someStruct;
SomeStructMethod(out someStruct);
for (int i = 0; i < someStruct.data.Length; i++)
{
var array = new byte[someStruct.size[i]];
Marshal.Copy(someStruct.data[i],array,array.Length);
Console.Write("> ");
for (int j = 0; j < array.Length; j++)
{
Console.Write($"{array[j]} ");
}
Console.WriteLine();
}
注意,最后你应该调用一些 C 方法来释放 someStruct
中分配的内存,否则你会发生内存泄漏!
关于extended_data
,我无法为您提供帮助,因为您还没有告诉我们它应该是什么,
我将使用以下基于 xanatos 代码的代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace ConsoleApplication1
{
class Program
{
[DllImport(@"somedll.dll",CallingConvention = CallingConvention.Cdecl /* or StdCall */)]
public static extern void SomeStructMethod(out SomeStruct someStruct);
static void Main(string[] args)
{
SomeStruct someStruct;
SomeStructMethod(out someStruct);
byte[] data = new byte[8];
Marshal.Copy(someStruct.data,data,8);
byte[][] extendedData = new byte[8][];
for(int i = 0; i < 8; i++)
{
extendedData[i] = new byte[someStruct.size[i]];
Marshal.Copy(someStruct.extended_data[i],extendedData[i],someStruct.size[i]);
}
}
}
struct SomeStruct
{
[MarshalAs(UnmanagedType.ByValArray,SizeConst = 8)]
public IntPtr data;
[MarshalAs(UnmanagedType.ByValArray,SizeConst = 8)]
public int[] size;
public IntPtr[] extended_data;
}
}