__declspec(dllimport) を使った関数呼び出しのインポート
次のコード例は、_declspec(dllimport) を使って DLL からアプリケーションに関数呼び出しをインポートする方法を示しています。 func1 は main 関数を含む .exe ファイルとは別の DLL にある関数であるものとします。
__declspec(dllimport) を使用しない場合のコードは、次のようになります。
int main(void)
{
func1();
}
コンパイラは、次のようなコードを生成します。
call func1
リンカーは、この呼び出しを次のように変換します。
call 0x4000000 ; The address of 'func1'.
func1 が別の DLL にある場合は、func1 のアドレスを知る方法がないので、リンカーは func1 を直接解決できません。 16 ビット環境では、リンカーはこのコードのアドレスを .exe ファイル内のリストに追加します。ローダーは実行時にコードの正しいアドレスを追加します。 32 ビット環境および 64 ビット環境では、リンカーはコードのアドレスを通知するサンクを生成します。 32 ビット環境におけるサンクの例を次に示します。
0x40000000: jmp DWORD PTR __imp_func1
ここで、imp_func1 は、.exe ファイルのインポート アドレス テーブル内にある func1 のスロット用アドレスです。 このため、すべてのアドレスがリンカーに認識されます。 ローダーの役割は、読み込み時に .exe ファイルのインポート アドレス テーブルを更新して、すべてが正しく動作する状態を保つことです。
したがって、リンカーは不要なサンクを生成しないので、__declspec(dllimport) を使用することをお勧めします。 サンクを使うとコードが大きくなり (RISC システム上では、複数の命令になることもあります)、キャッシュ パフォーマンスが低下する場合があります。 関数が DLL 内にあることをコンパイラに通知すると、間接呼び出しが自動的に生成されます。
コードは次のようになります。
__declspec(dllimport) void func1(void);
int main(void)
{
func1();
}
次の命令が生成されます。
call DWORD PTR __imp_func1
サンクおよび jmp 命令がないので、コードは小さく高速になります。
DLL 内部で関数を呼び出す場合は、間接呼び出しを使用する必要はありません。 関数のアドレスは、既にわかっています。 間接呼び出しの前に関数のアドレスの読み込みと格納を行うには、時間と領域が必要になるため、直接呼び出しの方が高速でコンパクトです。 __declspec(dllimport) を使用するのは、DLL の外部から DLL 関数を呼び出すときだけです。 DLL のビルド時に、DLL 内部の関数で __declspec(dllimport) を使用しないでください。