ウインドウズDLL

2000.09.28 作成
2000.12.05 修正

例題をダウンロードする

DLL の作成

まず以下のソースを準備する。
----[testdll.cpp]----
 
#include <windows.h>

extern "C"{
 int  WINAPI  _declspec(dllexport)  TestFunc2(int arg1,int arg2);
 };

int   _declspec(dllexport) TestFunc1(int arg1,int arg2)
{
  return (arg1-arg2);
  }

int  WINAPI  _declspec(dllexport) TestFunc2(int arg1,int arg2)
{
  return (arg1-arg2);
  }


BOOL WINAPI DllEntryPoint(HINSTANCE hInstance, DWORD fdwReason, LPVOID*lpvReserved){
{
  BOOL flag;

  switch (fdwReason){
    case DLL_PROCESS_ATTACH: //ロードされるときの処理
      return TRUE;

    case DLL_PROCESS_DETACH: //アンロードされるときの処理
      return TRUE;
    case DLL_THREAD_ATTACH:
      break;
    case DLL_THREAD_DETACH:
      break;
    }
  return TRUE;
  }

DLL の初期化と終了処理を記述したい場合には、上記のような DllEntryPoint を用意する。 DllEntryPoint はプロセスによりロード、アンロードされるときと、スレッドが起動、 終了するときに呼ばれる。 どの条件で呼ばれたかは、fdwReason で判別する。
最初にロードされるとき(DLL_PROCESS_ATTACH)、必要ならば前処理を行う。 このとき FALSE を返すことによって、DLL のロードを失敗させることができる。 最後にアンロードされるとき(DLL_PROCESS_DETACH)、必要ならば、後処理を行う。

次に関数を用意する。今回は2つの引数の差を返す関数 TestFunc1,TestFunc2 を用意する。 外部に公開する関数には _declspec(dllexport)を付ける。 また C++ の場合、実際につけられる関数名はユーザーが付けた名前を加工して作られる。 名前を加工して欲しくない場合は、extern "C" を宣言する。 また WINAPI を付けるのは、 外部公開名にアンダースコアが付かないようにするためである。

実際に DLL を作成するには DOS窓から以下のように打ち込む。

BCC32 -WD testdll.cpp

エラーがなければ、 testdll.dll が作成される。
外部公開名を確認するには、TDUMPを使う。 TDUMP testdll.dll と入力して出力結果をみると TestFunc1"TestFunc1(int, int)"として、 TestFunc2 はそのまま公開されているのが確認できる。


アプリケーションの作成と実行

次に DLL を呼び出すアプリケーションの作成である。まず、以下のように入力する。

IMPLIB testdll testdll.dll

ライブラリーファイル testdll.lib が作成される。 さらに以下のように入力する。

MAKE /f makeapp

test3.exe が作成されるので、起動してメニューから Test1 を選択する。 ダイアログが表示されるので、数値を入力して引き算を行う。 Call1 では TestFunc1 が、 Call2 では TestFunc2 が呼び出される。

TestFunc1 の呼び出しは静的に結合を行う方法で、ライブラリーを使用する。この方法は一般的な API を呼び出す場合と同じである。
TestFunc2 の呼び出しは動的に結合を行う方法で、DLL を自分でロードして、 関数のアドレスを取得して呼び出す。
ライブラリーは使用しない。この方法は静的な方法に比べると面倒臭いが、 DLL や関数が見つからない場合でもアプリケーションで対処可能という利点がある。
以下はTestFunc2 を呼び出している部分である。

----[test3.cpp の一部]----
 

           case IDC_CALL2:
              hdll=LoadLibrary("TestDll.DLL");
              if(!hdll){
                SetWindowText(GetDlgItem(hDlg,IDC_LABEL2),"DLL が見つかりません");
                return TRUE;}
                
              fproc=GetProcAddress(hdll,"TestFunc2");
              if(!fproc){
                SetWindowText(GetDlgItem(hDlg,IDC_LABEL2),"関数が見つかりません");
                FreeLibrary(hdll);
                return TRUE;}

             
              GetWindowText(GetDlgItem(hDlg,IDC_EDIT1),buf,80);
              n1=atoi(buf);
              GetWindowText(GetDlgItem(hDlg,IDC_EDIT2),buf,80);
              n2=atoi(buf);
              n3=(*(int (*)(int,int))fproc)(n1,n2);
 
              sprintf(buf,"%d",n3);
              SetWindowText(GetDlgItem(hDlg,IDC_LABEL),buf);
              FreeLibrary(hdll);
              return (TRUE);
 


リソースの呼び出し

 DLL にはプログラムだけではなく、リソースも含めることができる。一旦 testdll.dll を削除してから、以下のように入力する。

MAKE /f makedll

今度は testdll.rc の文字列リソースを結合した DLL を作成される。 test3.exe を起動してメニューから Test2 を選択する。 ダイアログが表示されるので、ID を入力して Test ボタンを押すと、対応する 文字列が表示される。ID は 1,2,10 が有効である。  なお makefile を使う場合は、 リンカーのオプションに -Tpd を指定しないと DllEntryPoint が呼ばれないので注意すること。

修正履歴

初期化のルーチンが LibMain になっていたのを DllEntryPoint に修正。 Visual C と同じだと勘違いしていた。

外部公開名にアンダースコアが付かないようにするために、 以前はコンパイラオプションで -u- を指定していたが、 これだと C のライブラリ関数の呼び出しができなくなる副作用があるので、 WINAPI を付ける方法に変更した。

2000.12.05 修正
次へ 前へ 目次へ