[英]Convert double[,] to IntPtr C#
我需要將 c# 中的雙數組轉換為 IntPtr 以正確地將其發送到我的 c DLL。 我已經成功地能夠使用此答案中的方法從IntPtr 轉換為 double[,] 。 我還找到了另一個接近我需要但處理 1D arrays 的答案。
我在我的邏輯中遺漏了一些東西,並在崩潰后得到錯誤代碼: Managed ' has exited with code -1073740940 (0xc0000374)。
這是我到目前為止所擁有的
IntPtr p = Marshal.AllocHGlobal(rows * columns);
for (int i = 0; i < rows; i++) //rows
{
double[] temp = new double[columns];
for (int x = 0; x < columns; x++)
temp[x] = input.cells[i, x];
Marshal.Copy(temp, 0, p, columns);
p = (IntPtr)(p.ToInt64() + IntPtr.Size);
}
toReturn.cells = p;
Marshal.FreeHGlobal(p);
return toReturn;
toReturn.cells 是我返回的結構中的 IntPtr。 單元格結構為單元格[行,列]
IntPtr 對我來說仍然很新。
編輯:感謝哈羅德。 他們的建議效果很好
這有很多問題。
首先, rows * columns
不是數據的大小,它只是元素的總數。 每個元素不是一個字節,而是八個,或者如果您願意,也可以是sizeof(double)
。
其次, p
更新為p = (IntPtr)(p.ToInt64() + IntPtr.Size);
(即根據當前模式下指針的大小,將其推進 4 或 8 個字節),但是您已經寫入了columns * 8
(或columns * sizeof(double)
)字節的數據。 將p
推進小於columns * 8
會使寫入相互覆蓋,因此並非所有數據都最終出現在結果中。 順便說一句,這里的復雜轉換實際上是不必要的,您可以直接添加到IntPtr
,因為 .NET 4。
第三, p
在循環中被改變,這本身還不錯,但是它以一種丟失原始指針的方式完成。 toReturn.cells = p;
和Marshal.FreeHGlobal(p);
使用不指代您分配的區域的p
,他們使用現在指向剛剛超過數據末尾的p
(如果p
更新了正確的數量,它將指向那里)。 必須記住原來的p
。
第四,在返回之前釋放數據意味着它現在不再存在,所以無論這個數據傳遞給什么代碼,仍然沒有它:它有一個指向無效的指針(它可能會意外工作,但是這是危險和錯誤的)。
前三點很容易修復,但最后一點需要對應用程序的工作方式進行非本地更改:memory 不能在此處釋放,但應該在某個時候釋放,即當數據的用戶完成時用它。
應用了一些修復:
int stride = columns * sizeof(double);
IntPtr p = Marshal.AllocHGlobal(rows * stride);
for (int i = 0; i < rows; i++) //rows
{
double[] temp = new double[columns];
for (int x = 0; x < columns; x++)
temp[x] = input.cells[i, x];
Marshal.Copy(temp, 0, p + stride * i, columns);
}
toReturn.cells = p;
return toReturn;
請記住,您仍應在適當的時候釋放 memory。
如何使用GHandles
將IntPtr
獲取到數組中。 我先做了一個副本以避免客戶端覆蓋數據。 不幸的是,您需要保留一個GHandle
才能調用 .Free .Free()
以避免 memory 泄漏。 這促使決定不僅在輸出中保留IntPtr
,而且還GHandle
和字節長度僅供參考。
static class Program
{
static void Main(string[] args)
{
Inputs inputs = new Inputs();
inputs.Cells = new double[,] { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };
var outputs = Prepare(inputs);
Console.WriteLine(outputs.CellPtr);
Console.WriteLine("Finish.");
}
static Outputs Prepare(Inputs inputs)
{
Outputs outputs = new Outputs();
outputs.ByteSize = Buffer.ByteLength(inputs.Cells);
var temp = new double[inputs.Cells.GetLength(0), inputs.Cells.GetLength(1)];
Buffer.BlockCopy(inputs.Cells, 0, temp, 0, outputs.ByteSize);
outputs.Handle = GCHandle.Alloc(inputs.Cells, GCHandleType.Pinned);
outputs.CellPtr = outputs.Handle.AddrOfPinnedObject();
return outputs;
}
}
public class Inputs
{
public double[,] Cells { get; set; }
}
public class Outputs : IDisposable
{
public IntPtr CellPtr { get; set; }
public int ByteSize { get; set; }
public GCHandle Handle { get; set; }
#region IDisposable Support
private bool disposedValue = false; // To detect redundant calls
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
// dispose managed resources here
}
Handle.Free();
CellPtr = IntPtr.Zero;
ByteSize = 0;
disposedValue = true;
}
}
~Outputs()
{
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
Dispose(false);
}
// This code added to correctly implement the disposable pattern.
public void Dispose()
{
Dispose(true);
}
#endregion
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.