将 double[] 转换为 IntPtr C#
Convert double[,] to IntPtr C#
我需要将 c# 中的双精度数组转换为 IntPtr,以便将其正确发送到我的 C DLL。我已经成功地使用 the method from this answer. Ive also found another answer 从 IntPtr 转换为 double[] ,这接近我需要的但处理一维数组。
我在我的逻辑中遗漏了一些东西,并在崩溃后得到错误代码:Managed ' 已退出,代码为 -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 是我 return 结构中的 IntPtr。
cells 的结构为 cells[rows, columns]
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
一定要记住
第四,在返回之前释放数据意味着它现在不再存在,所以无论这个数据传递给什么代码,仍然没有它:它有一个指向任何东西的指针,这将是无效的(它可能会意外工作,但这是危险和错误的。
前三点很容易修复,但最后一点需要对您的应用程序的工作方式进行非本地更改:内存不能在这里释放,但应该在某个时候释放,即当用户的数据已完成。
应用了一些修复:
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;
请记住,您仍应在适当的时候释放内存。
使用 GHandles
获取数组的 IntPtr
怎么样?我先做了一个副本,以避免客户端覆盖数据。不幸的是,您需要保留 GHandle
才能调用 .Free()
以避免内存泄漏。这促使决定不仅在输出中保留 IntPtr
,而且还保留 GHandle
和字节长度 FYI。
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
}
我需要将 c# 中的双精度数组转换为 IntPtr,以便将其正确发送到我的 C DLL。我已经成功地使用 the method from this answer. Ive also found another answer 从 IntPtr 转换为 double[] ,这接近我需要的但处理一维数组。
我在我的逻辑中遗漏了一些东西,并在崩溃后得到错误代码:Managed ' 已退出,代码为 -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 是我 return 结构中的 IntPtr。 cells 的结构为 cells[rows, columns]
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
一定要记住
第四,在返回之前释放数据意味着它现在不再存在,所以无论这个数据传递给什么代码,仍然没有它:它有一个指向任何东西的指针,这将是无效的(它可能会意外工作,但这是危险和错误的。
前三点很容易修复,但最后一点需要对您的应用程序的工作方式进行非本地更改:内存不能在这里释放,但应该在某个时候释放,即当用户的数据已完成。
应用了一些修复:
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;
请记住,您仍应在适当的时候释放内存。
使用 GHandles
获取数组的 IntPtr
怎么样?我先做了一个副本,以避免客户端覆盖数据。不幸的是,您需要保留 GHandle
才能调用 .Free()
以避免内存泄漏。这促使决定不仅在输出中保留 IntPtr
,而且还保留 GHandle
和字节长度 FYI。
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
}