次の方法で共有


#importでmsxml6.dllを指定しているのに、msxml3.dllがロードされる

質問

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等の明示的な指定は(現時点では)行わず、別の個所に注力して不具合を解析したいと思います。

皆様、大変ありがとうございました。