将struct中的字节数组传递给com对象 [英] Pass byte array within struct to a com object
问题描述
我编写了一个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屋!