在 MSMQ 消息中发送具有 BSTR 值类型的 COM 对象
我正在尝试通过 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; }
如果我获取生产者中的 BSTR 值(序列化之前),pAnalisis-getImgName()
,它工作正常。 相反,当我尝试将其获取到消费者中时,从队列中读取消息后,它不会返回任何内容。 其他值(例如大小)可以顺利返回。
有谁知道如何通过 MSMQ 在 COM 对象内发送 BSTR 值?
我试图找到一些类似的例子,但完全徒劳。
问题是,我得到的要么是一个带有奇怪字符的非常奇怪的值,要么是一个十六进制值,具体取决于我如何提取该值。问题是我永远不会得到正确的值。
但是我想知道...我们确定可以发送 BSTR 值吗? 如果我没记错的话,它是一个指向字符串的指针...我正在运行两个不同的进程(即生产者和消费者),因此它们使用不同的内存块,并且它们应该在不同的机器上运行。 ..
我试图以 VARIANT 类型发送此信息..但也迷路了。 然而,这似乎比发送 BSTR 稍微不那么牵强。
对此有什么想法吗?
I'm trying to send a COM object over a MSMQ message in C++. This is my object :
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);
Everything goes fine and I can get the whole object but the BSTR types. Floats and integers are properly sent and received. But the BSTR types dont work. I'm trying to send strings and can't find the way. I did try with VARIANT instead and the result was wrong too. Somehow, it looks like the strings are not serialized.
These are some of the get and set functions for my ATL component:
This one works fine:
STDMETHODIMP CAnalisis::getLight(FLOAT* light) { *light=img.light; return S_OK; } STDMETHODIMP CAnalisis::setLight(FLOAT light) { img.light=light; return S_OK; }
This one doesn't :
STDMETHODIMP CAnalisis::getImgName(BSTR* imgName) { *imgName = img.imgName; return S_OK; } STDMETHODIMP CAnalisis::setImgName(BSTR imgName) { img.imgName=imgName; return S_OK; }
and this is the way I create the MSMQ message and fill the values in my producer :
// 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);
here is the serialization code
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; }
If I get the BSTR value in the producer (before serialization), pAnalisis-getImgName()
, it works fine. In contrast, when I try to get it in the consumer, after reading the message from the queue, it doesn't return anything. The other values, such as the size, are returned with no trouble.
does anyone know how to send a BSTR value inside a COM object through MSMQ ?
I've tried to find some similar examples but totally in vain.
the thing is that i'm getting either an very weird value with weird characters or an Hexadecimal value, depending on how I extract the value .. the thing is that I do never get the right value.
and I was wondering, however... are we sure it is possible to send a BSTR value ? if i'm not wrong, it is a pointer to a string... I'm running two different processes (i.e. producer and consumer), so they use different memory blocks, and they are meant to be run on different machines so...
I was trying to send this info as a VARIANT type.. but also got lost. However, this seems a bit less far-fetched than sending a BSTR.
ANY IDEAS ON THIS?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
问题在于 Image 类的序列化将其视为连续的内存块。 由于 BSTR 实际上是一个指针,因此仅对指针值进行序列化,并且 BSTR 有效负载会丢失。
相反,您应该将除 BSTR 之外的所有字段写入为二进制并单独处理 BSTR。 例如,您可以先将 BSTR 长度写入整数,然后再写入其有效负载。 读取时首先读取长度,调用SysAllocStringLen()分配缓冲区,然后读取有效负载。
保持简单字段的序列化不变(IPersistStreamInit::Save()):
对于 BSTR 执行以下操作:
与读取类似(IPersistStreamInit::Load()):
请注意,此代码写入/读取字符串长度,然后写入/读取由 Unicode 字符组成的有效负载。 每个 Unicode 字符占用一个以上字节 - 因此 IStream 读/写方法调用中的乘法。
The problem is that the serialization of Image class treats it as a contiguous block of memory. Since BSTR is really a pointer only the pointer value is serialized and the BSTR payload is lost.
Instead you should write all fields except BSTRs as binary and process BSTRs separately. For example, you can write BSTR length as integer first, then its payload. When reading you will read length first, call SysAllocStringLen() to allocate a buffer, then read the payload.
Leave serialization of simple fields as it is (the IPersistStreamInit::Save() ):
For BSTRs do this:
Similar for reading (the IPersistStreamInit::Load()):
Note that this code writes/reads string length and then writes/reads the payload that consists of Unicode characters. Unicode characters occupy more than one bytes each - hence the multiplication in IStream Read/Write methods call.
如果您只是传递 WCHAR — 长度信息就会丢失。 BSTR 格式错误,这可能会给您带来痛苦。 您需要使用
SysAllocString
才能跨组件使用它。 请参阅 MSDN - 备注部分。 尝试:If you simply pass a WCHAR -- the length information is lost. The BSTR is malformed and this is probably causing you all the grief. You need to use
SysAllocString
to use it across components. See MSDN -- the Remarks section. Try:好吧,这个答案取决于你最近做了一些奇怪的事情,但可能适用。
很久很久以前,我必须从 VB 应用程序在 VB6 和 VC++6(pro) 下传递 VB 字符串。 到 VC++ 应用程序。 长度还可以,但我经常在另一边收到一个字符。
问题是接收应用程序。 不是为 unicode 编译的,而是作为 ANSI 项目编译的。 在传输远端将其解包的 COM 层代码做了一个有趣的技巧,我只在 MSDN 的一个不起眼的角落中找到记录在书籍摘录中:它创建了一个 ABSTR。
ABSTR 实际上并不是一种类型。 没有办法声明一个。 它实际上是 BSTR 底层存储的重新格式化,以便您可以假装它是 C++ 中的 ASCII char*。 这是通过首先确保 BSTR 指向其标头后的第一个字符(对于 C++、IIRC 中的此类结构而言是典型的),然后对实际字符串数据进行改组,使其包含所有第一个字符来完成的。字节后跟所有第二个字节。 对此的另一个名称是“纯粹的邪恶”。
两种非常坏事情可能会以这种方式发生:如果完成此转换,并且您将结果视为仍然是一个宽字符串,你会得到乱码。 如果未完成,并且将结果视为 ASCII 字符数组,则通常会得到单个字符(如果原始数据仅包含 ASCII 范围字符),因为在宽表示中,每隔一个字节都是零,而高字节排在第二位。
从您的描述中我无法确定您是否遇到过这种情况。 但我建议在调试器中停止该操作,并查看该接收值下的所有字符串数据,以查看它是否以某种意外的方式重新排列。 如果它被打乱了,问问自己为什么,并看看你构建项目的方式。
事实上,几乎没有记录的格式作为替代内存布局嵌入到现有类型中,即使按照 MS 开发的“我们可以使用多少字符串格式”标准,也很难找到这种格式,这一事实几乎让我感到震惊尖叫。 这几乎与第一次尝试识别“GetModuleFileName”作为用于获取当前可执行文件路径的函数一样糟糕。
OK, this answer depends on you doing something odd these days, but might be applicable.
A long, long time ago I had to pass a VB string under VB6 and VC++6(pro) from a VB app. to a VC++ app. The length came through OK but I often received one character on the other side.
The problem was that the receiving app. was not compiled for unicode, but rather as an ANSI project. The COM layer code that unpacked it on the far side of transfer did an interesting trick that I only found documented in an obscure corner of the MSDN in a book excerpt: it created an ABSTR.
An ABSTR isn't actually a type. There is no way to declare one. It's actually a reformatting of a BSTR's underlying storage so that you can pretend it's an ASCII char* in C++. This is done by first making sure the BSTR points to the first character after its header (typical anyway for such structures in C++, IIRC) and then shuffling the actual string data so that it contains all of the first bytes followed by all of the second bytes. Another name for this is "pure evil".
Two very bad things can happen this way: if this conversion is done, and you treat the result as if it's still a wide character string, you get gibberish. If it's not done and you treat the results as an ASCII character array, you usually get a single character, if the original contained only ASCII-range characters, since in the wide representation every other byte is a zero and the high bytes come second.
I can't quite tell from your description if this is what happened to you. But I recommend stopping the thing in a debugger and looking at all of the string data under that received value to see if it's been reshuffled in some unexpected way. If it has been shuffled, ask yourself why and look at the way you built the project.
The fact of an almost undocumented format wedged into an existing type as an alternative memory layout that's really hard to find, even by the how-many-string-formats-can-we-make-up standards of MS development, just about made me scream. It was almost as bad as trying to identify "GetModuleFileName" for the first time as the function to use to get the current executable's path.
您的对象需要在 getter 和 setter 中创建字符串的副本:
当然,您需要在析构函数中释放字符串,检查 NULL ptr 等。
Your object needs to create a copy of the string in both the getter and the setter:
of course, you need to free the string in the destructor, check for NULL ptrs etc.
或者,您可以使用 CComBSTR 而不是 BSTR。 CComBSTR 比 BSTR 更智能,它负责分配和释放内存。
Alternatively you can use CComBSTR instead of BSTR. CComBSTR is a smarter than BSTR, it takes care of allocating and deallocating the memory.
我的建议是将您的字段放入 Variant 中(即使是暂时的),然后使用 Variant 流代码来展平数据,并在另一端对其进行反序列化。
这是您可以使用的流代码(抱歉,它已经有 20 年历史了:))
链接: https://github.com/kasajian/VariantStream/blob/master/VariantStream.h
代码粘贴到这里有点冗长。
My suggestion is to put your fields in a Variant (even if temporarily) then use Variant streaming code to flatten the data, and deserialize it on the other end.
Here's streaming code you can use (sorry it's about 20 years old :) )
Link: https://github.com/kasajian/VariantStream/blob/master/VariantStream.h
The code is a bit verbose to paste in here.