我正在尝试通过C ++中的MSMQ消息发送COM对象。这是我的目标:
class ATL_NO_VTABLE CAnalisis : public CComObjectRootEx, public CComCoClass, public ISupportErrorInfo, public IDispatchImpl, public IPersistStreamInit { private: typedef struct { DOUBLE size; float color; float light; BSTR imgName; BSTR uname; } Image; Image img; STDMETHOD(Load)(IStream *pStm); STDMETHOD(Save)(IStream *pStm, BOOL fClearDirty);
一切顺利,我可以获得整个对象,但BSTR类型。正确发送和接收浮点数和整数。但BSTR类型不起作用。我正在尝试发送字符串而无法找到方法。我确实尝试使用VARIANT,结果也是错误的。不知何故,看起来字符串不是序列化的。
这些是我的ATL组件的一些get和set函数:
这个很好用:
STDMETHODIMP CAnalisis::getLight(FLOAT* light) { *light=img.light; return S_OK; } STDMETHODIMP CAnalisis::setLight(FLOAT light) { img.light=light; return S_OK; }
这个没有:
STDMETHODIMP CAnalisis::getImgName(BSTR* imgName) { *imgName = img.imgName; return S_OK; } STDMETHODIMP CAnalisis::setImgName(BSTR imgName) { img.imgName=imgName; return S_OK; }
这就是我创建MSMQ消息并填充生产者中的值的方式:
// For these ActiveX components we need only smart interface pointer IMSMQQueueInfosPtr pQueueInfos; IMSMQQueueInfoPtr pQueueInfo; IMSMQQueuePtr pQueue; IUnknownPtr pIUnknown; // Instanciate the follwing ActiveX components IMSMQQueryPtr pQuery(__uuidof(MSMQQuery)); IMSMQMessagePtr pMessage(__uuidof(MSMQMessage)); IAnalisisPtr pAnalisis(__uuidof(Analisis)); WCHAR * imagen; imagen = L"imagen1.jpg"; pAnalisis->setImgName(imagen); (...) pAnalisis->setFruitSize(20.00); (...) pQueueInfo = new IMSMQQueueInfoPtr( __uuidof(MSMQQueueInfo) ); pQueueInfo->PathName = "MYCOMPUTER\\private$\\myprivatequeue"; pQueue = pQueueInfo->Open(MQ_SEND_ACCESS, MQ_DENY_NONE); pMessage->Body = static_cast(pAnalisis); pMessage->Send(pQueue);
这是序列化代码
STDMETHODIMP CAnalisis::Load( IStream *pStm ) { ULONG cb; HRESULT hr; if (NULL==pStm) return ResultFromScode(E_POINTER); // Read an object from the stream. // hr=pStm->Read(&img, sizeof(Image), &cb); if (FAILED(hr)) return hr; if (sizeof(Image) != cb) return E_FAIL; return NOERROR; } STDMETHODIMP CAnalisis::Save( IStream *pStm, BOOL bClearDirty ) { ULONG cb; HRESULT hr; if (NULL==pStm) return ResultFromScode(E_POINTER); // Write an object into the stream. hr=pStm->Write(&img, (ULONG)sizeof(Image), &cb); if (FAILED(hr) || sizeof(Image)!=cb) return ResultFromScode(STG_E_WRITEFAULT); return NOERROR; }
如果我在生产者(序列化之前)pAnalisis-getImgName()
中获得BSTR值,它可以正常工作。相反,当我尝试在消费者中获取它时,在从队列中读取消息后,它不会返回任何内容。其他值(例如大小)将毫无问题地返回。
有没有人知道如何通过MSMQ在COM对象中发送BSTR值?
我试图找到一些类似的例子但完全徒劳无功。
问题是,我得到一个奇怪的字符或十六进制值非常奇怪的值,这取决于我如何提取值..事情是我永远不会得到正确的值。
我想知道,但是......我们确定可以发送BSTR值吗?如果我没有错,它是一个指向字符串的指针...我正在运行两个不同的进程(即生产者和消费者),所以他们使用不同的内存块,并且它们应该在不同的机器上运行。 ..
我试图将此信息作为VARIANT类型发送..但也迷路了。然而,这似乎不如发送BSTR那么牵强。
关于这个的任何想法?
答案 0 :(得分:1)
问题是Image类的序列化将其视为一个连续的内存块。由于BSTR实际上只是指针,因此指针值被序列化并且BSTR有效负载丢失。
相反,您应该将除BSTR之外的所有字段分别写为二进制和处理BSTR。例如,您可以先将BSTR长度写为整数,然后再写入其有效负载。读取时,首先读取长度,调用SysAllocStringLen()分配缓冲区,然后读取有效负载。
保持简单字段的序列化(IPersistStreamInit :: Save()):
pStm->Write(&(img.color), (ULONG)sizeof(float), &cb);
对于BSTR,请执行以下操作:
int length = SysStringLen( img.uname );
pStm->Write(&length, (ULONG)sizeof(int), &cb);
if( length > 0 ) {
pStm->Write( img.uname, (ULONG)(length * sizeof(WCHAR) ), &cb);
}
类似于阅读(IPersistStreamInit :: Load()):
int length;
pStm->Read(&length, (ULONG)sizeof(int), &cb);
if( length > 0 ) {
img.uname = SysAllocStringLen( 0, length );
pStm->Read( img.uname, (ULONG)( length * sizeof( WCHAR) ), &cb);
} else {
img.uname = 0;
}
请注意,此代码写入/读取字符串长度,然后写入/读取由Unicode字符组成的有效内容。 Unicode字符每个占用多个字节 - 因此IStream读/写方法调用中的乘法。
答案 1 :(得分:0)
如果您只是传递WCHAR,则会丢失长度信息。 BSTR格格不入,这可能会让你感到悲伤。您需要使用SysAllocString
跨组件使用它。请参阅MSDN - 备注部分。尝试:
BSTR imagen = SysAllocString(L"imagen1.jpg");
答案 2 :(得分:0)
好的,这个答案取决于你这些天做的事情,但可能适用。 很久很久以前,我不得不从VB应用程序传递VB6下的VB字符串和VC ++ 6(pro)。到VC ++应用程序。长度来得好,但我经常在另一边收到一个角色。
问题在于接收应用。没有为unicode编译,而是作为ANSI项目编译。在转移的远端解压缩它的COM层代码做了一个有趣的技巧,我只在书摘的MSDN的一个不起眼的角落里找到了它:它创建了一个ABSTR。
ABSTR实际上不是一种类型。没有办法申报一个。它实际上是对BSTR底层存储的重新格式化,因此您可以假装它是C ++中的ASCII字符*。这是通过首先确保BSTR指向其标题之后的第一个字符(对于C ++,IIRC中的此类结构通常无论如何)和然后对实际字符串数据进行混洗以使其包含所有第一个字符来完成的。字节后跟所有第二个字节。另一个名字是“纯粹的邪恶”。
两个非常 错误的 事物可以通过这种方式发生:如果完成此转换,您将结果视为仍然是广泛的字符串,你得到胡言乱语。如果没有完成并且您将结果视为ASCII字符数组,那么如果原始字符仅包含ASCII范围字符,则通常会获得单个字符,因为在宽表示中,每隔一个字节为零,高字节为秒。
我不清楚你的描述是否发生了这件事。但我建议在调试器中停止该事情并查看该接收值下的所有字符串数据,以查看它是否以某种意外方式重新洗牌。如果它被洗牌,问问自己为什么,看看你建立项目的方式。
几乎没有文档的格式楔入现有类型作为替代内存布局的事实很难找到,即使是MS开发的多少字符串格式 - 我们化妆标准,只是关于让我尖叫。这几乎与尝试首次识别“GetModuleFileName”作为用于获取当前可执行文件路径的函数一样糟糕。
答案 3 :(得分:0)
您的对象需要在getter和setter中创建字符串的副本:
STDMETHODIMP CAnalisis::getImgName(BSTR* imgName)
{
*imgName = SysAllocString(img.imgName);
return S_OK;
}
STDMETHODIMP CAnalisis::setImgName(BSTR imgName)
{
SysFreeString(img.imgName);
img.imgName=SysAllocString(imgName);
return S_OK;
}
当然,你需要在析构函数中释放字符串,检查NULL ptrs等。
答案 4 :(得分:0)
或者,您可以使用CComBSTR而不是BSTR。 CComBSTR比BSTR更聪明,它负责分配和释放内存。
答案 5 :(得分:0)
我的建议是将您的字段放在Variant中(即使是暂时的),然后使用Variant流代码来展平数据,并在另一端反序列化。
您可以使用这里的流媒体代码(对不起,大约20岁:) :)
链接:https://github.com/kasajian/VariantStream/blob/master/VariantStream.h
这里的代码有点冗长。