質問
2022年11月22日火曜日 2:09
■環境
Windows 10 Pro 21H1 OSビルド 19043.2311
Visual Studio Professional 2017 ver.15.9.51
MFCアプリケーション(x86)
■疑問
あるアプリケーションでMSXML2::IXMLDOMDocumentPtrを利用してXMLファイルをロードしています。
ソースコード上で、#importにて<msxml6.dll> named_guidsを指定しているのですが、Visual Studioの出力ウィンドウを見ると'C:\Windows\SysWOW64\msxml3.dll'がロードされています。
msxml6.dllを指定しているのにmsxml3.dllがロードされるのは何故なのでしょうか。またこれは問題ない動作なのでしょうか。
マイクロソフトのドキュメントサイトにて#importを参照したのですが、関連しそうな話は見つけられませんでした。
https://learn.microsoft.com/en-us/cpp/preprocessor/hash-import-directive-cpp?view=msvc-150
msxml6.dllとmsxml3.dllとの差がわからないのですが、MSXML2::IXMLDOMDocumentPtrはmsxml3.dllのみに含まれる機能であり、msxml6.dllからmscml3.dllへの依存関係が構築されていて、msxml6.dllの機能が使われていないからmsxml3.dllだけロードされるとか、そういう挙動だったりするのでしょうか。
■背景
あるアプリケーションでヒープ破損が発生する不具合があり、その解析の中で「このdll読み込みの違いに問題はないのか」という話題が出たため、調査しております。ヒープ破損自体は本件以外にもWinDbgとグローバルフラグを用いて追跡しようとしていますが、それはそれとして、不自然と思われる挙動は全て調査しようとしております。
すべての返信 (6)
2022年11月22日火曜日 12:29 ✅回答済み
ソースコード上で、#importにて<msxml6.dll> named_guidsを指定しているのですが、Visual Studioの出力ウィンドウを見ると'C:\Windows\SysWOW64\msxml3.dll'がロードされています。
msxml6.dllを指定しているのにmsxml3.dllがロードされるのは何故なのでしょうか。またこれは問題ない動作なのでしょうか。
すでに指摘されているとおり、#import は読み込む DLL を決定するものではありません。
実際に読み込まれる DLL は**「実行環境でそのクラス ID に紐付けられている DLL」**です。
たとえば、gekka さんのサンプルに出ている CLSID_DOMDocument30 のケースだと、 HKEY_CLASSES_ROOT\CLSID\F5078F32-C551-11D3-89B9-0000F81FE221}\InProcServer32 に書かれている DLL が読み込まれます。
このケースで手元の環境だと msxml3.dll となりますね。
COM を使ったときにどの DLL が読み込まれるかは、使う CLSID や実行環境によるため、一律に良い・悪いを言えるものではありません。
2022年11月22日火曜日 12:49 ✅回答済み
バージョンに依存しない ProgID や CLSID を指定した場合、MSXML3 がロードされる仕様です。
インスタンスを生成している箇所を確認してみてください。
基本的にはバージョン指定の MSXML6 を呼ぶようにし、6 が使えない環境でのみバージョン指定無しの MSXML3 を利用すれば良いと思います。そして 1.0、2.0、2.5、2.6、4.0 を使うことは、現時点ではまずありえません。ちなみに MSXML3 までは MSXML 2.x を置き換える "リプレース更新" でしたが、MSXML4 以降はバージョン別に共存インストールが可能な仕組みに方針転換され、個別に呼び分けられる設計になりました。
ただし MSXML6 は、XDR スキーマに対応していないため、そうしたレガシー機能を必要とするようなケースでは、意図的に旧バージョンを呼ぶようなケースもあります。
以前は Microsoft blog などにて、こうした MSXML のバージョン分離に関する記事が掲載されていたのですが、今はサイトが無くなっているようです。移転先も見つからなかったので、ひとまず、当時の記事の一つを Internet Archive から拾ってみます。
2022年11月22日火曜日 5:40
もしかしたら該当モジュール以外の他のモジュールが、"msxml3.dll" をロードさせてるのかも。
デバッグ対象プロセスに WinDbg をアタッチさせてるなら、ブレークイン状態で "lm" コマンドを実行すればそのプロセス内にロードされているモジュール一覧が出てくるので、その中に "msxml6.dll" も含まれているか確認してみては?
2022年11月22日火曜日 10:26
msxml6.dllだけを意図しているのにmsxml3も読み込まれているのか、msxml3だけ読み込まれているのか。
importはCOMインターフェース定義を生成するだけで、dllを勝手に読み込んでくれるものではないです。
インプロセスのCOMならCreateInstanceするまではdllは読み込まれません。
以下のコードを、VisualStudioのツール->デバッグ->ウィンドウ->モジュールでdllの読み込み状態を見ると、CreateInstanceを呼ぶまではmsxml3を6も存在しません。
6.0をCreateInstanceするとmsxml6.dllのみ、3.0だとmsxml3.dllだけ読み込まれるので、依存はないようです。
DependencyWalkerでmsxml6.dllの依存関係を調べてみましたが、msxml3.dllへの依存はありませんでした。
//#import "msxml6.dll" named_guids
//#include "msxml6.h"
//DEFINE_GUID(CLSID_DOMDocument30, 0xf5078f32, 0xc551, 0x11d3, 0x89, 0xb9, 0x00, 0x00, 0xf8, 0x1f, 0xe2, 0x21);//msxml2.hを使わずに
CoInitialize(NULL);
IXMLDOMDocumentPtr docPtr;
//DebugBreak();
/* ProgIDで */
//docPtr.CreateInstance("Msxml2.DOMDocument");//msxml3.dllを読み込んだ
//docPtr.CreateInstance("Msxml2.DOMDocument.3.0"); //msxml3.dllを読み込んだ
docPtr.CreateInstance("Msxml2.DOMDocument.6.0"); //msxml6.dllが読み込まれる
//両方実行すれば、msxml3とmsmlx6の両方とも読み込まれる
/* CLSIDで */
//docPtr.CreateInstance(CLSID_DOMDocument);//msxml3.dllを読み込んだ
//docPtr.CreateInstance(CLSID_DOMDocument30); //msxml3.dllを読み込めた
//docPtr.CreateInstance(CLSID_DOMDocument60); //msxml6.dllが読み込まれる
DebugBreak();
WCHAR sXML[] = L"<dummy/>";
WCHAR sDummy[] = L"dummy";
VARIANT_BOOL result;
if (SUCCEEDED(docPtr->loadXML(sXML, &result)))
{
if (result == VARIANT_TRUE)
{
IXMLDOMNodeList* list = NULL;
if (SUCCEEDED(docPtr->getElementsByTagName(sDummy, &list)))
{
long length = 0;
list->get_length(&length);
list->Release();
}
}
}
CoUninitialize()
CreateInstanceでバージョン指定してないか間違えてたらmsxml3.dllを読み込んでしまうことはありえそう。
IXMLDOMDocumentPtrをCreateInstanceしてる箇所にブレークポイントを置いて、呼び出し前にmsxml3.dllが読み込み済みか、呼び出し後にmsxml3が読み込まれるのかを調べてみては?
#msxml6が無いようなものすごく古い環境のために、msxml6を読み込めなかったらmsxml3にフォールバックさせるようなコードが書いてあるとか?
個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)
2022年11月24日木曜日 1:39
Takahiro Toyamaさん、こんにちは。フォーラムオペレーターのKumoです。
MSDNフォーラムにご投稿くださいましてありがとうございます。
ご質問いただいた件ですが、その後いかがでしょうか。
皆様から寄せられた投稿はお役に立ちましたか。
追加でご確認いただいたことなどあれば、追記いただくことで回答がつきやすくなります。
よろしくお願いいたします。
MSDN/ TechNet Community Support Kumo ~参考になった投稿には「回答としてマーク」をご設定ください。なかった場合は「回答としてマークされていない」も設定できます。同じ問題で後から参照した方が、情報を見つけやすくなりますので、 ご協力くださいますようお願いいたします。また、MSDNサポートに賛辞や苦情がある場合は、MSDNFSF@microsoft.comまでお気軽にお問い合わせください。~
2022年11月25日金曜日 0:25
皆様、ご回答ありがとうございます。またお返事遅くなり申し訳ございません。
失;ですが、まとめてお返事させて下さい。
■ロードされているDLLについて
msxml6.dllはロードされておりませんでした。msxml3.dllのみロードされています。
■ロード対象のタイプライブラリの指定について
import文では、ProgIDやCLSIDの指定は行っておりません。以下がコードに書いてあるのみです。
#import <msxml6.dll> named_guids
■再現テスト
Visual Studio Professional 2017で新しいMFCダイアログアプリケーションを作成してダイアログ上にボタンを追加し、ダイアログの.cppに以下を追加しました。実アプリケーションと同じようにProgIDを以下のように指定しました(質問文で記載しておらず申し訳ありません)。
#import <msxml6.dll> named_guids
void CMFCApplication1Dlg::OnBnClickedButton1()
{
MSXML2::IXMLDOMDocumentPtr doc("MSXML.DOMDocument");
doc->put_async(VARIANT_FALSE);
}
そうしたところ、やはり同じようにmsxml3.dllのみがロードされました。
■まとめ
Azuleanさん、魔界の仮面弁士さんの書き込みから、(import指定したdllではなく)実際に利用するオブジェクトが;納されるDLLがロードされることを理解しました。どうもありがとうございます。
今回問題が発生しているアプリケーションに関して、MSXML利用モジュールは複数のシステムで今まで15年ほどは使っており問題が発生したことはなかったので、とりあえずはProgID等の明示的な指定は(現時点では)行わず、別の個所に注力して不具合を解析したいと思います。
皆様、大変ありがとうございました。