4

何百ものシグネチャを typedef に格納してから、GetProcAddress でポインタをロードするよりも、はるかに優れた方法があるように感じます。私の知る限り、DLL 関数のロードに関しては、これが最も簡単ですが、面倒です。

DLL 関数をロードする簡単な方法はありますか? 具体的には、大量の Winapi とツール ヘルプ ライブラリ関数? 「.libを含める」だけでよいことは知っていますが、それは不必要な肥大化を引き起こすと思います。また、ソース コードにアクセスすることもできません (ただし、Jason C は .dll から .lib に移行できると述べています)。

このためのライブラリをコーディングしようとしていました。主な障害は、さまざまなシグネチャを持つ関数を扱うことだと思います。それとも、これがまさに、DLL 関数をロードするために「空想的なループ」ではなく typedef を使用する理由ですか?

4

6 に答える 6

3

関数は何らかの方法で宣言する必要があるため、関数シグネチャが必要です。ただし、関数シグネチャが重複していると思われる場合は、 を使用decltype(&func_name)してその重複を排除できます (func_name同じターゲット シグネチャを持つ関数が与えられた場合)。それをマクロでラップして、より美味しくすることができます。

別の一般的なアプローチは、すべての関数ポインターを 1 つの大きな構造体 (またはタスク固有の構造体の束) に押し込み、代わりに構造体へのポインターをロードすることです (たとえば、構造体へのポインターを返す関数をロードします)。これは次のようになります。

共通ヘッダー

struct func_table {
    void (*f1)(int);
    int (*f2)(void);
};

DLL

static struct func_table table = {
    &f1_Implementation,
    &f2_Implementation,
};

DLLEXPORT struct func_table *getFuncTable(void) {
    return &table;
}

DLL を使用したプログラム

static struct func_table *funcs;

void loadFuncTable() {
    struct func_table (*getFuncTable)(void) = GetProcAddress(..., "getFuncTable");
    funcs = getFuncTable();
}

/* call funcs->f1(n) and funcs->f2() */
于 2013-08-03T04:19:51.777 に答える
2

Visual C++ には、DLL の読み込みを遅らせるオプションがあります。DLL の lib ファイルにリンクするコードは、そのヘッダーを含み、通常どおり関数を呼び出します。Visual C++ リンカとランタイムは、関数が最初に呼び出されたときに LoadLibrary() と GetProcAddress() を呼び出します。

ただし、通常リンクされた DLL のようにアプリケーションがロードに失敗するのではなく、何らかの理由で DLL をロードできない場合に実行時例外が発生します。

この DLL が常にロードされ、アプリケーションで実行する必要がある場合は、通常どおり (.lib ファイルを使用して) DLL を使用する必要があります。その場合、DLL のロードを遅らせても何も得られません。

于 2013-08-03T04:14:34.467 に答える
2

Windows ライブラリをロードする方法はいくつかありますが、それぞれの方法は異なる目的を果たします。

LoadLibrary()and を手動で使用GetProcAddress()することは、いくつかの異なる動的リンクの状況について実行時に決定できるようにすることを目的としています。これらの呼び出しは、結果の PE ファイルのどの側面にもまったく影響を与えない単純なシステム コールであり、によって返されるアドレスはGetProcAddress()、コード生成に関して特別なものではありません。これは、呼び出し元がこれらのシンボルを非常に構文レベルで適切に使用する責任があることを意味します (たとえば、適切な呼び出し規則と、関数呼び出しの場合は引数の正しい数、サイズ、および配置を使用します)。

.libに関連付けられたファイルに対するリンク.dllは異なります。クライアント コードは、静的にリンクされたシンボルであるかのように、外部識別子を使用してランタイム コンテンツを参照します。ビルド プロセス中に、リンカーはこれらの識別子をファイル内のシンボルで解決し.libます。原則として、これらのシンボルは (他のシンボルと同様に) あらゆるものを指すことができますが、自動生成された.libファイルは、ロード時に PE ローダーによって埋められるメモリ コンテンツへの小さなプロキシとして機能するシンボルを提供するだけです。

