5

読み込みに 5 秒から 10 秒かかる DLL があります。つまり、それを使用する実行可能ファイルをコンパイルして実行するたびに、それだけ長く待たなければなりません。対応する実行可能ファイルをコンパイルするたびにすぐにアクセスできるように、DLL をメモリにロードしたままにする方法はありますか? 関連する場合は、QT MinGW でプログラムをコンパイルしています。

編集:これまでのところ運がありません。DLL を別のプログラムにロードしても効果がないようです (元のプログラムは依然として DLL をロードし、そうするのに同じくらい時間がかかります)。DLL とその関数が別のプログラムに読み込まれている場合は、別の方法で読み込む必要があると思いますが、これを行う方法がわかりません。現在、LoadLibrary と GetProcAddress を使用しています。

4

4 に答える 4

2

私は MinGW の開発者ではありませんが、あなたの質問は非常に一般的であり、DLL の作成方法にはあまり依存していません。このような問題は、通常、次の 3 つの手法を使用して解決できます。

  • DLL の一意のベース アドレスの選択
  • DLL と exe のバインド (アプリケーションのインストール中)
  • DLL 遅延読み込み手法の使用
  • 一部のDisableThreadLibraryCallsの呼び出しの読み込み時間を少し改善しますDLL_PROCESS_ATTACHDllMain

使用できるリンカーまたはその他のツールの正確なスイッチは、開発環境によって異なります。

問題を理解するには、実行可能ファイルまたは DLL がどのように読み込まれるかを知っておく必要があります。まず、EXE または DLL がメモリにマップされます。EXE/DLL を指すメモリ マップド ファイル(セクション) が作成されます。そのため、アクセスが EXE/DLL ファイルに対応するプロセスで、いくつかのアドレスが得られます。DLL をリンクすると、ベース アドレスを選択できます。アドレスがプロセスのアドレス空間で使用されていない場合、何も行われません。コードの最初の行が使用される場合 (DLL から何らかの関数を呼び出す場合)、使用されたアドレスに近い 8K のメモリのページがファイルからメモリにロードされます。2 つのプロセスが同じ DLL を使用する場合、コードの物理メモリは共有されます。プロセス間。初期化された変数を保持していても、変数を含むページは変数の最初の変更まで共有されます。変更すると、変更を行ったプロセスのメモリのページのコピーが作成されます。

プロセスで DLL がロードされた後、DLL から使用される関数の実際のアドレスを含めるために、呼び出し元 (EXE など) のいくつかの小さな部分を変更する必要があります。別の DLL の関数を使用する DLL についても同じことが行われます。

すべてが完璧に聞こえますが、DLL のコンパイル中にリンカー オプションを設定しない場合 (--image-baseまたは--enable-auto-image-baseリンカー オプションを使用しない場合)、すべての DLL が同じベース アドレス (リンカーの既定値) を持つことになります。したがって、最初の DLL をそのアドレスにロードできます。同じ (または重複したアドレス) にリンクされている 2 番目の DLL のロード中に、DLL の再配置が行われます。再配置中に DLL のコードが変更されるため、1) DLL のロードが遅くなります 2) プロセスでコードの変更されたコピーが作成されます (これには DLL によって使用されるメモリが含まれます) 3) 変更されたコピーDLL の複数のインスタンス間で共有されることはありません (すべてのインスタンスが同じ方法で変更される場合でも)。

まず、たとえばProcess Explorerを使用して、アプリケーションで再配置される DLL を確認することをお勧めします。「View」/「Lower Pain View」メニューでオプション「DLLs」を選択し、「Options」メニューの「Configure Highlighting」で「Relocation DLLs」チェックボックスを選択する必要があります。さらに、すべての DLL に関するどの情報を表示するかをカスタマイズできます。以下のような情報が多いほど、プログラムのロードが遅くなり、アプリケーションのインスタンス間または同じ DLL を使用する異なるアプリケーション間でアドレス空間が共有されなくなります。

ここに画像の説明を入力

上記の例では、ツリー Lenovo DLLTPOSDSVC.dllとが同じベース アドレスにリンクされており、HKVOLKEY.dll1つの DLL (ここでは) のみがそのアドレスにロードされます。他の 2 つの DLL を再配置する必要があります。TPLHMM.dll0x10000000TPOSDSVC.dll

このテーマについてここで本を書くことはできません。移転の問題についてアプリケーションを検討することをお勧めします。リンカーオプションを使用できます(--image-baseまたは--enable-auto-image-base必要なものと思われます)。dumpbin.exeツール (無料版の Visual Studio から) を使用して、PE イメージを調べることができます。

すべての DLL が一意のベース アドレスを持つようになったら、別のツールbind.exeをオプション付きで使用-uして、EXE と DLL を依存する DLL にバインドできます。さらに、メモリ サイズが削減され、アプリケーションの起動時間が改善されます。DLL と EXE の一部が更新されIMAGE_DIRECTORY_ENTRY_IMPORTます(回答を参照してください)。内部で APIを使用します。多くの Windows インストーラー セットアップでは、BindImageアクションとBindImageテーブルを使用して、EXE と DLL のインストールの最後にバインディングを作成します。IMAGE_DIRECTORY_ENTRY_DELAY_IMPORTBind.exeBindImageEx

DLL と EXE のサイズを小さくするために、他の手法 (ここを参照) を検討できます。

MinGW で遅延ロード手法をどのように使用できるかは正確にはわかりませんが、確実に可能であるはずです。Visual Studio では、2 つの手順を実行する必要があります。Delayimp.lib追加のライブラリとしてインクルードし、/DELAYLOADオプション (こちらを参照) を使用して、直接ではなく最初の使用時にどの DLL をロードするかを指定します。非常に役立つ Tool Dependency Walkerを使用すると、ほとんどの標準 Microsoft DLL がこの手法を使用していることがわかります。この手法も使用すると、アプリケーションの起動時間を改善し、使用メモリを減らすことができます。

于 2012-06-05T11:06:39.287 に答える
1

DLLのロードを維持する明示的にインストールされたシステムサービスを作成します。そうすれば、初期化は起動時に行われ、二度と行われません。私はこれらの他の答えのほとんどで概説されている方法に反対することをお勧めします。彼らは彼らが働いているように見えますが、彼らはあなたのソフトウェアの側の悪い振る舞いのように私には感じます。ユーザーとメンテナの両方の観点から、winlogin.exeに何かをフックするよりも、明示的にインストールされたシステムサービスを使用したいと思います。Windows APIと環境を正直に使用すればするほど、バージョン間およびバージョンアップグレード時の重大な変更が少なくなります。

于 2012-06-04T16:12:21.790 に答える
1

私が間違っていなければ、Windows は DLL の 1 つのインスタンスをメモリに保持するので、これを有効にしておくとうまくいくはずです。

#include <conio.h>
#include <windows.h>

int main()
    {
    HMODULE handle=LoadLibrary("yourdll.dll");
//  Make shure Windows resolves the DLL
    FARPROC dummy=GetProcAddress(handle,"functionInDll");
//  The process will now just wait for keyboard input.
    getch();
    CloseHandle(handle);
    return 0;
    }
于 2012-06-04T16:14:17.477 に答える
1

最も簡単な解決策は、(MSVC++ を想定して) DLL を遅延ロードすることです。もちろん、トレードオフは、初期化を行わなければならないということですが、これによってプログラムの他の部分が遅延することはなくなります。たとえば、バックグラウンド スレッドで実行できます。

于 2012-06-05T07:58:09.490 に答える