将struct中的字节数组传递给com对象 [英] Pass byte array within struct to a com object

查看:63
本文介绍了将struct中的字节数组传递给com对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我编写了一个C ++ COM服务器(进程外)和客户端,因此:

I wrote a C++ COM server (out-of-proc) and client so:

idl(接口为 IDispatch ):

typedef[uuid(0952A366-20CC-4342-B590-2D8920D61613)]
    struct MyStruct{
    LONG                id;
    BYTE*               data;
    } MyStruct;

[helpstring("")] HRESULT foo([out] MyStruct* pStreamInfo);

服务器:

STDMETHODIMP foo(MyStruct* myStruct)
{
  myStruct.id = 7;
  myStruct.data = pData; // pData is a pointer to some data of variable length
  return S_OK;
}

客户:

MyStruct ms;
hr = comObj->foo(&ms);

除了添加 myStruct.data = pData之外,该代码将正常运行。 行使服务器崩溃。在客户端中分配内存 ms.data = new BYTE [1000] 没有帮助,因为指针仍然以<$ c $的形式到达 foo c> NULL 。

The code will work fine except when adding the myStruct.data = pData; line which crashes the server. Assigning memory in the client e.g. ms.data = new BYTE[1000] does not help as the pointer still arrives to foo as NULL.

这将是一个解决方案,1.最好是最简单的客户端,因为接口将被各种用户使用2如果C#客户端3使用了接口,是否会有其他解决方案?如果 data 需要从结构体中删除(我希望不是),那么是否有完整的引用?

What would be a solution, 1. preferably most simple one for client side since interface will be used by various users 2. Would there by a different solution if interface is used by C# client 3. If data needs to be out of the struct (I hope not) is there a reference to a complete example.

推荐答案

正如其他人在评论中提到的那样,您不能以这种方式传递原始数组。至少,您必须将字节数组复制到 SAFEARRAY 个字节(在IDL中为 SAFEARRAY(BYTE))。我只是使用自定义的代理/存根(由midl.exe生成的P / S代码编译)测试了以下代码,然后就可以在线获取数据。

As others have mentioned in the comments, you cannot pass a raw array this way. Minimally, you have to copy the byte array into a SAFEARRAY of bytes (SAFEARRAY(BYTE) in IDL). I just tested the code below with a custom proxy/stub (compiled from the P/S code generated by midl.exe), and I was able to get my data across the wire.

如果要使用标准的P / S,例如 PSDispatch {00020420-0000-0000-C000-000000000046} )或 PSOAInterface {00020424-0000-0000-C000-000000000046} ),或者您要使用VBA作为客户端,则可能必须将其转换为 SAFEARRAY(VARIANT)和/或将生成的safearray放入变量。请尝试最简单的方法,首先只使用 SAFEARRAY(BYTE),因为那是开销最少的方法。 (与 SAFEARRAY(BYTE)相比, SAFEARRAY(VARIANT)使用的内存多16倍,因为 VARIANT 的长度为16个字节,并且您必须手动将每个字节与 VARIANT 进行相互转换,而不是简单的 memcpy 调用如下所示。)

If you want to use a standard P/S such as PSDispatch ({00020420-0000-0000-C000-000000000046}) or PSOAInterface ({00020424-0000-0000-C000-000000000046}), or if you want to use VBA as a client, then you may have to convert this to a SAFEARRAY(VARIANT) and/or put the resulting safearray into a VARIANT. Try the simplest approach of just using SAFEARRAY(BYTE) first, because that's the one with the least overhead. (A SAFEARRAY(VARIANT) uses 16x more memory than a SAFEARRAY(BYTE) because a VARIANT is 16 bytes long. And you would have to manually convert each byte to/from a VARIANT, as opposed to the simple memcpy calls show below.)

将字节数组打包为 SAFEARRAY :(请注意,这会将字节数组复制到 SAFEARRAY 中。您可以使用 SAFEARRAY 结构以防止复制,但是您将以非标准方式进行操作。)

