11章 DLL入門(その1)


 今回のテーマは、DLL(ダイナミック・リンク・ライブラリ)です。DLLもライブラリの一種ですから、DOSなどで慣れ親しんだライブラリのように、共通して使用されるコード(関数)や変数を入れて使用します

 ただ、従来のライブラリーは開発時にリンクされ、1枚岩の様にEXEファイルにまとめられますが、DLLは、実行時にリンクされ、1つのプロセスという形でリンクされます。つまり、静的なファイルとしては、リンクされないのです。

 この為、複数のアプリケーションで使用する関数があった場合、これをDLLとしてしまう事により、ディスクスペースの節約が出来ます。この上、実行時のメモリスペースも節約出来ます。さて、前者は理解しやすいと思いますが、後者は、WIN32のメモリ管理を理解していないと理解に苦しむと思います。

 WIN32では、個々のアプリケーションは独立したメモリ空間(アドレス空間)をOSから与えられ、このメモリ空間で動作します。個々のアプリケーションのインスタンスをプロセスというのですが、プロセスが2つあっても物理メモリは2倍必要としないという事を理解できれば、理解は早いです。

 つまり、今、A.EXEという実行ファイルを2回起動したとしましょう。A.EXEという実行ファイルは、リードオンリーなコード部分と実行中に書き換えられるデータ部分から構成されています。WIN32のメモリ管理機構は、2回目の起動のときに、A.EXEのコード部分を再利用します。つまり、すでにメモリ上に存在するコードを新たなプロセスの仮想アドレス空間に割り振る(マッピングする)のです。従って、A.EXEが2回起動された場合、単純計算では、必要な物理メモリ量は、A.EXEのコード領域サイズ+A.EXEのデータ領域サイズ×2となります。つまり、2回目以降はコード領域のサイズ分、メモリを消費しないのです(そのアルゴリズムはWIN32を実装しているOSにより異なります)。

 さて、B.EXEとC.EXEという二つの実行ファイルをそれぞれ1回づつ起動した場合を考えてみましょう。当然、今度は、2つ分のプロセスの物理メモリが必要です。しかし、もし、B.EXEとC.EXEがある関数を従来の一枚岩にする静的なライブラリーで共有していた場合、どうでしょうか。これは、ありがちなパターンですね。この場合も、2つの実行ファイルに共通のコード領域がありながら、OSは別物と判断して、それぞれに物理メモリを割り振ります。そこで、DLLの登場です。

 この様に異なるアプリケーションでも共通部分があれば、これをDLLとして持つ事により、OSに共通部分と認識さす事ができ、コード部分の再利用が行われます(言い換えるなら、データ部分の再利用というか共有は工夫無しでは行えない)。これにより、実行時に必要な物理メモリの節約になるのです。

 また、DLLのみを書き換える事(置き換える)により、これを共有する異なるアプリケーションすべてのバージョンアップが可能です。つまり、このライブラリーを共有するアプリケーションを(普通は)開発者しか持たないリンカを用いてリンクしなおす必要が無いのです。

 前置きは、これぐらいにしておいて、プログラミングの実際に入りましょう。


・DLLの作り方(統合環境編)

 では、1つの関数を複数のアプリケーションから共有するためのDLLを作ってみましょう。

 まず、VisualC++バージョン5を立ち上げます。そして、メニュー[ファイル]→[新規作成]を選びます。すると、新規作成のダイアログボックスが現れます。この中のプロジェクトタブを選び、WIN32 Dynamic Link Libraryを選びます。そして、プロジェクト名を入力し、OKボタンを押します。この一連の操作によって、コンパイル&リンク時のオプションが、DLL用に設定されます。この後、必要なCのソースファイルとヘッダファイルをメニュー[プロジェクト]→[プロジェクトへ追加]→[新規作成]で新規作成ダイアログボックスを開け、ファイルタブを選択して、必要なファイルを作成します。

・DLLの作り方(ソースコード編)

 では、一つの関数を外部に公開する最も単純と思われるDLLのソースを提示しましょう。今回もサンプルを用意しましたので、ここを押してダウンロードしてください。

 一般に、DLLのソースは、拡張子がCのC言語のソースファイルと拡張子がHのヘッダファイルからなります。これは、普通の静的なライブラリーと同じですね。

リスト11−1(C11Dll.h)
/*
        「C言語で始めるWindowsプログラミング」11章
        DLLのサンプル
        c11dll.h    programmed by Y.Kondo
*/

#ifdef  _KONDO_MAKE_DLL_
#   define  __PORT  __declspec(dllexport)   /*  DLLを作る場合   */
#else
#   define  __PORT  __declspec(dllimport)   /*  DLLを使う場合   */
#endif

__PORT  int MyFunction(int);

リスト11−2(DllMain.c)
/*
        「C言語で始めるWindowsプログラミング」11章
        DLLのサンプル
        DllMain.c   programmed by Y.Kondo
*/

