在 MSMQ 消息中发送具有 BSTR 值类型的 COM 对象

Posted

技术标签:

【中文标题】在 MSMQ 消息中发送具有 BSTR 值类型的 COM 对象【英文标题】:send a COM object with a BSTR value type in a MSMQ message 【发布时间】:2010-10-12 06:03:23 【问题描述】:

我正在尝试通过 C++ 中的 MSMQ 消息发送 COM 对象。这是我的目标:

类 ATL_NO_VTABLE CANalisis : 公共 CComObjectRootEx, 公共 CComCoClass, 公共 ISupportErrorInfo, 公共IDispatchImpl, 公共 IPersistStreamInit 私人的: 类型定义结构 双倍尺寸; 浮动颜色; 浮灯; BSTR imgName; BSTR 名称; 图片; 图像图像; 标准方法(加载)(IStream *pStm); STDMETHOD(Save)(IStream *pStm, BOOL fClearDirty);

一切正常,我可以得到整个对象,但 BSTR 类型除外。浮点数和整数被正确发送和接收。但是 BSTR 类型不起作用。我正在尝试发送字符串,但找不到路。我确实尝试使用 VARIANT,但结果也是错误的。不知何故,看起来字符串没有序列化。

这些是我的 ATL 组件的一些 get 和 set 函数:

这个很好用:

STDMETHODIMP CANalisis::getLight(FLOAT* light) *light=img.light; 返回 S_OK; STDMETHODIMP CANalisis::setLight(FLOAT light) img.light=光; 返回 S_OK;

这个没有:

STDMETHODIMP CANalisis::getImgName(BSTR* imgName)

    *imgName = img.imgName;

    返回 S_OK;


STDMETHODIMP CANalisis::setImgName(BSTR imgName)


    img.imgName=imgName;
    返回 S_OK;

这就是我创建 MSMQ 消息并在我的生产者中填充值的方式:

// 对于这些 ActiveX 组件,我们只需要智能接口指针
        IMSMQQueueInfosPtr pQueueInfos;
        IMSMQQueueInfoPtr pQueueInfo;
        IMSMQQueuePtr pQueue;
        IUnknownPtr pIUnknown;
        // 实例化以下 ActiveX 组件
        IMSMQQueryPtr pQuery(__uuidof(MSMQQuery));
        IMSMQMessagePtr pMessage(__uuidof(MSMQMessage));


        IAnalisisPtr pAnalisis(__uuidof(Analisis));

                WCHAR * 图像;
        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->发送(pQueue);


这是序列化代码

STDMETHODIMP CANalisis::Load( IStream *pStm ) 乌龙cb; HRESULT 小时; 如果 (NULL==pStm) 返回 ResultFromScode(E_POINTER); // 从流中读取一个对象。 // hr=pStm->Read(&img, sizeof(Image), &cb); 如果(失败(小时)) 返回小时; 如果(大小(图像)!= cb) 返回 E_FAIL; 返回无错误; STDMETHODIMP CANalisis::Save( IStream *pStm, BOOL bClearDirty ) 乌龙cb; HRESULT 小时; 如果 (NULL==pStm) 返回 ResultFromScode(E_POINTER); // 将对象写入流中。 hr=pStm->Write(&img, (ULONG)sizeof(Image), &cb); if (FAILED(hr) || sizeof(Image)!=cb) 返回 ResultFromScode(STG_E_WRITEFAULT); 返回无错误;

如果我在生产者中获得 BSTR 值(在序列化之前)pAnalisis-getImgName(),它工作正常。相反,当我尝试在消费者中获取它时,在从队列中读取消息后,它不会返回任何内容。其他值(例如大小)可以毫无问题地返回。

有谁知道如何通过 MSMQ 在 COM 对象中发送 BSTR 值?

我试图找到一些类似的例子,但完全是徒劳的。

问题是我得到了一个带有奇怪字符的非常奇怪的值或一个十六进制值,这取决于我如何提取值..问题是我永远不会得到正确的值。

我想知道,但是...我们确定可以发送 BSTR 值吗?如果我没记错的话,它是一个指向字符串的指针......我正在运行两个不同的进程(即生产者和消费者),所以它们使用不同的内存块,它们应该在不同的机器上运行。 ..

我试图将此信息作为 VARIANT 类型发送..但也迷路了。不过,这似乎没有发送 BSTR 牵强。

对此有任何想法吗?

【问题讨论】:

【参考方案1】:

问题在于 Image 类的序列化将其视为连续的内存块。由于 BSTR 实际上是一个指针,因此只有指针值被序列化,BSTR 有效负载丢失。

