Vista は DCOM 呼び出しのインターフェイス ID をより厳密にチェックしますか?(スタブが不正なデータを受信しました)?

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

  •  09-06-2019
  •  | 
  •  

質問

この質問が長くなり、語り口調になってしまうことを皆さんがご容赦いただければ幸いです。この状況をブログで詳しく説明することにしました。後でジョエルからのこのサイトへの招待状を見て、誰かが状況について洞察を持っているかどうかを確認するためにここに貼り付けようと思いました。

私は、ATL を使用して C++ で記述された中間層 COM+ コンポーネントに対して DCOM を通信する Visual Basic シック クライアントで構成されるアプリケーションを作成し、現在サポートしています。当社の 8 つのオフィスすべてで実行されます。各オフィスは、COM+ アプリケーション (18 の個別のコンポーネントで構成される) と SQLServer を含むバックエンド サーバーをホストしています。通常、SQLServer は同じバックエンド サーバー上にありますが、そうである必要はありません。

私たちは最近、当社最大のオフィスであるニューヨークのバックエンド サーバーを MSC クラスターから VMWare の ESX テクノロジーでホストされる新しい仮想マシンに移行しました。COM+ アプリケーションの場所が古いサーバーから別の名前の新しいサーバーに移動したため、すべてのクライアントをリダイレクトして、新しいサーバーで COM+ アプリケーションをアクティブ化する必要がありました。この手順は、同様のインフラストラクチャのアップグレードを行ったいくつかの小規模オフィスで基本的に同じことを行っていたため、昔ながらの手順でした。

すべてが日常的に行われているように見え、月曜日の朝にはオフィス全体 (約 1,000 台の Windows XP ワークステーション) が新しいサーバーで問題なく稼働していました。しかし、その電話は私のモバイル グループからのものでした。VPN 接続を使用して在宅勤務している弁護士がいましたが、新しいサーバーにリダイレクトされた後、奇妙なエラーが発生していました。

Error on FillTreeView2 - The stub received bad data.

はぁ?このエラーメッセージはこれまで見たことがありませんでした。新しいサーバーだったのでしょうか?しかし、オフィス内のすべてのワークステーションは正常に動作していました。私はモバイル グループに、弁護士を古いサーバー (まだ稼動していた) に切り替えるように伝えたところ、エラーは消えました。それで、違いは何でしたか?この弁護士は自宅で Vista を実行していたことが判明した。

私たちのオフィスでは Vista を実行していませんが、自宅で Vista を実行している弁護士が何人かいます (ニューヨークのオフィスにも確かに何人かいます)。私も同様ですが、この問題は見たことがありません。問題があることを確認するために、Vista ラップトップを起動し、新しいサーバーを指定したところ、同じエラーが発生しました。古いサーバーに戻すと、正常に動作しました。明らかに、Vista と新しいサーバー上のコンポーネントに何らかの問題がありました。この問題は XP クライアントには影響しないようでした。どうなり得るか?

次に停止します -- 私のラップトップ上のアプリケーション エラー ログです。これにより、エラーに関する詳細情報が得られました。

Source:        Microsoft-Windows-RPC-Events
Date:          9/2/2008 11:56:07 AM
Event ID:      10
Level:         Error
Computer:      DevLaptop
Description:   Application has failed to complete a COM call because an incorrect
interface ID was passed as a parameter.

The expected Interface ID was 00000555-0000-0010-8000-00aa006d2ea4, 
The Interface ID returned was 00000556-0000-0010-8000-00aa006d2ea4.

User Action - Contact the application vendor for updated version of the application.

インターフェイス ID は、謎を解明するために必要な手がかりを提供してくれました。「予想される」インターフェイス ID は、MDAC の Recordset インターフェイス、具体的にはそのインターフェイスのバージョン 2.1 を識別します。「返された」インターフェイスは、Recordset の新しいバージョン (バージョン 2.5 は、vtable -- メソッド Save の最後に 1 つの追加エントリが含まれている点でバージョン 2.1 と異なります) に対応します。

実際、私のコンポーネントのインターフェイスは、出力パラメータとして Recordset を渡す多くのメソッドを公開しています。それでは、彼らは突然、異なるインターフェイス ID を持つ新しいバージョンの Recordset を返したのでしょうか?確かにそのようでした。そして、なぜそれが重要なのかと思いました。vtable は、古いインターフェイスのクライアントには同じように見えます。実際、DCOM ではなく、インプロセス COM について話しているのであれば、この一見無害なインピーダンスの不一致は黙って無視され、問題は発生しなかったのではないかと思います。

