MSMQメッセージでBSTR値タイプを持つCOMオブジェクトを送信します
質問
C ++でMSMQメッセージを介してCOMオブジェクトを送信しようとしています。これが私のオブジェクトです:
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を試しましたが、結果も間違っていました。どういうわけか、文字列がシリアル化されていないようです。
これらは、ATLコンポーネントのgetおよびset関数の一部です。
これは正常に動作します:
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()
は正常に機能します。対照的に、キューからメッセージを読み取った後、コンシューマーで取得しようとすると、何も返されません。サイズなどの他の値は問題なく返されます。
MSMQを介してCOMオブジェクト内でBSTR値を送信する方法を誰もが知っていますか?
似たような例を見つけようとしましたが、まったく無駄です。
問題は、値を抽出する方法に応じて、奇妙な文字を含む非常に奇妙な値または16進値のいずれかを取得することです..問題は、正しい値を取得できないことです。
しかし、私は疑問に思っていました... BSTR値を送信することは可能ですか?私が間違っていない場合、それは文字列へのポインタです...私は2つの異なるプロセス(つまりプロデューサとコンシューマ)を実行しているので、異なるメモリブロックを使用し、異なるマシンで実行することを意図しています。 ..
この情報を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文字はそれぞれ1バイト以上を占有します-したがって、IStream読み取り/書き込みメソッド呼び出しの乗算。
他のヒント
WCHARを単に渡すと、長さ情報は失われます。 BSTRの形式が正しくないため、おそらくすべての悲しみを引き起こしています。コンポーネント間で使用するには、 SysAllocString
を使用する必要があります。 MSDN -備考セクションを参照してください。 。試してください:
BSTR imagen = SysAllocString(L"imagen1.jpg");
OK、この答えは最近奇妙なことをしていることに依存しますが、当てはまるかもしれません。 ずっと昔、VBアプリからVB6とVC ++ 6(pro)の下にVB文字列を渡す必要がありました。 VC ++アプリに。長さは問題ありませんでしたが、多くの場合、反対側で1つの文字を受け取りました。
問題は受信アプリです。 Unicode用にコンパイルされたのではなく、ANSIプロジェクトとしてコンパイルされました。転送の反対側でそれを解凍したCOMレイヤーコードは、MSDNの不明瞭なコーナーで本の抜粋でしか文書化されていない興味深いトリックを行いました。それはABSTRを作成しました。
Abstrは実際には型ではありません。宣言する方法はありません。実際には、BSTRの基礎となるストレージを再フォーマットするため、C ++のASCII char *のふりをすることができます。これは、最初にBSTRがそのヘッダーの後の最初の文字を指すことを確認し(C ++、IIRCのこのような構造では一般的です)、実際の文字列データをシャッフルして最初のすべてを含むようにしますすべての2番目のバイトが続くバイト。これの別名は「純粋な悪」です。
2つの very bad things は次のように発生する可能性があります。この変換が行われ、結果がまだ広い場合と同様に扱う場合文字列では、意味がわからなくなります。それが行われず、結果をASCII文字配列として扱う場合、ワイド表現では1バイトおきにゼロがあり、上位バイトが2番目に来るため、元の文字がASCII範囲の文字のみを含む場合、通常は単一の文字を取得します。
これがあなたに起こったことであるかどうか、私はあなたの説明からはまったくわかりません。ただし、デバッガーで停止し、その受信値の下にあるすべての文字列データを調べて、予期しない方法でシャッフルされているかどうかを確認することをお勧めします。シャッフルされている場合は、理由を自問して、プロジェクトのビルド方法を確認してください。
ほとんど文書化されていない形式が、MS開発の多くの文字列形式、メイクアップ標準でさえ、見つけるのが非常に難しい代替メモリレイアウトとして既存の型に組み込まれているという事実約叫んだ。 " 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 ptrなどを確認する必要があります。
代わりに、BSTRの代わりにCComBSTRを使用できます。 CComBSTRはBSTRよりも賢く、メモリの割り当てと割り当て解除を処理します。
私の提案は、フィールドをVariantに(一時的であっても)配置し、Variantストリーミングコードを使用してデータをフラット化し、反対側でデシリアライズすることです。
使用できるストリーミングコードは次のとおりです(20年ほど前にごめんなさい:))
リンク: https://github.com/kasajian/VariantStream/blob /master/VariantStream.h
コードはここに貼り付けるには少し冗長です。