将字节数组从C#传递到C ++(COM)

时间:2017-01-25 08:02:27

标签: c# c++ .net arrays com

我正在尝试将一个字节数组从c#传递给c ++。我正在使用组件对象模型。这是我的第一次COM体验。如何将字节数组从c#传递给c ++?有什么建议?当我尝试传递除二进制数组

之外的其他类型(字符串,int等)时没有问题

由于

我得到的错误

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

这是我写的代码

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 个答案:

答案 0 :(得分:2)

您需要分配不安全的内存,如@KinKulling的答案所示,或者将数组作为适当标记的SafeArray发送。以下摘录了一个展示三种不同方法的示例,available on github

C#界面外观如下:

[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);
}

使用C#服务器实现:

[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);
    }
}

和C ++客户端:

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;
}

答案 1 :(得分:0)

您需要将您的字节数组编组到unsave c ++世界中。您可以使用Marshal类来执行此操作:https://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal_methods(v=vs.110).aspx

所以你可以使用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;
}

在c ++中,您应该能够使用此指针,如:

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

在C ++中使用它之后不要忘记释放已分配的不安全内存:

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

希望这可以帮到你。