もちろん、プロセスとマシンの境界が影響する場合は、クライアントとサーバーの間にプロキシとスタブが存在します。この場合、無料のスレッド マーシャラーでタイプ ライブラリ マーシャリングを使用していました。したがって、解決すべき謎が 2 つありました。

新しいサーバーのメソッドから出力パラメーターで異なるインターフェイスを返したのはなぜですか?

これが Vista クライアントのみに影響するのはなぜですか?

私のサーバー ソフトウェアは 8 つのオフィスのそれぞれのサーバーでホストされていたため、Vista クライアントを順番にすべてのオフィスに向けて、Vista に問題があるものとそうでないものを確認してみることにしました。点灯テスト。古いサーバーの一部は引き続き Vista で動作しましたが、新しいサーバーは動作しませんでした。古いサーバーの一部はまだ Windows 2000 を実行していましたが、新しいサーバーは 2003 でしたが、それは問題ではないようでした。

コンポーネント DLL の日付を比較した結果、クライアントが 2003 Vista より前の日付のコンポーネント DLL を持つサーバーを指定していれば問題ないことがわかりました。しかし、2003 年以降の日付の DLL が含まれているものには問題がありました。信じられないかもしれませんが、サーバー コンポーネントのコードには長年にわたって (または少なくとも重大な) 変更はありませんでした。どうやら、日付の違いは単に開発マシン上でコンポーネントを再コンパイルしたためのようです。そして、それらの再コンパイルの 1 つが 2003 年に行われたようです。

電球がつきました。Recordset をサーバーからクライアントに渡すとき、ATL C++ コンポーネントはインターフェイスを _Recordset として参照します。このシンボルは、msado15.dll 内に埋め込まれたタイプ ライブラリから取得されます。これは C++ コードにあった行です。

#import "c:\Program Files\Common Files\System\ADO\msado15.dll" no_namespace rename ( "EOF", "adoEOF" )

msdad15.dll の 15 に騙されないでください。どうやら、この DLL は、長い一連の MDAC バージョンで名前が変更されていないようです。

私が当時アプリケーションをコンパイルしたとき、MDAC のバージョンは 2.1 でした。したがって、_Recordset は 2.1 インターフェイス ID でコンパイルされ、それがこれらのコンポーネントを実行しているサーバーによって返されるインターフェイスです。

すべてのクライアントは、1999 年に生成された (おそらく) COM+ アプリケーション プロキシを使用しています。インターフェイスを定義するタイプ ライブラリには次の行が含まれています。

importlib("msado21.tlb");

これは、私のメソッドの出力パラメーターに Recordset のバージョン 2.1 が期待される理由を説明しています。明らかに問題は 2003 年の再コンパイルにあり、その時点では _Recordset シンボルがバージョン 2.1 に対応していなかったという事実です。実際、_Recordset は、別個のインターフェイス ID を持つ 2.5 バージョンに対応していました。私にとっての解決策は、C++ コード内のすべての参照を _Recordset から Recordset21 に変更することでした。コンポーネントを再構築し、新しいサーバーにデプロイしました。ほら、クライアントはまた幸せそうでした。

結論として、私には厄介な疑問が 2 つ残っています。

Vista クライアントではプロキシ/スタブ インフラストラクチャの動作が異なるように見えるのはなぜですか?Vista は、XP よりもメソッド パラメータから返されるインターフェイス ID のチェックを厳密に行っているようです。

このようなことが起こらないようにするには、1999 年にこれを別の方法でコーディングする必要がありましたか?インターフェイスは不変であるはずですが、新しいバージョンの MDAC で再コンパイルしたときに、メソッドが出力パラメータとして異なる Recordset インターフェイスを返すようになったため、誤ってインターフェイスを変更してしまいました。私の知る限り、当時のタイプ ライブラリにはバージョン固有のシンボルがありませんでした。つまり、MDAC タイプ ライブラリの新しいバージョンでは Recordset21 が定義されていましたが、そのシンボルは 2.1 タイプ ライブラリでは使用できませんでした。

役に立ちましたか?

解決

Microsoft がセキュリティ信仰を獲得したとき、DCOM (およびその基盤となる RPC) は多くの注目を集め、セキュリティ ホールを塞ぐために確実に変更が加えられ、その結果、より厳格なマーシャリングが行われました。これが Vista では表示され、XP では表示されないことに驚いていますが、Vista では追加のチェックが追加された可能性があります。あるいは、XP ではオプションであった厳密性が Vista では必須になった可能性もあります。

私は MDAC について十分な知識がないので、あなたがこれを防ぐことができたかどうかを知ることはできませんが、セキュリティは Microsoft が下位互換性を犠牲にする数少ない分野の 1 つであることは知っています。そのため、あなたには何もできなかった可能性があります。」 1999年に遡ります。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top