COM 通过指针将C++结构从C++客户端传递到C++服务器
COM Passing C++ struct from C++ client to to C++ server by pointer
我一直在研究使用 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;
这里有一些混乱。
如果您的目标不是自动化友好,请将 IDL 更改为:
[size_is=iWidth*iHeight] unsigned short* pxxxData;
并且不要在此上使用 SAFEARRAY API。对于封送处理,必须编译代理/存根 DLL 并注册它。
如果您的目标是自动化友好,请将 IDL 更改为:
SAFEARRAY(short) pxxxData;
并为此使用 SAFEARRAY API。对于编组,您必须编译一个类型库(可选地嵌入它)并注册它。这也支持早期绑定(例如VB6,tlbimp)。
这将适用于支持用户定义类型的语言/环境。对于没有的(例如脚本语言),您必须使用基于oleautomation/dual/IDispatch的接口而不是结构(并在服务器中实现)。
编辑:基于您对问题所做的更改。
您应该仅将pData
参数声明为out
,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.
- "unknown ca"自生成的 CA、证书和客户端/服务器
- 如何将函数集合传递给客户端类,以便将它们当作客户端类本身的成员使用
- 使用调试/崩溃报告将应用程序部署到客户端
- 如何在本地机器上运行c++和javascript客户端代码(hackerbank风格)
- 如何通过套接字将文本文件的内容从服务器发送到客户端
- 从服务器传输到客户端的消息不会出现
- OpenSSL TLS服务器-使用客户端证书白名单
- 当服务中的事件被触发时,如何将响应从服务发送回客户端?
- 我可以与 python 服务器而不是 c++ 客户端建立 tcp/ip 套接字吗?
- 提升 Asio TCP 服务器 处理多个客户端
- boost::asio UDP 广播客户端仅接收"fast"数据包
- 如何绑定 C++ gRPC 客户端的网络接口
- C++套接字客户端到 Python 服务器未创建连接
- 用于解析 win64 堆栈跟踪的命令行客户端(可以访问符号服务器)
- 将相机数据从服务器实时流式传输到客户端
- 如何将 Firebase 与基于 Linux 的客户端应用配合使用,以便与服务器进行双向消息通信
- GRPC C++ TLS 客户端 grpc::SslCredentials() 方法不返回
- 计算出有多少客户端可以连接到我正在使用的一些tcp服务器代码
- 如何暂停插孔音频客户端
- 不将数据 socket.io c++(客户端)发送到 nodejs(服务器)socket.io