Packing the byte array into a SAFEARRAY: (Note that this copies the byte array into the SAFEARRAY. You could muck around with the internals of the SAFEARRAY struct to prevent the copy, but you'd be doing things in a non-standard way.)

/// <summary>Packs an array of bytes into a SAFEARRAY.</summary>
/// <param name="count">The number of bytes.</param>
/// <param name="pData">A reference to the byte array. Not read if count is 0.</param>
/// <param name="pResult">Receives the packed LPSAFEARRAY on success.</param>
HRESULT PackBytes(ULONG count, const BYTE* pData, /*[ref]*/ LPSAFEARRAY* pResult)
{
    // initialize output parameters
    *pResult = NULL;

    // describe the boundaries of the safearray (1 dimension of the specified length, starting at standard index 1)
    SAFEARRAYBOUND bound{ count, 1 };

    // create the safearray
    LPSAFEARRAY safearray = SafeArrayCreate(VT_UI1, 1, &bound);
    if (!safearray)
        return E_OUTOFMEMORY;

    // when there is actually data...
    if (count > 0)
    {
        // begin accessing the safearray data
        BYTE* safearrayData;
        HRESULT hr = SafeArrayAccessData(safearray, reinterpret_cast<LPVOID*>(&safearrayData));
        if (FAILED(hr))
        {
            SafeArrayDestroy(safearray);
            return hr;
        }

        // copy the data into the safearray
        memcpy(safearrayData, pData, count);

        // finish accessing the safearray data
        hr = SafeArrayUnaccessData(safearray);
        if (FAILED(hr))
        {
            SafeArrayDestroy(safearray);
            return hr;
        }
    }

    // set output parameters
    *pResult = safearray;

    // success
    return S_OK;
}

SAFEARRAY解压缩字节数组 :(请注意,这会从 SAFEARRAY 复制字节数组。您可以仔细研究<$ c $的内部c> SAFEARRAY 结构来防止复制,但是您将以非标准的方式进行操作。此外,您还可以选择直接使用 SAFEARRAY ,将消费代码放在 SafeArrayAccessData SafeArrayUnaccessData 之间。)

Unpacking the byte array from the SAFEARRAY: (Note that this copies the byte array from the SAFEARRAY. You could muck around with the internals of the SAFEARRAY struct to prevent the copy, but you'd be doing things in a non-standard way. Also, you could choose to use the data directly from the SAFEARRAY by putting the consuming code between SafeArrayAccessData and SafeArrayUnaccessData.)

/// <summary>Unpacks an array of bytes from a SAFEARRAY.</summary>
/// <param name="safearray">The source SAFEARRAY.</param>
/// <param name="pCount">A pointer to a ULONG that will receive the number of bytes.</param>
/// <param name="ppData">A pointer to a BYTE* that will receive a reference to the new byte array.
/// This array must be deallocated (delete []) when the caller is done with it.</param>
HRESULT UnpackBytes(LPSAFEARRAY safearray, /*[out]*/ ULONG* pCount, /*[out]*/ BYTE** ppData)
{
    // initialize output parameters
    *pCount = 0;
    *ppData = NULL;

    // validate the safearray element type (must be VT_UI1)
    VARTYPE vartype;
    HRESULT hr = SafeArrayGetVartype(safearray, &vartype);
    if (FAILED(hr))
        return hr;
    if (vartype != VT_UI1)
        return E_INVALIDARG;

    // validate the number of dimensions (must be 1)
    UINT dim = SafeArrayGetDim(safearray);
    if (dim != 1)
        return E_INVALIDARG;

    // get the lower bound of dimension 1
    LONG lBound;
    hr = SafeArrayGetLBound(safearray, 1, &lBound);
    if (FAILED(hr))
        return hr;

    // get the upper bound of dimension 1
    LONG uBound;
    hr = SafeArrayGetUBound(safearray, 1, &uBound);
    if (FAILED(hr))
        return hr;

    // if the upper bound is less than the lower bound, it's an empty array
    if (uBound < lBound)
        return S_OK;

    // calculate the count of the bytes
    ULONG count = uBound - lBound + 1;

    // create buffer
    BYTE* pData = new BYTE[count];
    if (!pData)
        return E_OUTOFMEMORY;

    // begin accessing the safearray data
    BYTE* safearrayData;
    hr = SafeArrayAccessData(safearray, reinterpret_cast<LPVOID*>(&safearrayData));
    if (FAILED(hr))
    {
        delete[] pData;
        return hr;
    }

    // copy the data
    memcpy(pData, safearrayData, count);

    // finish accessing the safearray data
    hr = SafeArrayUnaccessData(safearray);
    if (FAILED(hr))
    {
        delete[] pData;
        return hr;
    }

    // set output parameters
    *pCount = count;
    *ppData = pData;

    // success
    return S_OK;
}

这篇关于将struct中的字节数组传递给com对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