関数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が見つからないというエラーになるかという確認を行うようにしよう.