отправить объект COM с типом значения BSTR в сообщении MSMQ
Вопрос
Я пытаюсь отправить объект 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 р>
Код немного подробный, чтобы вставить его здесь.