[英]How do I marshall a pointer to a pointer of an array of structures?
我的 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, i.e., how much data is available?
uint myHandle = 1;
DATASTRUCT[] dataInformation = new DATASTRUCT[3]; // Doesn't it matter what I specify as the array size?
myData(myHandle, dataToShow, out Time, out maxData, ref dataInformation);
执行后,即使有 3 个结构要返回,上述函数也将成功返回只有一个结构。 为什么会这样?
附加信息; 我尝试通过以下方式将指针传递给结构数组的指针:
ref DATASTRUCT[] data;
// 它可以工作,但它只返回一个结构[Out, MarshalAs(UnmanagedType.LPArray)] DATASTRUCT[] data;
// 返回带有垃圾的已定义结构的数量据我了解,我可能需要使用IntPtr
进行一些手动编组,但是我不知道如何实现这一点,因此我们将不胜感激。
好的,看起来好像您的本机库进行了分配,所以您真正需要做的就是提供一个指针,您可以通过该指针访问分配的数据。
将您的 API 定义更改为(注意,我将 maxData 参数更改为 uint,long 在 .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 中将是 long long)
可以使用 GCHandle 类将您的 DATASTRUCT[] 编组到 IntPtr
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 方法也是一种选择,但为了证明这个概念,至少 GCHandle 应该可以解决问题,它对于非托管代码执行长时间运行操作的场景并不理想,因为您'已经将这个对象固定在适当的位置,GC 不能移动它,直到你释放它。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.