簡體   English   中英

將 double[,] 轉換為 IntPtr C#

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

如何使用GHandlesIntPtr獲取到數組中。 我先做了一個副本以避免客戶端覆蓋數據。 不幸的是,您需要保留一個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.

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