3

MSDN は次のように述べています。

LoadLibrary または LoadLibraryEx 関数 (またはこれらの関数を呼び出す関数) を呼び出してはなりません。これは、DLL の読み込み順序で依存ループが作成される可能性があるためです。これにより、システムが初期化コードを実行する前に DLL が使用される可能性があります。

DllMainから呼び出そうとしましたがLoadLibrary、何も起こりませんでした。

唯一の問題は、読み込まれた DLL が、残りの DllMain が実行される前に DLL 内の関数を使用することです。

DllMain で LoadLibrary を呼び出してはいけないのはなぜですか?

編集:

OK、他の信者と同じように MSDN を信じなければならないという理由だけで、DllMain で LoadLibrary を呼び出してはならないことに気付きました(そこで間違ったことをいくつか見ましたが、それらも忘れる必要があります)。
また、新しいバージョンの Windows では何かが起こる可能性があるためです (ただし、過去 10 年間は何も変更されていません)。

LoadLibraryしかし、DllMain で呼び出されたときに何が起こるかを再現するコードを誰かが示すことができますか? 既存の Windows OS では?
別のシングルトン初期化関数の呼び出しだけでなくLoadLibrary、DllMain での呼び出しですか?

4

6 に答える 6

15

DllMain からの LoadLibrary の呼び出しが完全に安全な単純な状況があり、それほど単純ではない状況もあります。ただし、設計上、DllMain は読み込まれたモジュールのリストを変更しないと信頼されています。

ローダー ロックの所有は、DllMain で実行できることを実際に制限しますが、それは LoadLibrary ルールに間接的に関連するだけです。ローダー ロックの関連する目的は、ロードされたモジュールのリストへのアクセスをシリアル化することです。NTDLL は 1 つのスレッドでこのリストを操作しますが、ローダー ロックを所有することで、別のスレッドで実行されている NTDLL コードによってリストが変更されないようにします。ただし、ローダーロックは重要なセクションです。同じスレッドがローダーロックを再取得してリストを変更するのを止めることは何もしません。

これは、リストの処理中に NTDLL が完全にそれ自体を保持している場合は問題になりません。ただし、NTDLL では、新しく読み込まれた DLL を初期化するときなど、この作業に他のコードを含めることができます。リストの処理中に NTDLL が自身の外部を呼び出すたびに、設計を行うための選択肢があります。大きく分けて、2 つのオプションがあります。1 つは、リストを安定させてローダー ロックを解放し、外部を呼び出してからローダー ロックを取得し、リストの作業を最初から再開することです。もう 1 つは、ローダーのロックを維持し、呼び出されたコードがリストを変更するようなことをしないことを信頼することです。したがって、LoadLibrary は DllMain で立ち入り禁止になります。

ローダー ロックが DllMain による LoadLibrary の呼び出しを停止するということではなく、ローダー ロック自体がそのような呼び出しを安全でなくするということでもありません。代わりに、ローダー ロックを保持することにより、NTDLLはDllMain が LoadLibrary を呼び出さないことを信頼します。

対照的に、同期オブジェクトを待機しないという DllMain ルールについて考えてみましょう。ここでは、ローダーロックがこれを危険にする直接的な役割を果たしています。DllMain で同期オブジェクトを待機すると、デッドロックの可能性が設定されます。必要なのは、待機しているオブジェクトを別のスレッドが既に保持していて、この別のスレッドが、ローダー ロックで待機する関数 (たとえば、LoadLibrary だけでなく、一見無害な GetModuleHandle などの関数) を呼び出すことだけです。

DllMain の規則を拡張したり破ったりすることは、いたずらであるか、完全に愚かでさえあるかもしれません。ただし、Microsoft は、これらの規則がどれほど強力で意味があるかを尋ねる人々に対して、少なくとも部分的に責任があることを指摘しなければなりません。結局のところ、いくつかは常に明確かつ強制的に文書化されているわけではなく、最後に調べたとき、それらが確実に必要とされるすべての状況でまだ文書化されていませんでした。(私が念頭に置いている例外は、少なくとも Visual Studio 2005 までは、DLL を作成する MFC プログラマーは初期化コードを CWinApp::InitInstance に配置するように言われていましたが、このコードが DllMain 規則に従うことは知らされていませんでした。)

さらに、DllMain の規則に疑問の余地なく従うべきであるかのように Microsoft の誰もが発言するのは、少し金持ちになるでしょう。Microsoft 自身のプログラマーが規則を破る例が存在し、規則を破った後も引き続き、重大な現実世界の問題を引き起こしていることが確認されています。

于 2010-12-10T07:35:41.630 に答える
15

これを進めることに賛成するあなたの議論は、言い換えると、次のようです。

Microsoft はこれを行うなと言っていますが、私の 1 つのテスト ケースは機能しているように見えます。

あなたは大きな仮定の下で操作しています: Windows ローダーの基本的な実装は決して変わらないと仮定しています。「Windows 8」でローダーが変更され、コードが正しく機能しなくなった場合はどうなりますか? 現在、マイクロソフトはその責任を負っており、最初に書かないようにと言っ たコードを回避するために、さらに別の互換性ハックを含める必要があります。

ガイドラインに従ってください。それらは、あなたの生活をより困難にするためだけに存在するのではなく、あなたのコードが現在と同じように将来の Windows でも正しく機能することを保証するために存在します。

于 2010-12-06T23:12:01.737 に答える
9

http://msdn.microsoft.com/en-us/library/ms682583%28VS.85%29.aspxに記載されているように:

DllMainのスレッドはローダーロックを保持しているため、追加のDLLを動的にロードまたは初期化することはできません。

乾杯

于 2010-12-06T20:55:40.680 に答える
3

私は DllMain で LoadLibrary を使用する必要があるケースに取り組んでいたので、調査中にこの議論が見つかりました。私の今日の経験からのこれに関する更新

これを読むと、本当に恐ろしくなりますhttp://blogs.msdn.com/b/oleglv/archive/2003/10/28/56142.aspx . さまざまなロックが重要なだけでなく、ライブラリがリンカーに渡された順序も重要です。ケースは1バイと言う

今、win7の下でvc9でこれを試しました。はい、そうです。ライブラリがリンカーに渡される順序に応じて、LoadLibrary の使用が機能するかどうかが決まります。ただし、win8 で vc11 を使用した場合も、リンクの順序に関係なく適切に動作します。Application Verifier はそれについて責任を負いません。

私は今、どこでもこのように使用することを求めていません:)しかし、参考までに、win10以降でも同じであれば、これはより便利になるかもしれません。いずれにせよ、win8 でのローダー メカニズムにはいくつかの顕著な変更が加えられたようです。

ありがとう。

于 2015-05-25T00:44:56.920 に答える