argodev
argodev

Reputation: 243

How can I pass a binary blob from C++ to C#?

I'm primarily a C# dev (not much C++ since college), but working on integrating a large collection of existing C++ code into an application. I have a C++/CLI assembly that is buffering the two worlds and have communication from C# through to C++ working fine. The question I have, is that the C++ class has a method call that generates a binary blob (think array of bytes in C# world) that I need to get in C# and process (pass around like a solid bag).

What I'm looking for is advice on how to handle the buffer/wrapper method (C++/CLI) between the two worlds. I assumed that I'd pass a char* and length, but C# sees that as a byte* (I'm assuming that is some C++/CLI "magic").

I also tried passing array<Byte ^>^ but that haven't had much luck translating the char* from the rest of the C++ lib to the byte array... and what I have doesn't smell right.

Upvotes: 1

Views: 3377

Answers (3)

sehe
sehe

Reputation: 393467

You can use an UnmanagedMemoryStream, like so:

byte[] message = UnicodeEncoding.Unicode.GetBytes("Here is some data.");
IntPtr memIntPtr = Marshal.AllocHGlobal(message.Length);
byte* memBytePtr = (byte*) memIntPtr.ToPointer();

UnmanagedMemoryStream writeStream = new UnmanagedMemoryStream(memBytePtr, message.Length, message.Length, FileAccess.Write);

writeStream.Write(message, 0, message.Length);
writeStream.Close();

The reverse route, roughly:

UnmanagedMemoryStream readStream = new UnmanagedMemoryStream(memBytePtr, message.Length, message.Length, FileAccess.Read);

byte[] outMessage = new byte[message.Length];
readStream.Read(outMessage, 0, message.Length);
readStream.Close();

// Convert back into string for this example
string converted = UnicodeEncoding.Unicode.GetString(outMessage);

Marshal.FreeHGlobal(memIntPtr);

I'm sure that MSDN will have more resources

Upvotes: 3

Jeff Mercado
Jeff Mercado

Reputation: 134491

I took a shot at it and came up with this. Nothing crazy, just allocate the managed array, copy the data and return it.

header:

#pragma once
using namespace System;
using namespace System::Runtime::InteropServices;
namespace CLRLib
{
    public ref class TwiddlerFunctions
    {
    public:
        static array< Byte >^ GetArray();
    };
}

implementation:

#include "CLRLib.h"
array< Byte >^ CLRLib::TwiddlerFunctions::GetArray()
{
    unsigned char data[] = { 1, 2, 34, 5 };
    // convert the unmanaged array to a managed array
    array< Byte >^ arr = gcnew array< Byte >(sizeof data);
    Marshal::Copy((IntPtr)data, arr, 0, arr->Length);
    return arr;
}

C# side:

using System;
using CLRLib;
class Program
{
    static void Main(string[] args)
    {
        byte[] arr = TwiddlerFunctions.GetArray();
        Console.WriteLine(String.Join(" ", arr)); // 1 2 34 5
    }
}

Upvotes: 2

ildjarn
ildjarn

Reputation: 62985

Have the C++/CLI code hand off a System::IO::UnmanagedMemoryStream to the C# code, which can then use a System.IO.BinaryReader on said stream to extract data as needed.

As an aside, C++/CLI's char is synonymous with C#'s sbyte, C++/CLI's unsigned char is synonymous with C#'s byte, C++/CLI's wchar_t is synonymous with C#'s char, and C++/CLI's array<unsigned char>^ is synonymous with C#'s byte[]. Note that it's array<unsigned char>^ or array<System::Byte>^ rather than array<unsigned char^>^ or array<System::Byte^>^, as System.Byte is a value type rather than a ref type.

Upvotes: 3

Related Questions