これらのプロキシを実装する方法は、予想されるメモリ アクセスの種類によって異なります。たとえば、 の関数を参照するシンボルは、クライアント コードからの元の命令がコンテンツからの命令にヒットし、によって解決されたアドレスに転送されるように.dll、単一の間接jmp命令として実装されます。ロード時の PE ローダー。このアドレスには、動的にロードされる関数のコードが存在します。calljmp.lib

詳細はさておき、これはすべて、クライアント コードが単独で行うのと同じジョブを実行するよう PE ローダーに指示するために使用さLoadLibrary()れます。GetProcAddress()必要なすべてのシンボルに。PE ローダーは、クライアント実行可能ファイルのインポート テーブルにある情報から、ロード時にこれを自動的に行います。

言い換えると、読み込み時の動的リンクは、実行時の動的リンクよりも、たとえあったとしてもごくわずかでも遅く、太りますが、特に多くのライブラリが常に必要とされるため、通常の開発者にはるかに単純なプログラミング インターフェイスを提供することを目的としています。いつでも利用できます。このような状況では、手動でロードして追加のロジックを提供しようとしても役に立ちません。

要約すると、パフォーマンスや堅牢性が向上しているように見えるという理由だけで、わざわざ使用LoadLibrary()しないでください。可能な限りファイルGetProcAddress()にリンクします。.lib

トピックをさらに進めると、インポート テーブルをまったく含まない(システム コールやその他のエクスポートされたルーチンにアクセスできる) PE イメージを作成することさえ可能です。このアプローチは、読み込み時間の情報を削除することで疑わしい API の使用 (Winsock 呼び出しなど) を隠すためにマルウェアによって使用されます。

于 2013-08-03T06:35:37.847 に答える
2

.lib ファイルへのリンクの「肥大化」 (「肥大化」とは、実行可能ファイルに数 kB 余分に追加することを意味していると推測していますが、それはご存知のとおりです...) を使用している場合は、「不要」ではありません。何百もの GetProcAddress 呼び出しを処理するのを避けるためです。:)

「空想的なループ」の意味がよくわかりません。このコンテキストで使用されるtypedefは、ヘッダー内の宣言と同様の目的を果たします。これらは、呼び出されている関数のシグネチャに関する情報をコンパイラと人間の読者に提供します。いずれにせよ、これを提供する必要があります。

.dll から .lib を生成するツールがあります。ただし、コンパイラが何をしているのかを知るために、呼び出している関数を宣言するヘッダーが必要です。基本的に、.dll 用に生成された .lib は、DLL をロードして関数アドレスを取得する単なる「スタブ」です。API は異なりますが、本質的には同じです。

これを回避する唯一の方法は、DLL インターフェイスを別の方法で設計して、処理する関数が多くならないようにすることです。ただし、ほとんどの場合、関数の型定義/宣言を回避することが、このようなことを行う十分な動機であるとは言えません。たとえば、次のような関数を 1 つだけ公開できます (たとえば):

void PerformAction (LPCSTR action, DWORD parameter);

そして、PerformAction の実装を「アクション」に応じて異なるものにします。あなたの投稿に関係のない特定の状況では確かに合理的ですが、あなたが説明している「問題」の適切な「回避策」ではありません。

あなたはほとんどそれに対処する必要があります。宣言を含むヘッダーを作成してスタブ .lib を生成するか、typedef を含むヘッダーを作成して LoadLibrary/GetProcAddress を使用します。前者を使用すると、入力の手間が省けます。後者を使用すると、DLL が存在しない場合を処理したり、設計時に不明な DLL をロードしたりできます (たとえば、別の回答で言及されているプラ​​グイン)。

于 2013-08-03T04:07:03.000 に答える
-1

ファイル名を指定して dll を手動でロードする機能が提供されている必要があります。Windows API では LoadLibrary() です。「プラグイン」スタイルのアプリケーションを開発している場合や、ロードとアンロードを手動で制御する必要がある場合を除き、通常は使用されません。

静的ライブラリまたは動的に読み込まれた dll が要件に最適なソリューションであるかどうかを判断するには、コードをどのように使用するかを検討する必要があります。

于 2013-08-03T04:01:06.340 に答える