1

I am trying to pass a byte array from c# to c++ . I am using Component Object Model.This is my first COM experience. How can i pass byte array from c# to c++? Any advice? There is no problem when i try to pass another types (strings, int etc.) except binary arrays

Thanks

Errors i get

1-)error C2440: '=' : cannot convert from 'SAFEARRAY' to 'byte'     
2-)IntelliSense: no suitable conversion function from "SAFEARRAY" to "byte" exists  

Here is the code i wrote

C# Side,

public byte[] GetImage()
{

    try
    {

       SqlCommand command = new SqlCommand("this command returns Varbinary ", conn);
       SqlDataAdapter dataAdapter = new SqlDataAdapter(command);
       ImgBlobDT = new DataTable("ImgBlobDT");
       dataAdapter.Fill(ImgBlobDT);
       DataRow dr = ImgBlobDT.Rows[0];
       imgBytes = (byte[])dr["ImgBinary"];

    }
    catch ()
    {
        //some codes
    }


    return imgBytes;

}

C++ Side

CoInitialize(NULL);

IDBCPtr obj;

obj.CreateInstance(__uuidof(DBC));

byte bytesArr[] = obj->GetImage();

CoUninitialize();
2
  • Which line of code gets the error? Is it shown here? Commented Jan 25, 2017 at 8:13
  • @Baldrick I get error from C++ side when i try to assign return value to a byte array Commented Jan 25, 2017 at 8:33

2 Answers 2

3

You either need to allocate unsafe memory as @KimKulling's answer suggests, or send the array as an appropriately marked SafeArray. An example demonstrating three different methods is excerpted below, and available on github.

The C# interface looks would look like:

[ComVisible(true)]
[Guid("7FE927E1-79D3-42DB-BE7F-B830C7CD32AE")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IImageProvider
{
    [return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UI1)]
    byte[] GetImage(int foo);

    IntPtr GetImageAsUnmanaged(int foo, out int cbBuff);

    void GetImageAsUnmanagedPreallocated(int foo, ref int cbBuff, IntPtr pBuff);
}

With a C# server implementation of:

[ClassInterface(ClassInterfaceType.None)]
[ComVisible(true)]
[Guid("D133B928-A98B-4006-8B00-4AA09BD042E7")]
[ProgId("CSByteArrayServer.ImageProvider")]
public class ImageProvider : IImageProvider
{
    private const int E_INVALIDARG = unchecked((int)0x80070057);

    RNGCryptoServiceProvider crng = new RNGCryptoServiceProvider();

    [return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UI1)]
    public byte[] GetImage(int foo)
    {
        return GetBytes();
    }

    private byte[] GetBytes()
    {
        byte[] data = new byte[500];
        crng.GetBytes(data);
        return data;
    }

    public IntPtr GetImageAsUnmanaged(int foo, out int cbBuff)
    {
        var data = GetBytes();

        var result = Marshal.AllocCoTaskMem(data.Length);
        Marshal.Copy(data, 0, result, data.Length);

        cbBuff = data.Length;
        return result;
    }

    public void GetImageAsUnmanagedPreallocated(int foo, ref int cbBuff, IntPtr pBuff)
    {
        var data = GetBytes();

        if (cbBuff < data.Length)
        {
            cbBuff = data.Length;
            throw Marshal.GetExceptionForHR(E_INVALIDARG);
        }

        cbBuff = data.Length;
        Marshal.Copy(data, 0, pBuff, data.Length);
    }
}

And a C++ client of:

int main()
{
    CoInitialize(NULL);

    IImageProvider *pImgProvider;
    CoCreateInstance(__uuidof(ImageProvider), nullptr, CLSCTX_INPROC_SERVER, __uuidof(IImageProvider), (LPVOID*)&pImgProvider);

    int imageId = 0;

    // with SafeArray
    {
        SAFEARRAY *pSafeArray;
        auto hr = pImgProvider->GetImage(0, &pSafeArray);
        assert(hr == S_OK);
        CComSafeArray<BYTE> safeArray;
        safeArray.Attach(pSafeArray);
        ManipulateData(&safeArray);
        // CComSafeArray will free the memory allocated by pSafeArray
    }

    // with CoTaskMemAlloc
    {
        char *pData;
        int cbData;
        auto hr = pImgProvider->GetImageAsUnmanaged(0, (long*)&cbData, (long*)&pData);
        assert(hr == S_OK);

        ManipulateData(pData, cbData);
        CoTaskMemFree(pData);
    }

    // with caller allocate
    {
        int cbData;
        auto hr = pImgProvider->GetImageAsUnmanagedPreallocated(imageId, (long*)&cbData, (long)nullptr);
        assert(hr == E_INVALIDARG);

        char *pData = new char[cbData];
        hr = pImgProvider->GetImageAsUnmanagedPreallocated(imageId, (long*)&cbData, (long)pData);
        assert(hr == S_OK);

        ManipulateData(pData, cbData);

        delete[] pData;
    }

    system("pause");

    CoUninitialize();
    return 0;
}

void ManipulateData(char* pBuff, int cbBuff)
{
    char hash = 0;
    for (int i = 0; i < cbBuff; i++)
    {
        hash ^= pBuff[i];
    }

    std::cout << "Hash is " << +hash << std::endl;
}

void ManipulateData(CComSafeArray<BYTE> *pSafeArray)
{
    BYTE hash = 0;
    for (ULONG i = 0; i < pSafeArray->GetCount(); i++)
    {
        hash ^= pSafeArray->GetAt(i);
    }

    std::cout << "Hash is " << +hash << std::endl;
}
Sign up to request clarification or add additional context in comments.

Comments

0

You need to marshall your byte array into the unsave c++ world. You can use the Marshal-class to do this: https://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal_methods(v=vs.110).aspx

So you can return an unsafe pointer tor c++ by using Marshaling:

public IntPtr GetImage() {
    DataRow dr = ImgBlobDT.Rows[0];
    byte[] imgBytes = (byte[])dr["ImgBinary"];

    // Initialize unmanaged memory to hold the array.
    int size = Marshal.SizeOf(imgBytes [0]) * imgBytes .Length;
    IntPtr pnt = Marshal.AllocHGlobal(size);

    // Copy the array to unmanaged memory.
    Marshal.Copy(imgBytes , 0, pnt, imgBytes .Length);

    // your method will return a unmanaged pointer instead of byte[]
    return pnt;
}

In c++ you should be able to use this pointer like:

CoInitialize(NULL);
IDBCPtr obj;
obj.CreateInstance(__uuidof(DBC));
IntPtr intPtr = obj->GetImage();
CoUninitialize();

And don't forget to release the allocated unsafe memory after using it in C++:

        // Free the unmanaged memory.
        Marshal.FreeHGlobal(pnt);

Hopefully this could help you.

3 Comments

Thanks for your answer. Could you please tell me how can i get pointer from c++ ?
I adapted the answer to make the usage more clearer.
I get this error, error C2440: 'initializing' : cannot convert from '__int64' to 'int *'

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.