関数subをDLLにしたい場合,次のようにソースプログラムを作成する.
外部プログラムから呼び出したい関数(この場合sub)の前に
__declspec(dllexport)という おまじないをつけておく(このおまじないがついていない関数はDLL外部からは見えないので安心).
sub.c |
|---|
#include <stdio.h>
__declspec(dllexport) int sub(int arg)
{
printf("sub %d\n", arg);
return arg + 1;
}
|
sub.cをダウンロード |
通常の静的にリンクして利用する関数を作る場合(*.objまたは静的リンク ライブラリ*.libを作ってリンクする場合)との違いは,
DLLの外に公開したい関数の定義の前に__declspec(dllexport)というキーワードがつけることだけになっている.
[さらに*.defファイルを作成してコンパイル時にコンパイラにわたすようにすれば,
__declspec(dllexport)も書かなくてよくなり,
ソースは*.objや静的リンク ライブラリ*.libを作る場合とまったく同じにできる.]
[注:静的リンク ライブラリはマイクロソフト用語では“標準ライブラリ”と呼ばれているようだ.
しかし,ここではDLL(ダイナミック リンク ライブラリ=動的リンク ライブラリ)の対になる語としてわかりやすい静的リンク ライブラリという語を使うことにする.]
コンパイル時にはオプション/LDを指定する.
> cl /LD sub.c
これで,sub.dll,sub.obj,sub.libの3つのファイルができたと思う.
コンパイラのバージョンによってはsub.libはできていないかもしれないが,
絶対必要なファイルはsub.dllだけなので気にしなくてもよい(というか特に必要でなければsub.dll以外は削除してもよい).
[注:このDLLを作るときに副産物として生成されるsub.libのことをインポート ライブラリと呼んでいる.
ファイル名は静的リンク ライブラリ(標準ライブラリ)とまったく同じ*.libだが,内容はまったく違うもので,
後で説明する暗黙リンクによるDLLの使用方法を採用した場合に必要になる.
したがって,インポート ライブラリの作り方については後節にて説明する.]
DLLの使い方には次の2種類の方法がある.
通常の実用的なアプリケーションでは明示的なリンクによる方法を使うことになると思われるが, まずはソースプロうグラムが簡単に書ける暗黙的なリンクによる方法を説明する.
ポイントは次の2点である.
sub)の宣言の前にマジックワード__declspec(dllimport)をつけておくsub.libをリンクする.
[注:これはリンク時にsubのシンボルが解決できないのを ごまかすためのもので,
インポート ライブラリは
静的リンク ライブラリ(標準ライブラリ)の場合と違って関数subの実体をもっていないため,
実際に使われるsubはDLLの中に入っているものになる.]
暗黙リンクでDLLを使用するプログラムは以下のように書けばよい.
main_static.c |
|---|
#include <stdio.h>
__declspec(dllimport) int sub(int);
int main(void)
{
printf("main %d\n", sub(1));
return 0;
}
|
main_static.cをダウンロード |
ここでは一覧性がよいように関数subのプロトタイプ宣言(__declspec(dllimport) int sub(int);の部分)を
メインプログラム内に書いているけど,普通はこのプロトタイプはsub.hというヘッダファイルに書いてあって
メインプログラムはsub.hをインクルードするだけになっているだろうから,
この方法ではメインプログラムはDLLを使う場合でも静的リンクする場合
(*.objや静的リンク ライブラリ*.libを使う場合)でも変更する必要はないということになる.
コンパイル時にはDLL作成時に同時に生成されたインポート ライブラリsub.libも いっしょにわたしておく.
> cl main_static.c sub.lib
これで実行可能ファイルmain_static.exeができあがる.
ちゃんとDLL内のsubが使われていることを確かめたい場合は,sub.dllを削除してから実行してみればよい.
ダイアログボックスが現れてsub.dllが見つからないというエラーメッセージを表示するはずだ.
[注:もしsub.dllを削除してからmain_static.exeを実行してもエラーにならずに正しく実行できてしまったという場合は
sub.libがインポート ライブラリではなく静的リンク ライブラリ(標準ライブラリ)になってしまっていると思われる.
“付録A. インポート ライブラリの作り方”を参照してインポート ライブラリを作り直してから再度main_static.cをコンパイルしなおしてほしい.]
明示的リンクでDLLを利用するプログラムは,
LoadLibrary,GetProcAddress,FreeLibrary
の3手からなる.
main_dynamic.c |
|---|
#include <stdio.h>
#include <windows.h>
int main(void)
{
HINSTANCE hDLL;
int (*func)(int);
if ((hDLL = LoadLibrary("sub.dll")) == NULL)
printf("LoadLibrary is failed.\n");
else {
if ((func = (int (*)(int))GetProcAddress(hDLL, "sub")) == NULL)
printf("GetProcAddress is failed.\n");
else
printf("main %d\n", func(1));
FreeLibrary(hDLL);
}
return 0;
}
|
main_dynamic.cをダウンロード |
コンパイル方法は特に工夫はいらない.普通にコンパイルすればよい.
> cl main_dynamic.c
暗黙的リンクによる方法の利点は簡単・簡潔なところだが, 実際のアプリケーションでは諸般の事情から明示的リンクを使うことが多い. 諸般の事情とは だいたい以下のような感じのものだ.
sub.lib)が手に入らない.abc5.dllというDLLを使う場合,
このDLLが去年まではabc4.dllという名前だったというようなことはありうる.
もちろん自分のユーザにはabc5.dllにアップデートするよう強制するという方法もないではないけども,
普通に考えれば(DLLがアップデートされた理由が自分が利用している関数でないなら)古いバージョンのabc4.dllが
インストールしてあればOKとする方が親切だ.
フリーウェアなんかだと せっかくダウンロードしてもらっても,abc5.dllにアップデートしろみたいなメッセージが出ただけで
もう めんどくさくなってアンインストールを選択するかもしれない.
ほかにも いろいろな理由があると思うのだけども,これらを見ただけでも, 不特定多数の人が使う実用的なアプリケーションの場合は普通 明示的リンクを選択するしかないのかな,と理解してもらえると思う.
(※この節は)
少し古いバージョンのVisual C++を利用している場合,
cl /LD sub.cとするだけではインポート ライブラリsub.libができないかもしれない
(sub.dllとsub.objの2つだけが生成される).
その場合,次のコマンドを試してみてほしい.
[注:現時点の最新版であるVisual C++ 2005(Visual Studio 2005)を使っている場合は“1. DLLの作り方”に示している方法で生成されるため,このオプションは不要.
いつごろから不要になったのかは定かではない.]
> cl /LD sub.c /link /IMPLIB:sub.lib
/link /IMPLIB:sub.libはリンカにインポート ライブラリを作成するように指示するオプションである.
リンカ オプションなので,sub.objを使ってlink /IMPLIB:sub.lib sub.objとしてもよいが,
紛らわしいので,上記のようにDLLと一緒に作ってしまうほうがいいかもしれない(そうでもないか…).
なお,インポート ライブラリは静的リンク ライブラリ(標準ライブラリ)とまったく同じ名前になるので, うっかり静的リンク ライブラリを作ってしまって それをリンクしてテスト&配布してしまうというミスをおかさないように十分に注意しないといけない. 必ずDLLを削除してみてDLLが見つからないというエラーになるかという確認を行うようにしよう.