[英]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.