38

I have two IntPtr values pointing to some data areas of length bytes. length may have an order of magnitude of 200k to 400k.

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

Now I want to copy the data from ptrSrc to ptrDst. This code works fine:

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

but it has the drawback of needing an additional temporary (potentially huge) array. Unfortunately, I could not find a Marshal.Copy variant in the .NET framework for copying directly from IntPtr to IntPtr, so I am looking for alternatives.

I am interested in a solution which works on 32-bit Windows as well as 64-bit Windows. Any suggestions?

janw
  • 8,758
  • 11
  • 40
  • 62
Doc Brown
  • 19,739
  • 7
  • 52
  • 88

4 Answers4

46

You can P/Invoke into the appropiate C function. That is probably the easiest way of doing that. Example:

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);
    }
}
Doc Brown
  • 19,739
  • 7
  • 52
  • 88
driis
  • 161,458
  • 45
  • 265
  • 341
  • Yes. `memcpy` copies bytes, so there are no pointer size or word boundary problems. C# marhsalling will take care of providing the right pointer size to the underlying C function. – driis Apr 12 '13 at 16:14
  • And can I assume msvcrt to be installed on the users machine, or do I have to supply my own? I mean, there are different msvcrt.dll's for 32 and 64 bit Windows, right? I am not worried about pointer size. – Doc Brown Apr 12 '13 at 16:16
  • `msvcrt.dll` will be available on most systems, but if you want to be sure, you will need to distribute it with your app. – driis Apr 12 '13 at 16:23
  • 1
    Update, you will probably want to use a built in Win32 function to really make sure the function will be available. I updated the example to use `CopyMemory` from `kernel32.dll`. – driis Apr 12 '13 at 16:26
  • 2
    Your solution seems to work, in 64 bit mode as well as in 32 bit mode (after exchanging source and target ;-), see my edit of your answer. – Doc Brown Apr 12 '13 at 16:29
  • can someone provide a working example for "CopyMemory", see my question https://stackoverflow.com/questions/45805525/bitmapinfoheader-fill-data-using-copymemory – Saad A Aug 22 '17 at 14:22
  • 1
    Count arg is 64 bit on 64 bit platform and 32 on 32 bit platform. I guess this doesn't cause ABI problems because x86 is little endian (so on 64 bit the 32 bit part will still be copied entirely and be interpreted correctly), but a more accurate P/INVOKE should switch between the two different signatures, depending on 32/64 bit platform. – ceztko Apr 18 '18 at 22:49
  • Since `IntPtr` and `UintPtr` are always the size of a pointer (32 bit on x86, 64 bit on x64), you could solve the correctness issue by making the `count` parameter a `UIntPtr` and then casting in the call. – Pieter-Jan Briers Mar 24 '19 at 08:51
  • note my answer at the bottem if your on .net4.6 or above – Peter Jun 20 '20 at 23:35
23

I think the answer needs an update in .net 4.6 there is

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

This method copies sourceBytesToCopy bytes from the address specified by source to the address specified by destination. If the buffers overlap and the difference between destination minus source is less than sourceBytesToCopy, the source block is copied to the destination block in reverse order.

So if you not on 4.6 or universal windows app 10 then use the previous answer.

Peter
  • 2,043
  • 1
  • 21
  • 45
  • Thanks for this update. Sure this solution works for UWP apps as well? I guess the accepted answer won't. – Doc Brown Aug 06 '17 at 07:13
  • Yes thats normal when your in C# and access memory directly. C# isnt C++ where such actions are more common – Peter Nov 24 '17 at 08:11
  • 1
    This requires the code to be unsafe though – joe Aug 11 '21 at 06:12
  • @joe yes right, if your coding for ultimate speed one makes offers. In this case it was used for frame grabbing watterfall effects and do object recognignition all on a pentium i5 wihtin 20ms – Peter Aug 14 '21 at 18:44
12

As user38000527 points out the modern answer is MemoryCopy and it is part of .NET core 1.0, .NET standard 1.3, and .NET framework 4.6.

Here is how you use it in your context:

Buffer.MemoryCopy(ptrSrc.ToPointer(), ptrDest.ToPointer(), length, length) 
cdiggins
  • 17,602
  • 7
  • 105
  • 102
2

Old question, new answer..

Using Span<T> works great and VERY fast.. Platform independend.

The best way in dotnet core 2.1 and higher is:

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

srcSpan.CopyTo(dstSpan);

Or, simplified:

public static void CopyMemory(void* srcPtr, void* dstPtr, int count) =>
   new Span<byte>(srcPtr, count).CopyTo(new Span<byte>(dstPtr, count));
Jeroen van Langen
  • 21,446
  • 3
  • 42
  • 57