Search code examples
c#c++interoppinvokemarshalling

C++ <--> C# modify a marshalled array of bytes


I have an unmanaged C++ function which is calling a managed C# method in a DLL. The purpose of the C# method is to take an array of bytes (allocated by the C++ caller), populate the array, and return it. I can get the array INTO the C# method, but the populated data are lost when they get back to the C++ function. Right now, this is my test code to debug the process:

C# DLL Method:

// Take an array of bytes and modify it
public ushort GetBytesFromBlaster([MarshalAs(UnmanagedType.LPArray)] byte[] dataBytes)
{
    dataBytes[0] = (byte)'a';
    dataBytes[1] = (byte)'b';
    dataBytes[2] = (byte)'c';
    return 3;
}

C++ function which calls the DLL:

// bytes[] has been already allocated by its caller
short int SimGetBytesP2P(unsigned char bytes[])
{
    unsigned short int numBytes = 0;
    bytes[0] = 'x';
    bytes[1] = 'y';
    bytes[2] = 'z';
    // bytes[] are {'x', 'y', 'z'} here
    guiPtr->GetBytesFromBlaster(bytes, &numBytes);
    // bytes[] SHOULD be {'a', 'b', 'c'} here, but they are still {'x', 'y', 'z'}
    return(numBytes);

}

I believe it has something to do with C# turning the C++ pointer into a new managed array, but modifying the original one. I have tried several variations using the "ref" modifyer, etc., but no luck. Also, these data are NOT null-terminated strings; the date bytes are raw 1-byte values, not null-terminated.

Can anyone please shed some light on this? Thanks!

Stuart


Solution

  • You could do the marshaling yourself. Have the C# function accept a parameter by value of type IntPtr. Also a second parameter indicating array length. No special marshaling attributes are needed or wanted.

    Then, use Marshal.Copy and copy the array from the unmanaged pointer to a managed byte[] array that you allocated. Do your thing, and then when you're done, use Marshal.Copy to copy it back to the C++ unmanaged array.

    These particular overloads should get you started:

    http://msdn.microsoft.com/en-us/library/ms146625.aspx
    http://msdn.microsoft.com/en-us/library/ms146631.aspx

    For example:

    public ushort GetBytesFromBlaster(IntPtr dataBytes, int arraySize)
    {
        byte[] managed = new byte[arraySize];
        Marshal.Copy(dataBytes, managed, 0, arraySize);
        managed[0] = (byte)'a';
        managed[1] = (byte)'b';
        managed[2] = (byte)'c';
        Marshal.Copy(managed, 0, dataBytes, arraySize);
        return 3;
    }
    

    Alternatively you could implement a custom marshaller as described in http://msdn.microsoft.com/en-us/library/w22x2hw6.aspx if the default one isn't doing what you need it to. But that looks like more work.