Search code examples
c#c++.netcomcom-interop

Does .NET interop copy array data back and forth, or does it pin the array?


I have this COM method signature, declared in C#:

void Next(ref int pcch,
          [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)]
          char[] pchText);

I call it like this:

int cch = 100;
var buff = new char[cch];
com.Next(ref cch, buff);

Does the .NET interop layer first copy the whole array to a temporary unmanaged memory buffer, then copy it back? Or does the array get automatically pinned and passed by reference?

For the sake of trying, I did this in the COM object (C++):

*pcch = 1;
pchText[0] = L'A';
pchText[1] = L'\x38F'; // 'Ώ'

I do get 'Ώ' back when I check buff[1] in C# upon return. But I don't think this is a strong proof that the array gets pinned, rather than copied back and forth.


Solution

  • Let's do a small experiment. First, let's change your COM method to look like this (in C++):

    STDMETHODIMP CComObject::Next(ULONG* pcch, int* addr, OLECHAR* pbuff)
    {
        pbuff[0] = L'A';
        pbuff[1] = L'\x38F';
        *addr = (int)pbuff;
        *pcch = 1;
        return S_OK;
    }
    

    Then, change the C# method signature:

    void Next(ref uint pcch, out IntPtr addr, 
        [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)]
        char[] pbuff); 
    

    Finally, test it like this:

    uint cch = 10;
    var buff = new char[cch];
    IntPtr addr1;
    
    unsafe
    {
        fixed (char* p = &buff[0])
        {
            addr1 = (IntPtr)p;
        }
    }
    
    IntPtr addr2;
    com.Next(ref cch, out addr2, buff);
    Console.WriteLine(addr1 == addr2);
    

    As expected, addr1 == addr2 is true. Thus, apparently the array does get pinned rather than copied when passed to COM.

    That said, I couldn't find any documentation which would feature this as a hard requirement for a CLR implementation. E.g., this may or may not be true for Mono.