相反,您应该将除 BSTR 之外的所有字段写入二进制文件并分别处理 BSTR。例如,您可以先将 BSTR 长度写入整数,然后再写入其有效负载。读取时先读取长度,调用SysAllocStringLen()分配缓冲区,然后读取payload。

保持简单字段的序列化不变(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 读/写方法中的乘法。

【讨论】:

所以你的意思是我应该把它从结构中取出,如果我没记错的话:
 private: typedef struct  DOUBLE size;浮动颜色;浮灯;变体起源;  图片;长 imgNameLenght;长无名; BSTR imgName; BSTR 名称; 
不行,修改序列化代码。编辑答案以澄清。 成功了!!!!在挣扎了几天之后,我在大约 2 周前放弃了。现在它已经完成了一段时间。太感谢了。我刚刚将此答案标记为正确答案,并且完全有帮助。我相信这对许多其他程序员有用,因为根本没有示例。令人惊叹!thxAmillion!【参考方案2】:

如果您只是传递一个 WCHAR——长度信息就会丢失。 BSTR 格式不正确,这可能会让您感到非常痛苦。您需要使用SysAllocString 来跨组件使用它。请参阅MSDN -- 备注 部分。试试:

BSTR imagen = SysAllocString(L"imagen1.jpg");

【讨论】:

好吧,我已经这样做了……在我的其他一些尝试中,我得到了类似的结果。我用 SysAllocString 再次尝试过,没有任何变化。但是,我会这样。我认为错误是在尝试从序列化对象中取出值时,否则我不明白。【参考方案3】:

好的,这个答案取决于你最近做了一些奇怪的事情,但可能适用。 很久很久以前,我不得不从 VB 应用程序中传递 VB6 和 VC++6(pro) 下的 VB 字符串。到 VC++ 应用程序。长度还可以,但我经常在另一边收到一个字符。

问题在于接收应用程序。不是为 unicode 编译的,而是作为 ANSI 项目编译的。在传输的另一端解压缩它的 COM 层代码做了一个有趣的技巧,我只在 MSDN 的一个不起眼的角落在一本书摘录中发现它:它创建了一个 ABSTR。

一个 ABSTR 实际上不是一种类型。没有办法声明一个。它实际上是对 BSTR 底层存储的重新格式化,以便您可以假装它是 C++ 中的 ASCII char*。这是通过首先确保 BSTR 指向其标头之后的第一个字符(对于 C++、IIRC 中的此类结构而言通常是典型的),然后 然后 打乱实际的字符串数据,使其包含所有第一个字符字节后跟所有第二个字节。它的另一个名字是“纯粹的邪恶”。

两个非常 不好 事情可能会发生这种情况:如果完成了这个转换,并且你把结果当作它仍然是一个宽字符串,你会得到乱码。如果没有完成并且您将结果视为 ASCII 字符数组,则通常会得到一个字符,如果原始字符仅包含 ASCII 范围的字符,因为在宽表示中,每隔一个字节都是零,高字节排在第二位。

从您的描述中我无法完全判断这是否发生在您身上。但我建议在调试器中停止这个东西,并查看接收到的值下的所有字符串数据,看看它是否以某种意想不到的方式被重新洗牌。如果它被改组了,问问自己为什么,看看你构建项目的方式。

几乎没有记录的格式嵌入到现有类型中,作为一种很难找到的替代内存布局,即使按照 MS 开发的 how-many-string-formats-can-we-make-up 标准,只是关于让我尖叫。这几乎和第一次尝试将“GetModuleFileName”识别为用于获取当前可执行文件路径的函数一样糟糕。

【讨论】:

【参考方案4】:

您的对象需要在 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 等。

【讨论】:

【参考方案5】:

您也可以使用 CComBSTR 代替 BSTR。 CComBSTR 比 BSTR 更智能,它负责分配和释放内存。

【讨论】:

【参考方案6】:

我的建议是将您的字段放在 Variant 中(即使是临时的),然后使用 Variant 流代码来展平数据,并在另一端反序列化它。

这是您可以使用的流式代码(抱歉,它已经有 20 年的历史了 :))

链接:https://github.com/kasajian/VariantStream/blob/master/VariantStream.h

这里粘贴的代码有点冗长。

【讨论】:

以上是关于在 MSMQ 消息中发送具有 BSTR 值类型的 COM 对象的主要内容,如果未能解决你的问题,请参考以下文章

C# 消息队列之MSMQ

转MSMQ 微软消息队列 简单 示例

我们如何加快从 MSMQ 接收消息的速度?

队列不存在或您没有足够的权限来执行该操作。通过 MSMQ 发送消息时出现异常

关于MSMQ

通过 MSMQ 发送文件