2

我正在尝试通过 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 更牵强。

对此有任何想法吗?

4

6 回答 6

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 读/写方法调用中的乘法。

于 2009-03-10T09:59:42.553 回答
0

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

BSTR imagen = SysAllocString(L"imagen1.jpg");
于 2009-03-09T17:42:18.520 回答
0

好的,这个答案取决于你这些天做了一些奇怪的事情,但可能适用。很久很久以前,我不得不从 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”识别为用于获取当前可执行文件路径的函数一样糟糕。

于 2009-03-09T21:43:51.903 回答
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 等。

于 2009-03-09T21:48:15.793 回答
0

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

于 2009-03-10T01:19:20.997 回答
0

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

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

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

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

于 2017-08-09T06:03:50.950 回答