簡體   English   中英

如何編組指向結構數組指針的指針?

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

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM