如何编组指向结构数组指针的指针?

我的C声明如下:

int myData(uint myHandle, tchar *dataName, long *Time, uint *maxData, DATASTRUCT **data); typedef struct { byte Rel; __int64 Time; char Validated; unsigned char Data[1]; } DATASTRUCT ; 

我的C#声明如下:

 [DllImport("myData.dll", EntryPoint = "myData")] public static extern int myData(uint myHandle, [MarshalAs(UnmanagedType.LPTStr)] string dataName, out long Time, out uint maxData, ref DATASTRUCT[] data); [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct DATASTRUCT { public sbyte Rel; public long Time; public byte Validated; public double Data; } 

然后我调用托管函数如下:

 string dataToShow = "description"; long Time; uint maxData; // How many structs will be returned, ie how much data is available uint myHandle = 1; DATASTRUCT[] dataInformation = new DATASTRUCT[3]; // doesn't matter what I specify as the array size? myData(myHandle, dataToShow, out Time, out maxData, ref dataInformation); 

在执行时,即使有3个返回,上述函数也将成功返回只有一个结构。 为什么会这样?

附加信息; 我试过通过以下方式将指针传递给结构数组的指针:

 - ref DATASTRUCT[] data; // Works but only returns one struct - [Out, MarshalAs(UnmanagedType.LPArray)] DATASTRUCT[] data; // returns the number of defined structs with garbage 

据我了解,我可能需要使用IntPtr进行一些手动编组,但我不知道如何实现这一点,所以任何建议都将受到赞赏。

好吧,好像你的本地库做了分配,所以你需要做的就是提供一个指针,你可以通过它来访问分配的数据。

将您的API定义更改为(注意,我将maxData参数更改为uint,.NET中为64位,本机为32位)。

 [DllImportAttribute("myData.dll", EntryPoint = "myData")] public static extern int myData(uint myHandle, [MarshalAs(UnmanagedType.LPTStr)] string dataName, out uint Time, out uint maxData, out IntPtr pData); 

我不记得你是否需要最后一个参数的out关键字,但我想是的。

然后,调用myData:

 uint nAllocs = 0, time = 0; IntPtr pAllocs = IntPtr.Zero; myData(1, "description", out time, out nAllocs, out pAllocs); 

现在,pAllocs应该指向非托管内存,将这些内容编组到托管内存中并不太难:

 [StructLayoutAttribute(LayoutKind.Sequential, Pack = 1)] public struct DATASTRUCT { public byte Rel; public long Time; public byte Validated; public IntPtr Data; //pointer to unmanaged string. } int szStruct = Marshal.SizeOf(typeof(DATASTRUCT)); DATASTRUCT[] localStructs = new DATASTRUCT[nAllocs]; for(uint i = 0; i < nallocs; i++) localStructs[i] = (DATASTRUCT)Marshal.PtrToStructure(new IntPtr(pAllocs.ToInt32() + (szStruct * i)), typeof(DATASTRUCT)); 

现在你应该有一系列本地结构。

需要注意的一点您可能需要将项目设置为编译为x86,以将IntPtr的大小标准化为4字节(DWORD)而不是AnyCPU的默认值8。

指向指针的指针可以在您的dllimport声明中表示为ref IntPtr数据,因此您的声明将变为:

 [DllImportAttribute("myData.dll", EntryPoint = "myData")] public static extern int myData(uint myHandle, [MarshalAs(UnmanagedType.LPTStr)] string dataName, out long Time, out uint maxData, ref IntPtr data); 

(顺便说一句,我认为C中的long很相当于C#中的int .C#中的long是Int64,在C中会长很长)

将DATASTRUCT []编组到IntPtr可以使用GCHandle类完成

 DATASTRUCT [] dataInformation = new DATASTRUCT[3]; GCHandle gch = GCHandle.Alloc(dataInformation , GCHandleType.Pinned); IntPtr ptr = gch.AddrOfPinnedObject(); myData(myHandle, dataToShow, out Time, out maxData, ref ptr); //It's absolutely essential you do this next bit so the object can be garbage collected again, //but it should only be done once the unmanaged code is definitely done with the reference. gch.Free(); 

使用Marshal类和它的StructureToPtr或Copy方法也是一个选项,但为了certificate这个概念至少GCHandle应该做的伎俩,它对于非托管代码长时间运行操作的情况来说并不理想,因为你已将此对象固定到位,GC无法移动它,直到您释放它为止。