C++ – send a COM object with a BSTR value type in a MSMQ message

bstrccommsmqsend

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?

Best Answer

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() ):

pStm->Write(&(img.color), (ULONG)sizeof(float), &cb);

For BSTRs do this:

int length = SysStringLen( img.uname );
pStm->Write(&length, (ULONG)sizeof(int), &cb);
if( length > 0 ) {
   pStm->Write( img.uname, (ULONG)(length * sizeof(WCHAR) ), &cb);
}

Similar for reading (the 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;
}

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.