отправить объект COM с типом значения BSTR в сообщении MSMQ

StackOverflow https://stackoverflow.com/questions/627254

  •  06-07-2019
  •  | 
  •  

Вопрос

Я пытаюсь отправить объект 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.

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

Это было полезно?

Решение

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

Другие советы

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Код немного подробный, чтобы вставить его здесь.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top