__declspec(dllimport)
を使った関数呼び出しのインポート
__declspec(dllimport)
を使用して呼び出しに注釈を付けると、処理が速くなります。 __declspec(dllimport)
は、エクスポートされた DLL データにアクセスするために常に必要です。
DLL から関数をインポートする
次のコード例は、__declspec(dllimport)
を使用して、DLL からアプリケーションに関数呼び出しをインポートする方法を示しています。 func1
は、main 関数を含む実行可能ファイルとは別の DLL 内にある関数であると仮定します。
__declspec(dllimport)
がない次のコードを考えてみます。
int main(void)
{
func1();
}
コンパイラは次のようなコードを生成します。
call func1
この呼び出しは、リンカーによって次のように変換されます。
call 0x4000000 ; The address of 'func1'.
別の DLL に func1
が存在する場合、リンカーは、func1
のアドレスを知る方法がないため、このアドレスを直接解決することはできません。 32 ビット環境と 64 ビット環境では、リンカーは既知のアドレスでサンクを生成します。 32 ビット環境では、サンクは次のようになります。
0x40000000: jmp DWORD PTR __imp_func1
ここで __imp_func1
は、実行可能ファイルのインポート アドレス テーブルの func1
スロットのアドレスです。 これらのアドレスはすべてリンカーに知られています。 ローダーが読み込み時に実行可能ファイルのインポート アドレス テーブルを更新するだけで、すべてが正常に機能します。
そのため、__declspec(dllimport)
を使用することをお勧めします。これは、リンカーが不要な場合にサンクを生成しないためです。 サンクによってコードが大きくなり (RISC システムでは複数の命令になる可能性があります)、キャッシュのパフォーマンスが低下する可能性があります。 関数が DLL 内にあることをコンパイラに通知すると、間接的な呼び出しが生成される可能性があります。
そこで、次のコードを考えてみます。
__declspec(dllimport) void func1(void);
int main(void)
{
func1();
}
これから次の命令が生成されます。
call DWORD PTR __imp_func1
サンクと jmp
命令がないため、コードはサイズが小さくなり、高速になります。 また、プログラム全体の最適化を使用することによって、__declspec(dllimport)
なしで同じ効果を得ることもできます。 詳細については、「/GL (プログラム全体の最適化)」を参照してください。
DLL 内の関数呼び出しでは、間接的な呼び出しを使用する必要はありません。 リンカーは既に関数のアドレスを把握しています。 間接的な呼び出しの前に、関数のアドレスを読み込んで保存するための追加の時間と領域が必要になります。 直接呼び出しは、常により高速でより小さくなります。 DLL 自体の外部から DLL 関数を呼び出す場合にのみ、__declspec(dllimport)
を使用する必要があります。 DLL のビルド時に DLL 内の関数で __declspec(dllimport)
を使用しないでください。