-1

我一直在研究使用 COM 将结构作为参数从 C++ 客户端传递到 C++ 服务器。我找到了很多例子,但没有一个像我五岁那样真正向我解释它,也没有一个真正提供了对如何做我想做的事情的深刻理解,这只是通过双方都是的 COM 接口传递一个 C++ 结构C++。应该很容易吧?

我在服务器端的 IDL 文件中建立了如下结构:

[
    uuid(7F0C9A48-3C41-425B-B4E6-8156B61D5355),
    version(1.0)
]
typedef struct xxxData
{
    int iWidth;
    int iHeight;
    SafeArray(short) pxxxData;
} xxxData;

// Fix for UUID DECLARATION FOR _uuidof() functionality
// From http://go4answers.webhost4life.com/Example/error-c2787-no-guid-been-associated-158947.aspx
cpp_quote("struct __declspec(uuid(\"{7F0C9A48-3C41-425B-B4E6-8156B61D5355}\")) xxxData;")

据我所知,哪个有效。

现在我的客户调用 GetImageData,如下所示:

[id(16)] HRESULT GetImageData([in,out] VARIANT* pData);

现在我的客户调用如下:

VARIANT* pData = new VARIANT;
VariantInit( pData );
xxxData* data = new xxxxData;
HRESULT hr = mpCOMEvents->GetImageData(pData);
data = (FBIS_ImageData*)(pData->pvRecord);
int length = data->iWidth * data->iHeight;

但是,长度给了我一个不正确的地址位置。这让我想知道我对 pvRecord 的使用是否不正确以及我是否真的可以进行类型转换?

这是我的 COM 服务器端:

xxxData data;
//SAFEARRAY *psa;
IRecordInfo *pRI;
HRESULT hr;

/* Pass in Structure Information */
data.iHeight = 100;
data.iWidth = 100;

// Used http://vcfaq.mvps.org/com/4.htm as reference
hr = GetRecordInfoFromGuids(LIBID_xxxLib, 1, 0, 0x409, _uuidof(xxxData), &pRI);
VariantInit(pData);
pData->vt = VT_RECORD;
pData->pvRecord = &data;
pData->pRecInfo = pRI;
pRI = NULL;
4

1 回答 1

3

这里有些混乱。

如果您的目标不是自动化友好,请将您的 IDL 更改为:

[size_is=iWidth*iHeight] unsigned short* pxxxData;

并且不要在此使用 SAFEARRAY API。对于编组,您必须编译代理/存根 DLL 并注册它。

如果您的目标是自动化友好,请将您的 IDL 更改为:

SAFEARRAY(short) pxxxData;

并在此使用SAFEARRAY API。对于编组,您必须编译一个类型库(可选地,嵌入它)并注册它。这也启用了早期绑定(例如 VB6、tlbimp)。

这适用于支持用户定义类型的语言/环境。对于那些没有的(例如脚本语言),您必须使用基于 oleautomation/dual/IDispatch 的接口而不是结构(并在服务器中实现)。


编辑:根据您对问题所做的更改。

您应该将pData参数声明为outonly,GetImageData将填充它,不使用它并可能替换它。它也只需要在返回时编组,而不是在调用时。这里有一个建议:

[id(16)] HRESULT GetImageData([out] VARIANT* pData);

您的客户端代码有内存泄漏,它总是会创建一个 xxxData。这里有一个建议:

// If pData is in-out, this is not safe, use CoTaskMemAlloc(sizeof(VARIANT)) instead.
// The callee may override the buffer by assuming it was CoTaskMemAlloc'ed, thus
// assuming it can CoTaskMemFree the original location and set the pointer to a new
// CoTaskMemAlloc'ed location.
// The callee may be a proxy.
// Assuming it's out only, we can provide any location with enough space for a VARIANT.
VARIANT vData; 
VariantInit( &vData );
xxxData* data; // remove memory leak
HRESULT hr = mpCOMEvents->GetImageData(&vData);
// error handling removed for clarity (I hope)
data = (xxxData*)(vData.pvRecord);
int length = data->iWidth * data->iHeight;
// ... use data ...
// Don't forget to clear the variant, or there'll be a memory leak
// It implies:
//   vData.pRecInfo->RecordDestroy(vData.pvRecord);
//     This should recursively release memory allocated in each field
//     and finally release the memory allocated for the struct itself.
//   vData.pRecInfo->Release();
VariantClear( &vData );
// don't use data past this point

您的服务器代码设置pData->pvRecord为指向堆栈,这意味着它可能会被调用者或其他调用的函数覆盖。这里有一个建议:

xxxData* data; // Changed to pointer
IRecordInfo *pRI;
HRESULT hr;

// data.iHeight = 100; // removed
// data.iWidth = 100;  // removed

hr = GetRecordInfoFromGuids(LIBID_xxxLib, 1, 0, 0x409, _uuidof(xxxData), &pRI);
// error handling removed for clarity (I hope)
VariantInit(pData);
// This will allocate memory for the struct itself
// For fields that require memory allocation, follow "normal" COM rules,
// such as using CoTaskMemAlloc for buffers, SysAllocString or similar for BSTRs,
// etc.
// For each inner (pointed to) structure, you should call RecordCreate on the
// respective IRecordInfo instance for that type.
data = (xxxData*)pRI->RecordCreate();
data->iHeight = 100; // new
data->iWidth = 100;  // new
// If pData is in-out, this will leak, use VariantClear instead.
// Assuming it's out only, use VariantInit as it points to (allocated) garbage.
VariantInit(pData);
pData->vt = VT_RECORD;
pData->pvRecord = data; // data is already a pointer
pData->pRecInfo = pRI;
pRI = NULL;
// This won't (normally) leak, the caller must call VariantClear on the out VARIANT.
// The caller may be a stub.
于 2013-09-24T13:42:30.730 回答