简体   繁体   English

将 double[,] 转换为 IntPtr C#

[英]Convert double[,] to IntPtr C#

I need to convert a double array in c# to an IntPtr to properly send it to my c DLL.我需要将 c# 中的双数组转换为 IntPtr 以正确地将其发送到我的 c DLL。 I have successfully been able to convert from IntPtr to double[,] using the method from this answer .我已经成功地能够使用此答案中的方法从IntPtr 转换为 double[,] 。 Ive also found another answer that is close to what I need but deals with 1D arrays.我还找到了另一个接近我需要但处理 1D arrays 的答案。

Im missing something in my logic and get the error code after a crash: Managed ' has exited with code -1073740940 (0xc0000374).我在我的逻辑中遗漏了一些东西,并在崩溃后得到错误代码: Managed ' has exited with code -1073740940 (0xc0000374)。

This is what I have so far这是我到目前为止所拥有的

            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 is my IntPtr inside a struct that I return. toReturn.cells 是我返回的结构中的 IntPtr。 cells is structured as cells[rows, columns]单元格结构为单元格[行,列]

IntPtr's are still very new to me. IntPtr 对我来说仍然很新。

Edit: thanks to harold.编辑:感谢哈罗德。 their suggestions worked beautifully他们的建议效果很好

There are various things wrong with that.这有很多问题。

First, rows * columns is not the size of the data, it's only the total number of elements.首先, rows * columns不是数据的大小,它只是元素的总数。 The elements are not one byte each, but eight, or sizeof(double) if you prefer.每个元素不是一个字节,而是八个,或者如果您愿意,也可以是sizeof(double)

Second, p is updated by p = (IntPtr)(p.ToInt64() + IntPtr.Size);其次, p更新为p = (IntPtr)(p.ToInt64() + IntPtr.Size); (ie advancing it by 4 or 8 bytes depending on how big pointers are in the current mode), but you've written columns * 8 (or, columns * sizeof(double) ) bytes of data. (即根据当前模式下指针的大小,将其推进 4 或 8 个字节),但是您已经写入了columns * 8 (或columns * sizeof(double) )字节的数据。 Advancing p by less than columns * 8 makes the writes overwrite each other, so not all data ends up in the result.p推进小于columns * 8会使写入相互覆盖,因此并非所有数据都最终出现在结果中。 By the way, the complicated conversions here are actually not necessary, you can add directly to an IntPtr , since .NET 4.顺便说一句,这里的复杂转换实际上是不必要的,您可以直接添加到IntPtr ,因为 .NET 4。

Third, p is changed in the loop, which is not bad on its own, but it's done in a way that loses track of the original pointer.第三, p在循环中被改变,这本身还不错,但是它以一种丢失原始指针的方式完成。 toReturn.cells = p; and Marshal.FreeHGlobal(p);Marshal.FreeHGlobal(p); use a p which does not refer to the area that you allocated, they use a p which now points just past the end of the data (well it would point there if p was updated by the right amount).使用不指代您分配的区域的p ,他们使用现在指向刚刚超过数据末尾的p (如果p更新了正确的数量,它将指向那里)。 The original p must be remembered.必须记住原来的p

Fourth, freeing the data before returning means that it now no longer exists, so whatever code this data is passed to, still doesn't have it: it has a pointer to nothing which will be invalid to use (it may accidentally work, but it's dangerous and wrong).第四,在返回之前释放数据意味着它现在不再存在,所以无论这个数据传递给什么代码,仍然没有它:它有一个指向无效的指针(它可能会意外工作,但是这是危险和错误的)。

The first three points are easy to fix, but the last one needs a non-local change to how your application works: the memory cannot be freed here, but it should be freed at some point, namely when the user of the data is done with it.前三点很容易修复,但最后一点需要对应用程序的工作方式进行非本地更改:memory 不能在此处释放,但应该在某个时候释放,即当数据的用户完成时用它。

Some fixes applied:应用了一些修复:

        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;

Keep in mind you should still free the memory at the appropriate time.请记住,您仍应在适当的时候释放 memory。

What about using GHandles to get the IntPtr to an array.如何使用GHandlesIntPtr获取到数组中。 I made a copy first to avoid overwriting data by the client.我先做了一个副本以避免客户端覆盖数据。 Unfortunately, you need to keep a GHandle in order to call .Free() to avoid memory leaks.不幸的是,您需要保留一个GHandle才能调用 .Free .Free()以避免 memory 泄漏。 This drives the decision to keep not only an IntPtr in the outputs, but also the GHandle and the byte length FYI.这促使决定不仅在输出中保留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