12章 DLL入門(その2)

<html>

<head>
<meta http-equiv="Content-Type" content="text/html; charset=shift_jis"></meta>
<title>12章 DLL入門(その2)</title>
</head>

<body bgcolor="WHITE">
<p>
<font size="5">12章 DLL入門(その2)</font>
<hr>
<p>
 前回は、DLLの作り方の初歩と、暗黙のリンクによるDLLの使い方を学びました。今回は、前回説明できなかったDLLエントリーポイントとDLLの明示的なリンクについて学びます。
<hr>
<p>
<b>・DLLエントリーポイント</b>
<p>
 <b><i><u>DLLエントリーポイントとは、DLLの初期化・終了処理を行う関数の事</u></i></b>です。実際には、<b><i><u>4つの事象が生じた場合に呼び出されます</u></i></b>。つまり、DLLがカレントプロセスのアドレス空間に接続されたとき、DLLがカレントプロセスのアドレス空間から切り離されたとき、カレントプロセスが新たにスレッドを作ったとき、そして、スレッドが正常に終了したときです。
<p>
 マニュアルによると、DLLエントリーポイントは以下のような形をしています。
<p align="CENTER">
<table border="1" bgcolor="#F0F0F0">
<tr><td>
<pre>
BOOL WINAPI DllEntryPoint( HINSTANCE hinstDLL, // handle to DLL module
                         DWORD fdwReason,     // reason for calling function
                          LPVOID lpvReserved   // reserved
                        ); 
                                             《MSDNライブラリーより抜粋》
</pre>
</table>
<p>
 さて、DLLエントリーポイントの名前ですが、普通は<b><i><u>DllMain</u></i></b>を用います。リファレンスマニュアルには、DllMainと書いていないのは、リンカオプション<b><i><u>/ENTRY</u></i></b>で変更できるからです。デフォルトでは、Cのランタイムライブラリである<b><i><u>_DllMainCRTStartup関数</u></i></b>がOSから呼ばれます。そして、この関数が、Cランタイムライブラリを初期化して、DllMain関数を呼び出すのです。それゆえ、<b><i><u>普通はDLLエントリーポイントの関数名はDllMainです</u></i></b>。
<p>
 さて、以下にその仮引数と戻り値について説明します。
<hr>
<p>
<b>・HINSTANCE hinstDLL</b>
<p>
 DLLのインスタンスハンドルです。
<p>
<b>・DWORD fdwReason</b>
<p>
 DLLエントリーポイントが呼ばれた理由が格納される仮引数です。この仮引数の取り得る値は、以下の通りです。

<p align="CENTER">
<table border="1" bgcolor="#F0F0F0">
<tr><td>
DLL_PROCESS_ATTACH<br>
DLL_PROCESS_DETACH<br>
DLL_THREAD_ATTACH<br>
DLL_THREAD_DETACH
</table>

<p>
 これら4つの定数は、WINNT.Hで定義されています。
<p>
 fdwReasonがDLL_PROCESS_ATTACHのとき、DLLがカレントプロセスのアドレス空間に接続された事を意味します。同様に、fdwReasonがDLL_PROCESS_DETACHのとき、DLLがカレントプロセスのアドレス空間から切り離された事を意味します。残りの2つも同様ですが、この講座では、まだスレッドに関して詳しい説明をしていませんので、保留にしておきます。
<p>
<b>・LPVOID lpvReserved</b>
<p>
 将来のための予約です。
<p>
<b>・戻り値</b>
<p>
 戻り値は、<b><i><u>仮引数fdwReasonがDLL_PROCESS_ATTACHとしてDLLエントリーポイントが呼ばれた時のみ有効です</u></i></b>。他の場合は、呼び出し側から無視されます。
<p>
 さて、その戻り値ですが、DLLの初期化に成功した場合はTRUEを、初期化に失敗した場合はFALSEを返します。
<hr>
<p>
<b>・Dllエントリーポイントの一般的な構造</b>
<p>
 それゆえ、DLLエントリーポイント(DllMain関数)は以下のような構造になります。
<p align="CENTER">
<table border="1" bgcolor="#F0F0F0">
<tr><td>
<pre>
BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)
{
  switch(fdwReason)
   {
       case    DLL_PROCESS_ATTACH:
           /*  ほげほげ    */
           break;

      case    DLL_PROCESS_DETACH:
           /*  ほげほげ    */
           break;

      case    DLL_THREAD_ATTACH:
           /*  ほげほげ    */
           break;

      case    DLL_THREAD_DETACH:
           /*  ほげほげ    */
           break;
   }
   return  TRUE;
}
</pre>
</table>
<hr>
<p>
<b>・なぜ、DLLエントリーポイントは省略出来たのか?</b>
<p>
 11章の例では、DLLエントリーポイントを省略しました。11章の例のように簡単なDLLでなくてもDLLエントリーポイントを特に必要としない場合はよくあるものです。しかし、上記の説明でお分かりのように、必ず、DllMain関数は呼ばれます。では、なぜDllMain関数を省略できたのでしょうか。これは、リンカがDLLを作成するとき、オブジェクトファイルにDllMain関数が無い場合、自動的に、Cランタイムライブラリーから必ずTRUEを返すDllMain関数をリンクするからです。
