将结构中的字节数组传递给 com 对象

Pass byte array within struct to a com object

本文关键字:com 对象 数组 字节数 结构 字节      更新时间:2023-10-16

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

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]没有帮助,因为指针仍然以NULL的形式到达foo

什么是解决方案,1.最好是客户端最简单的解决方案,因为界面将被各种用户使用2。如果 C# 客户端 3 使用接口,是否会有不同的解决方案。如果data需要脱离结构(我希望不需要),是否有对完整示例的引用。

正如其他人在评论中提到的,您不能以这种方式传递原始数组。至少,您必须将字节数组复制到SAFEARRAY字节(在 IDL 中SAFEARRAY(BYTE))。我刚刚使用自定义代理/存根(从 midl.exe 生成的 P/S 代码编译)测试了下面的代码,并且我能够通过网络获取数据。

如果要使用标准 P/S,例如PSDispatch({00020420-0000-0000-C000-000000000046}) 或PSOAInterface({00020424-0000-0000-C000-000000000046}),或者如果您想使用 VBA 作为客户端,那么您可能必须将其转换为SAFEARRAY(VARIANT)和/或将生成的安全数组放入VARIANT中。尝试最简单的方法,先使用SAFEARRAY(BYTE),因为这是开销最小的方法。(SAFEARRAY(VARIANT)使用的内存比SAFEARRAY(BYTE)多 16 倍,因为VARIANT的长度为 16 个字节。并且您必须手动将每个字节转换为/从VARIANT,而不是下面显示的简单memcpy调用。

将字节数组打包到SAFEARRAY中:(请注意,这会将字节数组复制到SAFEARRAY中。你可以使用SAFEARRAY结构的内部来防止复制,但你会以非标准的方式做事。

/// <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复制字节数组。你可以使用SAFEARRAY结构的内部来防止复制,但你会以非标准的方式做事。此外,您可以选择直接从SAFEARRAY使用数据,方法是将使用代码放在SafeArrayAccessDataSafeArrayUnaccessData之间。

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