从IntPtr复制数据到IntPtr

38

我有两个IntPtr值,它们指向长度为length字节的某些数据区域。length的数量级可能在200k到400k之间。

int length = /* ..*/
IntPtr ptrSrc = /*.. */;
IntPtr ptrDst = /* .. */;

现在我想要将数据从ptrSrc复制到ptrDst。这段代码可以正常工作:

现在我想要将数据从ptrSrc复制到ptrDst。这段代码可以正常工作:

byte[] data = new byte[length];
Marshal.Copy(ptrSrc, data, 0, length);
Marshal.Copy(data, 0, ptrDst, length);

但它的缺点是需要一个额外的临时(可能很大)数组。不幸的是,我在.NET框架中找不到可以直接从复制到的Marshal.Copy变体,因此我正在寻找替代方法。

我对适用于32位Windows和64位Windows的解决方案感兴趣。有什么建议吗?


1
你能使用 unsafe 吗? - driis
@driis:是的,这是可能的。 - Doc Brown
4个回答

46
您可以使用P/Invoke调用适当的C函数。这可能是最简单的方法。例如:
class Program
{
    [DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)]
    public static extern void CopyMemory(IntPtr dest, IntPtr src, uint count);

    static void Main()
    {
        const int size = 200;
        IntPtr memorySource = Marshal.AllocHGlobal(size);
        IntPtr memoryTarget = Marshal.AllocHGlobal(size);

        CopyMemory(memoryTarget,memorySource,size);
    }
}

是的。memcpy会复制字节,所以不会出现指针大小或单词边界问题。C#中的马歇尔将负责向底层C函数提供正确的指针大小。 - driis
1
更新,您可能希望使用内置的Win32函数来确保该函数真正可用。我更新了示例,使用了kernel32.dll中的CopyMemory函数。 - driis
2
你的解决方案似乎可行,在 64 位模式和 32 位模式下都可以(在交换源和目标后;-),请查看我编辑过的答案。 - Doc Brown
有人能提供一个“CopyMemory”的工作示例吗?请参见我的问题https://stackoverflow.com/questions/45805525/bitmapinfoheader-fill-data-using-copymemory - Saad A
1
计数参数在64位平台上是64位,而在32位平台上是32位。我猜这不会导致ABI问题,因为x86是小端序(所以在64位上,32位部分仍将被完全复制并正确解释),但更准确的P/INVOKE应该根据32/64位平台在两个不同的签名之间切换。 - ceztko
显示剩余4条评论

23

我认为这个答案需要更新,因为在 .net 4.6 中有...

 Buffer.MemoryCopy Method (Void*, Void*, Int64, Int64)

该方法将从源地址复制sourceBytesToCopy字节到目标地址。如果缓冲区重叠且destination减去source的差小于sourceBytesToCopy,则源块将以相反的顺序复制到目标块。

因此,如果您不使用4.6或通用Windows应用程序10,则请使用先前的答案。


谢谢这个更新。这个解决方案对于UWP应用程序也适用吗?我猜被接受的答案不会。 - Doc Brown
是的,当你在C#中直接访问内存时,这是正常的。但C#不同于C++,在C++中这样的操作更为普遍。 - Peter
1
这需要代码不安全才能实现。 - joe
@joe 是的,如果你在编写代码时追求极致速度,就需要做出一些妥协。在这种情况下,它被用于帧抓取瀑布效果和物体识别,全部在 Pentium i5 上完成,仅需 20 毫秒。 - Peter

12

正如用户38000527指出的那样,现代答案是MemoryCopy,它是.NET Core 1.0、.NET Standard 1.3和.NET Framework 4.6的一部分。

以下是你在上下文中使用它的方法:

Buffer.MemoryCopy(ptrSrc.ToPointer(), ptrDest.ToPointer(), length, length) 

2

老问题,新答案。

使用Span<T>非常好且非常快。跨平台兼容。

在dotnet core 2.1及更高版本中,最佳方法是:

var srcSpan = new Span<byte>(srcPtr, count);
var dstSpan = new Span<byte>(dstPtr, count);

srcSpan.CopyTo(dstSpan);

或者,简化一下:
public static void CopyMemory(void* srcPtr, void* dstPtr, int count) =>
   new Span<byte>(srcPtr, count).CopyTo(new Span<byte>(dstPtr, count));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接