Tips of VC++ > DLL > 明示的リンク
前へ戻る次へ進む


このドキュメントにはサンプルプログラムが含まれています。
ワークスペース
ソースファイル(テキスト)


明示的リンク

最初に「DLL概要」「エクスポートとインポートとリンク」を 読むと、すんなりと理解できるでしょう。 また、DLLのリンク方法には、「暗黙的リンク」を お薦めします。理由は単純に記述が楽だから。明示的リンクは面倒です。

名前のとおり、明示的リンクはAPIを呼び出してDLLをロードし、 関数アドレスへのポインタを取得して、関数を呼び出す方法です。 暗黙的リンクは、暗黙のうちに、このへんも処理をやってくれましたよね? まあ、利点もあるわけですが…。そのへんは 「エクスポートとインポートとリンク」を参照。

明示的リンクに使用する手順は以下です。

なんだかんだ言って、使う関数は二つです。まず一個目

HMODULE LoadLibrary(
  LPCTSTR lpFileName   // モジュールのファイル名
);

DLLをロードして、メモリにマップさせ、そのDLLのDllMain関数を呼び出します。 引数にはDLLへのパスを指定します。 絶対パスを記述すればいいですが、その他の場合の検索の手順は以下のとおり。

  1. アプリケーションのロード元ディレクトリ
  2. カレントディレクトリ
  3. Windowsシステムディレクトリ
  4. Windows ディレクトリ
  5. 環境変数PATHに記述されている各ディレクトリ

環境変数PATHってよく分かりません。(ぉぃぉぃ
この関数はDLLではなくEXEファイルもロードできます。 LoadResourceとか使って他の実行可能ファイルのリソースを読み込めます。

ここでは、DLLの関数を呼び出すのが目的なので、 戻り値であるハンドルをGetProcAddressに渡します。 指定したパスにDLLがなかったり、 DllMainがTRUEを返さなかった場合、エラーになります。

FARPROC GetProcAddress(
  HMODULE hModule,    // DLL モジュールのハンドル
  LPCSTR lpProcName   // 関数名
);

DLL内のエクスポート済関数のアドレスを取得します。 戻り値は関数アドレスです。 FARPROCってのはただのポインタなので、呼び出す関数に合わせて typedefを使って新たに細かい関数型を作ったほうがいいです。
パラメータは、LoadLibraryの戻り値であるライブラリへのハンドル。 また、関数名を示す文字列へのポインタです。 これはDLL内部での関数名ではないのがミソです。 エクスポートされている識別名(?)みたいなもので、 VisualC++付属のツールに含まれているDependency Walkerや、 同じく付属されているDUMPBIN.EXEとか、 Windows付属のクイックビューアを使えば見れます。 また、オンラインソフトにもDachoさんという方の [Dependency Viewer]というソフトがあって、 Dependency Walkerとほぼ同じ機能が使えます。すごいなぁ…。→ ここから
これらのソフトで表示される名前をそのまま使います。 また、序数でエクスポートされている場合、 数字をそのままLPCTSTR型にキャストして使ってください。 こっちのほうが楽かもしれませんね。

あとは、ポインタを使って関数を呼び出します。 関数ポインタの使い方さえ知っていれば、何も難しいことはないテクニックです。

サンプルを使って、試しに作ってみます。 DLLの関数を呼び出すだけなので、単一ファイルでいいでしょう。 適当な名前のソースファイルを作って以下のように書きます。 同時に二つのDLLを使ってみました。export1.dllとexport2.dllは、それぞれ、 __declspec(dllexport)を使ってエクスポート モジュール定義ファイル(.DEFファイル)を使ってエクスポート のサンプルに対応しています。

#include <windows.h>


// ユニークな関数型の定義
typedef int (*AVERAGE)(int,int);

// DLLへの相対パス
const char DLLPATH1[]="export1.dll";
const char DLLPATH2[]="export2.dll";

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow )
{
    // DLLをロード
    HMODULE hDll1= ::LoadLibrary(DLLPATH1);
    HMODULE hDll2= ::LoadLibrary(DLLPATH2);

    // 関数アドレス取得
    AVERAGE average1= (AVERAGE)::GetProcAddress(hDll1,"?average@@YAHHH@Z");
    AVETAGE average2= (AVERAGE)::GetProcAddress(hDll2,(LPCTSTR)100);

    // 関数呼び出し
    int result1= average1(8456,562);
    int result2= average2(562,8456);

    // 結果表示ルーチン
    char buf[100];
    ::wsprintf(buf,"result1= %d\r\nresult2= %d",result1,result2);

    ::MessageBox(NULL,buf,"finish",MB_ICONEXCLAMATION);

    return 0;
}

もちろん結果は、いずれとも4509になるはず。

Jul.26, 2002