отправить объект COM с типом значения BSTR в сообщении MSMQ - PullRequest
2 голосов
/ 09 марта 2009

Я пытаюсь отправить объект COM поверх сообщения MSMQ на C ++. Это мой объект:

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, и результат оказался неверным. Почему-то похоже, что строки не сериализованы.

Вот некоторые функции get и set для моего компонента ATL:

Этот работает нормально:

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(), это работает нормально. Напротив, когда я пытаюсь получить его у потребителя, после прочтения сообщения из очереди оно ничего не возвращает. Другие значения, такие как размер, возвращаются без проблем.

Кто-нибудь знает, как отправить значение BSTR внутри COM-объекта через MSMQ?

Я пытался найти несколько похожих примеров, но безуспешно.

Дело в том, что я получаю либо очень странное значение со странными символами, либо шестнадцатеричное значение, в зависимости от того, как я извлекаю значение ... Дело в том, что я никогда не получаю правильное значение.

и мне было интересно, однако ... мы уверены, что можно отправить значение BSTR? если я не ошибаюсь, это указатель на строку ... Я запускаю два разных процесса (т.е. производителя и потребителя), поэтому они используют разные блоки памяти, и они предназначены для запуска на разных машинах, так. ..

Я пытался отправить эту информацию как тип VARIANT .. но тоже потерялся. Однако, это кажется немного менее надуманным, чем отправка BSTR.

ЛЮБЫЕ ИДЕИ НА ЭТОМ?

Ответы [ 6 ]

1 голос
/ 10 марта 2009

Проблема в том, что сериализация класса 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.

0 голосов
/ 09 августа 2017

Я предлагаю поместить ваши поля в Variant (даже если временно), затем использовать потоковый код Variant для выравнивания данных и десериализации их на другом конце.

Вот потоковый код, который вы можете использовать (извините, ему около 20 лет :))

Ссылка: https://github.com/kasajian/VariantStream/blob/master/VariantStream.h

Код немного многословен для вставки сюда.

0 голосов
/ 10 марта 2009

В качестве альтернативы вы можете использовать CComBSTR вместо BSTR. CComBSTR умнее, чем BSTR, он заботится о выделении и освобождении памяти.

0 голосов
/ 10 марта 2009

Ваш объект должен создать копию строки как в получателе, так и в установщике:

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 и т. Д.

0 голосов
/ 10 марта 2009

Хорошо, этот ответ зависит от того, делаете ли вы что-то странное в наши дни, но может быть применим. Давным-давно мне пришлось передавать строку VB под VB6 и VC ++ 6 (pro) из приложения VB. в приложение VC ++. Длина прошла хорошо, но я часто получал один символ на другой стороне.

Проблема была в том, что получало приложение. не был скомпилирован для Unicode, а скорее как проект ANSI. Код уровня COM, который распаковал его на дальней стороне передачи, сделал интересный трюк, который я нашел только в неясном углу MSDN в отрывке книги: он создал ABSTR.

ABSTR на самом деле не тип. Там нет никакого способа объявить один. На самом деле это переформатирование основного хранилища BSTR, так что вы можете притвориться, что это ASCII-символ * в C ++. Для этого сначала необходимо убедиться, что BSTR указывает на первый символ после его заголовка (что в любом случае типично для таких структур в C ++, IIRC), а затем , а затем перетасовывать фактические строковые данные, чтобы он содержал все первые байты. сопровождаемый всеми вторыми байтами. Другое название для этого - «чистое зло».

Два очень плохо вещи могут произойти следующим образом: если это преобразование выполнено, и вы обрабатываете результат, как если бы это была строка широких символов, Вы получаете бред Если этого не сделано, и вы рассматриваете результаты как массив символов ASCII, вы обычно получаете один символ, если оригинал содержал только символы диапазона ASCII, поскольку в широком представлении каждый второй байт равен нулю, а старшие байты идут вторыми.

Я не могу точно сказать из вашего описания, что это случилось с вами. Но я рекомендую остановить эту вещь в отладчике и просмотреть все строковые данные в соответствии с полученным значением, чтобы увидеть, не было ли оно каким-то неожиданным образом переставлено. Если он был перемешан, спросите себя, почему, и посмотрите, как вы создали проект.

Факт почти недокументированного формата вклинился в существующий тип в качестве альтернативного макета памяти, который действительно трудно найти, даже по стандартам «сколько строк форматов мы можем составить», просто о заставил меня кричать. Это было почти так же плохо, как попытка идентифицировать «GetModuleFileName» в первый раз, как функцию, которая использовалась для получения пути к текущему исполняемому файлу.

0 голосов
/ 09 марта 2009

Если вы просто передадите WCHAR - информация о длине теряется. BSTR неправильно сформирован, и это, вероятно, вызывает у вас все горе. Вам нужно использовать SysAllocString, чтобы использовать его в разных компонентах. См. MSDN - раздел Примечания . Попробуйте:

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