<hr>
<p>
<b>・DLLの明示的なリンク</b>
<p>
 DLLの明示的なリンクというのは、インポートライブラリを用いないリンクです。実際に、11章で作成したDLL(C11Dll.dll)を明示的にリンクして使ってみましょう。リスト12-1のソースと実行ファイルをダウンロードするには、<a href="c12main.lzh">ここ</a>を押してください。
<p>
<table border="1" bgcolor="#F0F0F0">
<caption align="BOTTOM">リスト12-1
<tr><td>
<pre>
/*
  C言語で始めるWindowsプログラミング
   12章 DLLの明示的なリンクのサンプルプログラム
                            Programmed by Y.Kondo

/


#define STRICT
#include &lt;windows.h&gt;
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;

/* C11Dll.DLLにあるMyFunctionの型を定義する */
typedef int (*TFUNC)(int);

int main(void)
{
  HINSTANCE   hInstDLL;       /*  DLLのインスタンスハンドル                */
   TFUNC       DllFunction;    /*  DLLのMyFunctionへのポインタ    */
   
   /*  ここでDLLをロードする    */
   if((hInstDLL=<b>LoadLibrary("C11DLL.DLL")</b>)==NULL)
       abort();

  /*  ここでMyFunction()のポインタ値を取得する    */
   DllFunction=<b>(TFUNC)GetProcAddress(hInstDLL,"MyFunction")</b>;

  /*  実際に使ってみる    */
   printf("SUCCESS=%d\n",DllFunction(123456));

  /*  関数へのポインタは使い方が2つありますね    */
   printf("SUCCESS=%d\n",(*DllFunction)(123456));

  /*  ここでDLLを開放する  */
   if(!<b>FreeLibrary(hInstDLL)</b>)
       abort();

  return  EXIT_SUCCESS;
}
</pre>
</table>
<p>
 ここで重要な関数は3つです。すべて太文字にしておきました。つまり、LoadLibrary関数、GetProcAddress関数、そして、FreeLibrary関数です。では、これらについて簡単にに説明しましょう。
<p>
<b>・LoadLibrary関数</b>
<p>
 目的のDLLを呼び出しプロセスのアドレス空間に割り振る関数です。引数は、DLLのファイル名です。そして、DLLを探す順番は、暗黙のリンクと同じです。もし、目的のDLLを発見できなかった、または、目的のDLLを発見できても、そのDllMain関数がFALSEを返した場合、LoadLibrary関数はNULLを返します。それ以外、つまり、目的のDLLが見つかり、そのDllMain関数がTRUEを返した場合、LoadLibrary関数は、アドレス空間に割り振ったDLLのインスタンスハンドルを返します。
<p>
<b>・GetProcAddress関数</b>
<p>
 DLLからエキスポートされている関数のアドレスを取得する関数です。第一引数は、DLLのインスタンスハンドル。第二引数は、目的の関数名です。戻り値は、目的の関数が見つかった場合は、その関数のアドレス。もし、見つからなかった場合は、NULLです。
<p>
<b>・FreeLibrary関数</b>
<p>
 DLLを呼び出しプロセスのアドレス空間から切り離す関数です。引数は、DLLのインスタンスハンドル。戻り値は、成功した場合は、0以外の数、失敗した場合は0です。
<hr>
<p>
<b>・明示的なリンクはどのような場合に使うのか?</b>
<p>
 さて、今回紹介した明示的なリンクと、前回紹介した暗黙のリンクでは、どちらが簡単だったでしょうか?当然、前回紹介した暗黙のリンクですね。普通は、DLLを用いるとき、インポートライブラリーとヘッダファイルを介します。明示的なリンクは、あたかもVBからWIN32API関数を使うようなまどろっこしさがあります。
<p>
 では、明示的なリンクは、欠点ばかりなのでしょうか?いいえ、そうではありません。DLLと言っても、関数や変数をエキスポートしている物ばかりではありません。<b><i><u>1つも関数や変数をエキスポートしていない場合、インポートライブラリーは生成されません</u></i></b>。従って、この様なDLLをプロセスのアドレス空間に割り振ろうと思えば、明示的なリンクしか無いのです。では、関数も変数もエキスポートしていないDLLに価値があるのでしょうか。はい、価値はありますよ。たとえば、ビットマップなどのリソースを格納したDLLです。DLLの中にリソースを格納しておくと、DLLを差し替えるだけで、まったく別の概観を持つアプリケーションを作成する事も出来ます。
<p>
 そして、もう一つの例は、、、、。これは、次回のお楽しみとしましょう。
<hr>
<p>
 前回と今回で、DLLの基礎を学びました。
<p>
 次回では、このDLLの知識を用いて再利用の為の子ウインドウを発展させてみましょう。
<p>
 ではお楽しみに。
<p align="RIGHT">
1999年11月01日<br>
<hr>
<p align="RIGHT">
<a href="index.html">目次</a><br>
<a href="chap13.html">次へ</a>
<hr>
<p align="RIGHT">
著作権者:近藤妥

</body>
</html>

  • 最終更新:2018-03-11 04:41:45

このWIKIを編集するにはパスワード入力が必要です

認証パスワード