/*  _KONDO_MAKE_DLL_を定義する事により、"c11dll.h"がDLL用に動作する */
#define     _KONDO_MAKE_DLL_
#include    "c11dll.h"

/*  
    外部に公開され共有される関数MyFunction
    その機能は、与えられた整数を100で割った余りを返す。
*/
__PORT int  MyFunction(int x)
{
    return  x%100;
}

 まず、DLLを作る場合も、基本的には、従来の静的なライブラリを作るのと同じです。ただ、ヘッダファイルに目新しいトークンが登場していますので、説明します。

 リスト11ー1で示されているヘッダファイルは、DLLを作るときと使うときで共用するためにプリプロセッサディレクティブを用いているので、ややこしく見えます。しかし、DLLを作るときには、その関数の前で、__declspec(dllexport)と宣言するとよい事が分かります。__declspec(dllexport)というのは、マイクロソフト固有の予約語で、ANSI−Cにはありません。知らなくても普通です。

 さて、この様に、__declspec(dllexport)と宣言された関数のみ、DLLの外部に公開されます。昔は、モジュール定義ファイルでEXPORTS文を用いて、外部に公開する関数名を並べていましたが、今は、この方式は推薦されていません。

・DLLの作り方(ビルド後の産物編)

 ビルドすると色々なファイルが出来て来ますが、重要なのは2つです。つまり、今回の場合、C11Dll.dllとC11Dll.Libです。DLLを作る事が目的ですから、拡張子dllのファイルが出来てくるのは分かります。しかし、拡張子libのファイルが出来てくるのは、ふに落ちないですね。この拡張子libのファイルの事をインポートライブラリと言い、このDLLを用いる時に使います。このインポートライブラリーには、DLLを用いるための情報が格納されています。


・DLLの使い方(統合環境編)

 出来るだけ簡単にするために、コンソールアプリケーションで試してみましょう。

 まず、VisualC++バージョン5を立ち上げます。そして、メニュー[ファイル]→[新規作成]を選びます。すると、新規作成のダイアログボックスが現れます。この中のプロジェクトタブを選び、WIN32 Console Applicationを選びます。そして、プロジェクト名を入力し、OKボタンを押します。この一連の操作によって、コンパイル&リンク時のオプションが、WIN32コンソールアプリケーション用に設定されます。この後、必要なCのソースファイルとヘッダファイルをメニュー[プロジェクト]→[プロジェクトへ追加]→[新規作成]で新規作成ダイアログボックスを開け、ファイルタブを選択して、必要なファイルを作成します。

 さて、DLLを使う場合、暗黙的なリンク明示的なリンクの二通りあります。今回は、一般的な暗黙的なリンクを用います。この場合、従来の静的なライブラリをリンクする方法と大差ありません。ただ、リンクするのが、インポートライブラリである事だけです。しかし、インポートライブラリの拡張子がlibゆえ、見かけ上、全く違いが無いとも言えます。このインポートライブラリーは、ワークスペースの中に取り込むか、プロジェクト設定でリンクするライブラリーに加えるだけです。今回、筆者は前者の方法を採りました。後で出てくるサンプルを参考にしてください。

・DLLの使い方(ソースコード編)

では、DLLを使うソースコードの例を提示しましょう。サンプルを用意しましたので、ここを押してダウンロードしてください。

リスト11−3(main.c)
#include    <stdio.h>
#include    "c11dll.h"

int main(void)
{
    printf("%d\n",MyFunction(12345));
    return  0;
}

 結局、ソースコードレベルでは、従来の静的なライブラリとあまり変りませんね。インクルードされているc11dll.hは、先ほどのリスト11−1の物です。ただ、この場合は、__declspec(dllimport)が有効になっています。これは、次に示された関数が、外部のDLLで実装されている事をコンパイラに伝えるものです。

・DLLの使い方(DLLの置き場所編)

 さて、DLLはどこにあるものとしてロードされるのでしょうか?HDDのどこにでも転がしておけば良いってものではありません。厳格な決まりがあります。

 Windowsは以下の順序で目的のDLLを探します。

1.アプリケーションがロードされたディレクトリ
2.カレントディレクトリ
3.Windowsのシステムディレクトリ
  (GetSystemDirectory関数で得られるディレクトリ)
4.Windowsディレクトリ
  (GetWindowsDirectory関数で得られるディレクトリ)
5.環境変数PATHで列挙されたディレクトリ

 今回のサンプルは、アプリケーションのディレクトリにDLLをコピーしておきました。コマンドラインからすぐに実行できるはずです。


 予定では、1回でDLLの基礎について学ぶはずでした。しかし、最低限の事を説明しようと思っても、2回に分けないと説明しにくいので2回に分ける事にしました。

 次回は、DLLのエントリーポイントと明示的なリンクについて学びます。

 ではお楽しみに。

1999年10月23日
加筆修正1999年10月29日


目次
次へ


著作権者:近藤妥