Microsoft の「The case of the DLL that was not present in memory despite not being formally unloaded, part 1」は、Microsoft 製品の新機能追加や設定変更を知らせる記事ではありません。結論から言うと、shell32.dll が原因に見えるクラッシュでも、実際には別のコンポーネントが DLL のメモリ領域を強制的に解放していた可能性があるという、Windows アプリのクラッシュ解析事例です。2026年6月25日に Microsoft Dev Blogs の The Old New Thing で公開された内容では、設定変更や移行期限は示されておらず、管理者や開発者が確認すべきポイントは「クラッシュの見かけの発生元を早合点しないこと」と「DLL のロード状態と実メモリ状態を分けて調査すること」です。(Microsoft for Developers)
Microsoft の「The case of the DLL that was not present in memory despite not being formally unloaded, part 1」とは
この公式記事は、Raymond Chen 氏による Microsoft Dev Blogs「The Old New Thing」の技術解説です。対象は Windows の DLL ロード、アンロード、例外処理、クラッシュダンプ解析に関わる内容で、Microsoft 365 や Azure のような管理ポータル上の設定変更を案内するものではありません。(Microsoft for Developers)
記事で扱われているのは、あるサードパーティ製プログラムで多数発生していたクラッシュです。クラッシュダンプを見ると shell32.dll が関係しているように見えたため、当初は shell32.dll 側の問題として扱われました。しかし調査を進めると、shell32.dll は原因ではなく、すでにメモリ上から失われていた別 DLL を呼び出してしまった「被害者」である可能性が見えてきます。
この点は、運用担当者にとって重要です。Windows 標準 DLL 名がクラッシュログに出ているからといって、すぐに OS や Microsoft 側の不具合と断定すると、調査の方向を誤ることがあります。
今回の更新ポイントは「機能変更」ではなく「調査観点の整理」
今回押さえるべき更新ポイントは、サービス仕様の変更ではなく、Windows アプリの障害調査で使える考え方です。
| 確認項目 | 今回の整理 |
|---|---|
| 新機能の追加 | 公式記事上では示されていません |
| 管理ポータルの設定変更 | 公式記事上では示されていません |
| 移行期限・廃止期限 | 公式記事上では示されていません |
| 主な対象 | Windows アプリ、DLL を利用するデスクトップアプリ、クラッシュダンプを調査する開発者・管理者 |
| 実務上の重要点 | クラッシュ元に見える DLL と、実際の原因コンポーネントを切り分けること |
つまり、この記事を「Microsoft の新機能・変更点」として読む場合は、設定値の変更ではなく、障害解析時の判断基準が更新された参考事例として捉えるのが適切です。
公式記事で発生していたクラッシュの流れ
記事では、対象プログラムのクラッシュダンプにスタックオーバーフローの明確な兆候があったと説明されています。例外処理の途中でさらに例外が発生し、RtlLookupFunctionEntry から KiUserExceptionDispatch に至る処理が繰り返され、最終的にスタックを使い果たしてプロセスが終了する流れです。(Microsoft for Developers)
ここで重要なのは、最初に見えているクラッシュ地点だけを見ると shell32.dll が疑わしく見える点です。実際のスタックでは、プロセス終了時の DLL 切り離し処理の中で shell32.dll のクリーンアップ処理が動き、その途中で combase!CoTaskMemFree を呼び出そうとしていました。
しかし、調査の焦点は次の点に移ります。
combase!CoTaskMemFree を実行しようとしたアドレスが、実際には実行可能なメモリではなかったのです。記事では、デバッガー上で該当メモリ領域が MEM_FREE、保護属性が PAGE_NOACCESS と表示され、combase.dll 全体がメモリ上からアンロードされたような状態だったと説明されています。(Microsoft for Developers)
なぜ「正式にアンロードされていない DLL」がメモリから消えたのか
通常、DLL は Windows ローダーの管理下でロード・アンロードされます。Microsoft Learn の FreeLibrary の説明では、DLL モジュールの参照カウントが減り、参照カウントがゼロになるかプロセスが終了すると、モジュールがプロセスのアドレス空間からアンロードされるとされています。アンロード前には、必要に応じて DLL の DllMain に DLL_PROCESS_DETACH が通知されます。(Microsoft Learn)
ところが、今回の記事では loader の管理情報上、combase.dll はまだロード済みとして扱われていました。さらに load count が 0xFFFFFFFF、つまり pinned された状態で、通常の FreeLibrary によってアンロードされる想定ではない状態だったと説明されています。(Microsoft for Developers)
この矛盾から、記事では「誰かが FreeLibrary ではなく、メモリ領域そのものを明示的に解放したのではないか」という仮説に進みます。たとえば、メモリ破損、未初期化変数、誤ったポインター管理によって、解放すべきではない DLL のベースアドレスを VirtualFree のような処理に渡してしまった可能性です。
Microsoft Learn の VirtualFree の説明では、同関数は呼び出し元プロセスの仮想アドレス空間内のページ領域を解放またはデコミットする関数です。MEM_RELEASE を指定した場合、予約された領域全体が free 状態になり、解放後のメモリを参照するとアクセス違反につながります。(Microsoft Learn)
影響範囲:Microsoft 製品というより Windows アプリ運用全般に関係する
今回の内容は、特定の Microsoft サービスに対して「この設定を変更すべき」という案内ではありません。影響範囲として意識すべきなのは、Windows 上で DLL を利用するアプリケーション全般です。
特に注意したいのは、次のような環境です。
| 環境・システム | 注意すべき理由 |
|---|---|
| サードパーティ製デスクトップアプリ | Windows 標準 DLL がクラッシュログに出ても、実際はアプリ側や拡張モジュール側のメモリ破損が原因の場合があります |
| 業務アプリにプラグインやアドインを入れている環境 | 複数 DLL が同一プロセスに読み込まれるため、原因 DLL と被害 DLL の切り分けが難しくなります |
| セキュリティ製品、監視エージェント、入力補助ツールが常駐する端末 | API フックやプロセス注入を行う製品がある場合、クラッシュ再現条件が端末ごとに変わることがあります |
| C++ や .NET でネイティブ DLL を利用するアプリ | マネージドコード側の例外に見えても、ネイティブ DLL のメモリ管理不備が根本原因になることがあります |
| グローバル展開端末 | 言語や地域ではなく、インストールされている DLL、アドイン、エージェントの組み合わせで発生条件が変わる可能性があります |
記事内では、100件の直近クラッシュをピボットテーブルで確認したところ、shell32.dll に見えるクラッシュ以外にも複数の異なるクラッシュ分類があり、実際には同じ「DLL の強制的なメモリ解放」が多数の別々のクラッシュとして現れていたと説明されています。これは、1つの根本原因が複数のクラッシュバケットに分散して見える典型例です。(Microsoft for Developers)
設定変更と移行期限:今回の公式情報で確認できること
管理者が最初に確認したい「設定変更は必要か」「移行期限はあるか」については、以下のように整理できます。
| 項目 | 対応方針 |
|---|---|
| Microsoft 管理センターでの設定変更 | 今回の記事では案内されていません |
| Windows Update の特定パッチ適用指示 | 今回の記事では案内されていません |
| 移行期限・廃止予定日 | 今回の記事では案内されていません |
| 管理者がすぐ行うべきこと | 同種クラッシュが発生している端末で、クラッシュダンプ、読み込まれた DLL、常駐ソフト、拡張機能を確認する |
| 開発者が行うべきこと | FreeLibrary、VirtualFree、カスタムアロケーター、DllMain 内処理、未初期化ポインターを重点的に点検する |
「Microsoft の記事だから、何か設定を変更しなければならない」と考える必要はありません。むしろ、クラッシュ調査時に OS 標準 DLL を犯人扱いする前に、メモリ破損や不正なメモリ解放を疑うことが今回の実務上の学びです。
管理者が確認すべきポイント
クラッシュログの「表示上の原因 DLL」だけで判断しない
イベントログやクラッシュレポートに shell32.dll、combase.dll、ntdll.dll などの Microsoft DLL 名が出てくることは珍しくありません。しかし、それは「その DLL が壊れている」という意味とは限りません。
今回の記事でも、shell32.dll は最初に疑われましたが、Microsoft 側の分析では shell32.dll は被害者であり、別の何者かによって combase.dll のメモリが強制的に取り除かれた可能性が示されています。(Microsoft for Developers)
管理者は、ベンダーや社内開発チームへ問い合わせる際に、次の情報を一緒に渡すと調査が進みやすくなります。
- クラッシュダンプ
- 発生端末の OS バージョンと更新状態
- 対象アプリのバージョン
- インストール済みアドイン、プラグイン、エージェント
- セキュリティソフトや EDR の有無
- 発生直前の操作
- 同じ端末で再現するか、特定ユーザーだけで発生するか
複数のクラッシュバケットをまとめて見る
今回の事例では、shell32.dll に見えるクラッシュだけでなく、別 DLL や unknown access violation として見えているクラッシュも、同じ根本原因から派生している可能性がありました。記事では、100件の直近クラッシュを分布で確認し、同じ形の問題が複数の分類に散っていたと説明されています。(Microsoft for Developers)
そのため、管理者はクラッシュを1件ずつ個別の障害として見るのではなく、次の観点でまとめるとよいでしょう。
| 観点 | 見るべき内容 |
|---|---|
| 発生プロセス | 同じアプリで起きているか |
| 例外コード | access violation、stack overflow などが集中していないか |
| 発生タイミング | 起動時、終了時、印刷時、ファイル保存時などに偏りがあるか |
| 共通 DLL | 同じアドインや同じ常駐モジュールが読み込まれていないか |
| 端末条件 | 特定部署、特定イメージ、特定セキュリティ製品の端末に偏っていないか |
プロセス終了時の DLL_PROCESS_DETACH に注目する
今回のクラッシュは、プロセス終了時の DLL 切り離し処理の中で表面化しています。Microsoft Learn の DllMain の説明では、DLL_PROCESS_DETACH は DLL がプロセスの仮想アドレス空間からアンロードされるとき、またはプロセス終了時などに呼び出される通知です。(Microsoft Learn)
アプリ終了時だけクラッシュする場合、ユーザーは「終了時だから実害は小さい」と考えがちです。しかし、終了処理中のクラッシュは、解放順序、DLL 依存関係、メモリ破損、未完了のバックグラウンド処理などが表に出やすいタイミングです。
特に、終了時にだけ発生するクラッシュでは以下を確認します。
| 確認項目 | 判断基準 |
|---|---|
| DllMain 内で重い処理をしていないか | ファイル I/O、スレッド待機、DLL ロード・アンロードなどを行っていないか |
| 終了時に他 DLL の関数を呼んでいないか | すでに終了処理が進んだ DLL に依存していないか |
| グローバルオブジェクトのデストラクタ | 破棄順序に依存した処理がないか |
| COM オブジェクトやヒープの解放 | 解放済み領域を再度参照していないか |
| アドインやフック DLL | 終了時に不正なクリーンアップをしていないか |
Microsoft Learn の DLL ベストプラクティスでも、DllMain は loader lock が保持された状態で呼ばれるため、実行できる処理には大きな制約があり、複雑な初期化や終了処理を避けるべきとされています。(Microsoft Learn)
開発者が重点的に確認すべきコード
今回のような問題は、管理者だけでは根本原因まで追えないことがあります。社内開発アプリやベンダー製アプリで再現する場合、開発者は次のコード領域を優先して確認します。
| 確認対象 | 具体的な確認内容 |
|---|---|
VirtualFree / VirtualFreeEx 呼び出し | 解放対象のアドレスが本当に自分で確保した領域か。DLL のベースアドレスや関数ポインターを誤って渡していないか |
| 未初期化ポインター | 初期化前の変数が偶然 DLL のアドレスを保持していないか |
| カスタムメモリアロケーター | 解放リスト、所有権管理、二重解放の検出ができているか |
FreeLibrary 呼び出し | GetModuleHandle で取得したハンドルを誤って FreeLibrary に渡していないか |
| DllMain | 複雑な処理、他 DLL への依存、ロック取得、スレッド待機がないか |
| 終了処理 | グローバルオブジェクト、COM、CRT 終了処理の順序依存がないか |
| 例外処理 | 最初の例外後に例外処理自体が再帰的に壊れていないか |
特に VirtualFree は、正しく使えば通常のメモリ解放処理ですが、誤ったアドレスを渡すとプロセス内の重要なメモリ領域を破壊する可能性があります。今回の事例では、loader の管理情報では DLL が残っているのに、実メモリは解放済みという矛盾が調査の決め手になっています。
失敗しやすい判断
Microsoft DLL 名だけを見て OS 不具合と判断する
ntdll.dll、kernel32.dll、shell32.dll、combase.dll は、多くのアプリが利用する基盤的な DLL です。クラッシュスタックにこれらが出ることと、それらが根本原因であることは別です。
今回の事例でも、shell32.dll はクラッシュの見かけ上の発生地点に近い位置にいましたが、実際には先に壊れていた combase.dll のメモリ状態が問題の中心でした。
1つのクラッシュ分類だけを見て調査を終える
クラッシュバケットが異なっていても、根本原因が同じことがあります。今回の記事では、複数の stack overflow や unknown access violation が、同じ「DLL が強制的にメモリから取り除かれた」問題として説明されています。(Microsoft for Developers)
運用現場では、エラー名や DLL 名が違うだけで別件扱いにしてしまいがちです。しかし、同じアプリ、同じ端末群、同じ時期に発生している場合は、まとめて傾向を見るべきです。
終了時クラッシュを軽視する
終了時に発生するクラッシュでも、メモリ破損はもっと前の処理で起きている可能性があります。終了処理は、壊れた状態が最後に表面化するタイミングにすぎないことがあります。
たとえば、業務アプリで「閉じるときだけ落ちる」場合でも、実際にはファイルを開いた時点、アドインを読み込んだ時点、特定画面を表示した時点でメモリ破損が起きているかもしれません。終了時のスタックだけでなく、再現手順全体を記録することが重要です。
グローバル環境の管理者向けチェックリスト
グローバル展開している企業では、国や地域ごとにインストール済みアプリ、セキュリティエージェント、入力システム、プリンタードライバー、業務アドインが異なります。今回のような DLL 関連クラッシュでは、OS 言語よりも「同一プロセスに何が読み込まれているか」が重要です。
| チェック項目 | 実務での見方 |
|---|---|
| 発生国・拠点 | 地域差がある場合、ローカルアプリや周辺機器ドライバーの差を確認する |
| 端末イメージ | 同じマスターイメージの端末で集中していないかを見る |
| EDR・ウイルス対策 | 特定バージョンのエージェント導入後に増えていないか確認する |
| アドイン | Office、ブラウザ、業務アプリに読み込まれるアドインを比較する |
| クラッシュダンプ取得設定 | 再現端末でユーザーモードダンプを取得できるようにしておく |
| ベンダー連携 | 「Microsoft DLL で落ちている」ではなく、ダンプと読み込み DLL 一覧を添えて問い合わせる |
管理者が最初に行うべきことは、OS の再インストールや一律の設定変更ではありません。まず、同じ現象がどの端末群で起きているかを絞り込み、クラッシュダンプと環境差分を集めることです。
まとめ:今回確認すべき次のアクション
Microsoft の「The case of the DLL that was not present in memory despite not being formally unloaded, part 1」は、機能追加や移行期限を知らせる更新ではなく、Windows アプリのクラッシュ解析で重要な考え方を示した公式技術記事です。
今回のポイントは、次の3つです。
- shell32.dll のような Microsoft DLL がクラッシュスタックに出ても、根本原因とは限らない
- loader の管理情報では DLL が残っていても、実メモリが不正に解放されている場合がある
- 複数のクラッシュ分類が、1つのメモリ破損や強制アンロードに由来することがある
管理者は、設定変更や移行期限を探すのではなく、同種クラッシュが自社環境で起きていないか、クラッシュダンプ、読み込み DLL、常駐ソフト、アドイン、発生端末の共通点を確認してください。開発者は、VirtualFree、FreeLibrary、DllMain、終了処理、未初期化ポインターを優先的に点検するのが現実的な対応です。

コメント