ライブラリには2種類ある

図6●ダイナミック・リンクとスタティック・リンクの違い

 ここまでは,基本的に実行する前にすべての参照関係を解消するリンクについて解説してきた。これに対し,プログラムの実行時に参照関係を決定するものもある。これを「ダイナミック・リンク」と呼ぶ。この呼び方と対比する場合,通常のリンクを「スタティック・リンク」と呼ぶ(図6[拡大表示])。

 Javaにはリンクとは別にコンパイルの後の作業がある。Javaの場合,ソース・コードをコンパイルすると,個々のクラスごとに「.class」という拡張子のファイルを生成する。ちょっとした規模のプログラムでも,その数は膨大になる。これでは他のユーザに配布するのは困難である。そこで配布できる形にまとめた「.jar」というファイルを作るのが普通だ。しかしこのファイルの場合,リンクの作業,すなわち参照関係の結びつけはしていない。

 Javaでリンクが発生しないのは,実行時にすべての参照関係を確定するからだ。実行時に名前で呼び出してみて,参照する相手がいなければエラーで停止する。その名前で呼び出した相手が適切であれば,処理は継続する注6)。

図7●コモン・ダイアログの表示の違い。
元のプログラムはまったく同じもの。これをWindows XPで動かした場合(右)と,Windows Meで動かした場合(左)ではダイアログ・ボックスのデザインが異なる。動的にリンクしているライブラリが違った結果である

 ところでダイナミック・リンクはJavaの専売特許ではない。WindowsやLinuxにも「ダイナミック・リンク・ライブラリ(DLL)」がある。Windowsで言えば「.dll」,Linuxで言えば「.so」という拡張子のファイルがそれだ。しかしそれでも,「リンク」作業がなくなるわけではない。なぜならば,こういったDLLを呼び出すためのコードをスタティックにリンクするからである。

 ダイナミック・リンク・ライブラリのメリットは,後からその機能の改善をできることである。例えば,ファイルを開くダイアログ・ボックスはよく,OSに標準のものが使われる。このダイアログ・ボックスを表示させるプログラムは,「comdlg32.dll」というファイルに格納されている。同じプログラムでcomdlg32.dllを利用したプログラムをWindows XPとWindows Meで比較すると,表示が違う(図7[拡大表示])。これは設計者が意図したことではなく,システムが提供するライブラリの改変によって起きたことだ。このように,プログラマが意識しなくても,OS自体やDLLを改変することで機能の改善を実現できるのが,DLLのメリットだ。またスタティック・リンクだと,それぞれのアプリケーションに同じコードが組み込まれてしまう。これはディスク領域を圧迫しかねないし,無駄も多い。もしも組み込んでいたコードにバグが見つかったら,すべてのアプリケーションを再構築しなければならなくなる。DLLにしておけばこういったトラブルから解放される。

図8●OSはダイナミック・リンク・ライブラリの固まりといってもよい。
例えば筆者が使っているWindows XPでは,\Windows\system32ディレクトリに実に1252個のダイナミック・リンク・ライブラリが入っている

 OSが提供するさまざまな機能は,多くの場合DLLの形で実装されている(図8[拡大表示])。前述のコモン・ダイアログは,その代表例だ。こういったDLLを「OSの機能」と見るか否かは,非常に難しい。DLLはOSの機能拡張と考えることもできるからだ。標準状態で仮にそのDLLが組み込まれていないとしても,後からDLLを追加すればアプリケーション・ソフトは利用できるようになる。例えばMicrosoft社が標準的な機能として追加する計画を立て,Windows Updateなどを通じて配布すれば,それはやがて“OSの機能”と言えるものになるかもしれない。現在配布している「.NET Framework」などは,その端的な例と言えるだろう。.NET Frameworkも,基本的にはDLLで実装されていると考えていいだろう。ただし.NET Frameworkはカバー範囲が広く,単純にDLLだけで構成されているわけではない。.NET Framework対応のプログラムは通常のプログラム同様,「.exe」形式になる。この.exeファイルが必要なDLLを呼び出している。

動かすとエラーが出ることがある

 こうしてみるとDLLはいいことずくめのように見える。しかしもちろんDLLにもデメリットはある。いわゆる「DLL地獄」というヤツだ。

 DLL地獄とは,そのDLLのバージョンによって正しく動作しなかったり,場合によってはアプリケーションが動かなくなったりすることだ。DLLを使うということは,呼び出す側は「こういう名前の処理で,パラメータを渡す順番はこうだ」と決めつけるしかない。もしもバージョンの違いによって,新しい機能を利用するためのパラメータを一つ追加してしまったりすると,それだけでプログラムは動かなくなる可能性がある注7)。

 また当然ながら,同じ引数,同じ名称の関数であっても,処理方式が変わることによってエラーを引き起こす可能性がある。特に副作用を期待しているような場合に問題が発生し得る。

 Windows系OSの場合,もう一つ重要な問題がある。DLLのなかのルーチンを,名称ではなく番号で呼び出す仕組みを備えていることだ。この番号を前提として呼び出しているシステムの場合,非常にエラーが起きる確率が高くなる。

「なるほど。コンパイルとビルドって,全然違う作業なんですね」
「そうだね。それになぜリンクが必要なのかと疑問に感じるのは,
とても自然なことじゃないかな。
すべてがダイナミック・リンクであれば,不要にできることだからね」

(北郷 達郎、八木 玲子)