共有DLLの使い方

戻る

Windowsシステムは,多くのDLLで構成されています.コードをDLL化することにより, 複数のアプリケーションが同じコードを同時に利用することができ,メモリや ディスク容量を減らすことができます.しかし,DLLにバグがある場合,同時に複数の アプリケーションでトラブルを起こす危険もあります.
マイクロソフトの開発環境では,再配布を行ってもよいとされるDLLが多く存在します が,これらのDLLを配布することにより,インストール先のマシンにインストール されている他のアプリケーションに影響を与える場合があります.
そこで,筆者の経験を踏まえて共有DLLの扱いについて考えてみたいと思います.

15-1 新しいDLLは安全か?

Microsoft社は,新しい開発環境を提供する度に共有DLLもアップグレードします. 少し前は,新しい開発環境ごとに共有DLLも異なるファイル名で提供してきました.
例えば,MFC(Microsoft Foundation Class)やVB(Visual Basic)のランタイムDLLは ファイルサイズが1MB以上のサイズにも関わらず,MFC30.DLL, MFC40.DLL, MFC42.DLLの ようにクラスのバージョンが新しくなると別モジュールとして提供されています.
これでは,似たような機能のDLLが複数存在することになり,せっかく共有にしても メリットがあまりない状態でした.この方式は,必ずランタイムルーチンを必要とする VBアプリケーションを利用するユーザーからの不満が多かったのではないかと思い ます.そこで,最近ではVBランタイムやMFCは新しい機能を提供してもモジュール名は 変えないようになってきました.
(* Visual Studio97,6.0のMFCは,MFC42.DLLのままアップグレードされている.)
しかし,それらのモジュールに新しい機能を追加したり,仕様の変更が行われることに より以前のアプリケーションが正しく実行できなくなるといったことが,よく起こる ようになってしまったように思います.
このため,新しいモジュールは以前のバグが無くなっているので安全ということはなく 新しい問題を抱えている可能性があることも忘れてはいけません.

15-2 システムディレクトリにDLLを置くことを拒否される

Microsoft社が提供するアプリケーション特にInternet ExplorerやOfficeは,システム モジュールを多く変更するため,それによるトラブルが発生することが多く発生 しました.そして,そのことが雑誌などに大きく取り上げられたこともあり, エンドユーザによっては,すべてのアプリケーションについて「システムモジュールを 置き換えるものは基本的にお断り」としている場合が多くなっています.
このような,システムディレクトリのモジュールを置き換える必要がある アプリケーションを警戒しているエンドユーザの場合,そのようなアプリケーションを こちらから提示すると,次のような要求を受ける場合があります.
「他のアプリケーションに影響を与えないことを証明して欲しい」
「何を根拠に,このモジュールを置き換えるのか?根拠を提示して欲しい」
「マイクロソフト社はどう言っているのか聞いて欲しい」
しかしながら,実際にトラブルが起こる場合があることがありますので,共有DLLを 利用しないようにするか,現状を説明して,モジュールを置き換えることを承諾して もらうに説得するしかありません.承諾してもらえなければ,共有DLLが必要な機能を 組み込まないか,共有DLLを利用せずに実現する方法を考える必要もあるかも しれません.

15-3 システムディレクトリの共有DLLを必要としないアプリケーション

それでは,そうすれば共有DLLを利用しないアプリケーションを作成できるかという ことになりますが,必ず可能であるという定石はありませんが,基本になる項目を あげてみると,次のようになります.
  • VisualBasicなどランタイムDLLを必要とする言語は利用しない
  • Cランタイムルーチンはスタティックリンクする
  • MFCは利用しない(少なくとも,VisualC++ver5.0以前のコンパイラではMFCが利用しているCランタイムルーチンはスタティックリンクされない.)
  • OLEに関連する機能のうち,特にOLEオートメーション関連やファーストリリースの Windows95(4.00.950)で何もインストールしていない状態で動作しない機能は避ける
    しかし,このような条件で作成できるアプリケーションは,少数派ではないかと 思われます.また,すべての機能をWin32API+Cランタイムルーチンでアプリケーション を作成する必要がありますので,ウィンドウプロシジャを知らないMFCに馴れた新しい Windowsプログラマーには大きな負担になるのではないかと思われます.

    ●共有DLLを自分nディレクトリに置く
    もう少し簡単に共有させない方法としては,共有DLLを自分のディレクトリに置く といった方法があります.そうすれば少なくとも自分のアプリケーションでしか DLLの共有できませんが,アプリケーションが多くのモジュールで構成されている場合 には,よいでしょう.そうすれば,MFCベースのアプリケーションも利用できるように なります.しかし,アプリケーションがOLE関連の機能を利用した場合,OLEを介して 間接的にシステムディレクトリのモジュールを利用する場合があります.
    この方法を採用する場合は,アプリケーションが持つすべての機能で,システム ディレクトリにある共有DLLがロードされないことを十分にテストしなければ なりません.
    (* WindowsNTでモジュールが利用されているかを確認するには,開発環境 に含まれるプロセスビューアを利用します.Windows95/98ではマイクロソフトが提供 する開発ツールでそのような機能を持つのは,デバッガくらいしか思い当らない.)
    特にCOM/OLEを利用しているアプリケーションは,連携して動作する他の アプリケーションが存在することがありますので,それらの状態も含めて調査する 必要があるかもしれません.

    ●Windows98固有の仕組みに注意
    Windows98では,もうひとつ注意しなければならないことがあります.
    HKEY_LOCAL_MACHINE\System\CurrentControlSet\SessionManager\KnownDLLs
    にDLL名を設定すると,アプリケーションと同じディレクトリに共有DLLがあっても \Windows\System ディレクトリにあるDLLのみを検索してロードを行います.
    これは,DLLロードのパフォーマンスを向上させるためですが,アプリケーション と同じディレクトリにある共有DLLをロードさせたいときは,レジストリからDLL名 を削除しなければなりません.

    ●システムを再起動してもよいですか?
    共有DLLを自分のアプリケーションで利用した場合,利用しているDLLをインストーラで 配布する必要があります.しかし,MSVCRT40.DLL のようにWindowsNTの内部コマンド などでも利用されているDLLをインストールする必要がある場合,ほぼ確実にシステム の再起動が必要になります.特に,サービスを作成する場合は共有DLLを出来るだけ 利用しないようにしておいたほうがよいでしょう.
    UNIXシステムの場合,実行モジュール・ダイナミックリンクライブラリともに, 実行中でも更新や削除を行うことができます.このため,再起動を行わなくても インストールを完了することができます.しかし,差し換えられたモジュールの動作は 保証されないようです.逆にWindowsシステムでは,実行中のモジュールを差し換えると 正常に動作できないため,モジュールをロックし上書きできないようにしてあるのでは ないかと思われます.

    15-4 システムディレクトリの共有DLLを必ず必要とするアプリケーション

    共有DLLを必ず必要とするアプリケーションの代表的なものは次のようなものが あります.
  • VisualBasicで作成している
  • MFCを利用している(MFC自身をスタティックリンクしてもMSVCRT40.DLLを利用する)
  • OLEオートメーション・プロパティーページを利用している
  • DAOなどのランタイムルーチンやドライバーを利用している
  • OSのパッケージに含まれないモジュール(WinInetAPIなど)を利用している
    ここで重要なのは,お店から買ってきたばかりのPCで動作するからといって,すべての マシンで大丈夫という保証はできません.最近のPCには,すでにアプリケーションが インストールされており,それらをアンインストールしてもシステムディレクトリに 含まれるモジュールがすべて元の状態に戻るという保証はできません.
    したがって,自分のアプリケーションが必要とする共有DLLがどのバージョンのOSから 含まれるのか,また同じモジュールでもどのバージョンからアプリケーションを実行 しても問題ないかを調べておく必要があるかもしれません.
    (* OLE関連のモジュールは,必要なときだけロードされる場合が多いので,一見正常に 動作しているように見えるが,「ある機能だけ動作しない」という状況が起こる場合が 多い.)
    例えば,VisualC++5.0で作成したMFCベースのActiveXは,MFCとランタイム以外に OLEAUT32.DLL, OLEPRO32.DLL を必要としますが,これらのOLEモジュールを インストールしなくてもOSのインストール媒体に含まれているためActiveXが正常に 動作する場合とそうでない場合があります.どのバージョンから正常に動作するかを 知っておくことにより,他のアプリケーションによってそれらのモジュールが置き換え られたときに少なくとも,どのモジュールを戻せば正常に動作するかを知ることが できます.ただし,安易に古いバージョンに戻すと,他のアプリケーションが動作 しなくなる恐れがありますので注意が必要です.

    15-5 OSや開発環境に含まれる共有DLL

    開発環境で再配布が可能とされるもののうち,代表的なモジュールのバージョン をまとめてみました.エンドユーザー先で問題が起こった場合,モジュールの バージョンによって動作しないことを付きとめるには,自分のアプリケーションが どのバージョンなら正常に動作するかを確認しておくとよいでしょう.
    VC++ にOLEAUT32.DLL, OLEPRO32.DLL の両方が含まれる場合,それらはペアになって います.これらのモジュールをインストールする必要があるときは,両方を インストールしなければなりません.
    なお,各モジュールの修正履歴については,MSDN Library や,Microsoft社の ホームページで確認してください.

    表15-1:代表的なモジュールのDLLバージョン
     9595OSR1/sp195OSR295OSR2.195OSR2.598NT4.0WSNT4.0sp3IE3.02*IE4.01*
    MFC30.DLL--3.2.0003.2.0003.2.0003.2.000----
    MFC30LOC.DLL----------
    MFC40.DLL--4.1.61394.1.61394.1.61394.1.61394.1.6139-4.1.61394.1.6139
    MFC40LOC.DLL---------4.1.6038
    MFC42.DLL-----4.21.7325---4.1.6038
    MFC42LOC.DLL-----4.21.73034.2.6256---
    MSVCRT.DLL-----5.00.7128----
    MSVCIRT.DLL-----4.20.61644.20.6201--4.20.6164
    MSVCRT10.DLL------4.20.6201---
    MSVCRT20.DLL--2.11.0002.11.0002.11.0002.11.000----
    MSVCRT40.DLL--4.10.60384.10.60384.10.60384.10.60384.10.6038-4.10.60384.10.6038
    OLEAUT32.DLL2.12.12.20.40492.20.40492.20.40492.20.41222.20.40492.20.41182.20.40542.20.4122
    OLEPRO32.DLL--4.1.60384.1.60384.1.60385.0.41224.0.52775.0.41184.1.60384.1.6038
    (a)OS(日本語版)に含まれる共有モジュール

    * InternetExplorerはOSではありませんが,再配布可能モジュール以外のシステム モジュールを置きかえるなど,システムへの影響が大きいアプリケーションで あるため確認しておくことにします.

     2.04.04.14.25.05.0sp36.06.0sp2
    MFC30.DLL3.0.001-- - ----
    MFC30LOC.DLL3.0.000-------
    MFC40.DLL-4.0.53214.1.61304.1.6139----
    MFC40LOC.DLL-4.0.53214.1.60384.1.6038----
    MFC42.DLL---4.2.62564.21.70224.21.73036.00.8168.06.00.8267.0
    MFC42LOC.DLL---4.2.62564.21.70224.21.73036.00.8168.0-
    MSVCRT.DLL---4.20.62015.00.70225.00.73036.00.8168.06.00.8337.0
    MSVCIRT.DLL---4.20.62015.00.7022-6.00.8168.0-
    MSVCRT10.DLL情報なし情報なし情報なし情報なし----
    MSVCRT20.DLL2.10.0002.10.0002.10.0002.10.0002.11.000---
    MSVCRT40.DLL-4.00.52704.10.60384.10.60384.10.6038-4.21.0000-
    OLEAUT32.DLL----2.20.4054 -2.30.42612.40.4268
    OLEPRO32.DLL-4.0.53214.1.60384.2.60685.0.4055-5.0.42615.0.4268
    (b)Visual C++ に含まれるモジュール

    15-6 共有DLLを置き換える

    インストーラで共有DLLを置きかえる場合,すでに他のアプリケーションが利用して 上書きできない場合のために,システムの再起動を行い,起動時にアプリケーションが それらのDLLを利用する前に置きかえるようにスケジュールする必要があります. また,共有DLLは次のレジストリキーに登録し,現在利用しているアプリケーションが 存在することを設定しなければなりません.

    ●JAAXDIST.EXE
    Visual C++ ver5.0 には,JAAXDIST.EXE という再配布可能モジュールが あります.これを実行すると,次のモジュールがインストールされます.
    WININET.DLL
    INLOADER.DLL
    URLMON.DLL
    HLINK.DLL
    HLINKPRX.DLL
    STDOLE2.TLB
    OLEAUT32.DLL
    AXDIST.INF
    REGSVR32.EXE
    (* これらのモジュールはVC++5.0での再配布可能モジュール. VC++6.0には含まれないものがあるので,詳しくはVC++6.0のドキュメントを参照)
    JAAXDIST.EXE は,AXDIST.INF でインストール作業を行います.このEXEファイルは, CAB形式の圧縮ファイルを自己展開できるようにしてありますので,INFファイルの 書き方を知りたいときは,このファイルを見てみるとよいでしょう.

    ●OLEAUT32.DLLの配布
    OLEAUT32.DLL を配布するときには,STDOLE2.TLB も一緒に配布しなければ正しく動作 しないことがあります.つまり,VC++5.0以降で作成したアプリケーションが,OLEの 再配布モジュールを必要とする場合,
    OLEAUT32.DLL
    OLEPRO32.DLL
    STDOLE2.TLB
    をセットにして配布し,DLLはレジストを行う必要があります.

    ●DCOM98 for Windows9x
    しかし最近では,これらのモジュールを独自にアプリケーションのインストーラで, インストールするのでなく"DCOM98 for Windws95/98" を配布することを強く推奨 しています.このことは,OLE関連モジュールのインストールミスなどによる トラブルが多く発生していることを裏付けているのではないでしょうか?

    ●手作業
    Windows95/98のモジュールを,手作業でモジュールを上書きするときは,まず エクスプローラでコピーを行ってみます.ここで,上書きできないときは, あらゆるアプリケーションを終了させてから,再度行います.それでもだめなときは, 「MS-DOSモードで起動する」でWindowsを終了し,コマンドラインからCOPYコマンドで コピーを行います.WindowsNTでは,システムドライブがFATでフォーマット されている場合に,MS-DOSをフロッピーディスクなどで起動すると,上書きすること ができます.
    HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\SharedDLLs
    にファイルのフルパスのキーを作成し,すでにキーがある場合は値をインクリメント します.もし,アプリケーションを上書きインストールする場合は,値を インクリメントしないようにしなければなりません.
    そして,アンインストールを行うときは,その値をデクリメントし,0ならモジュール をシステムから削除してもよいということになります.しかし,その場合でも無条件 に削除するのでなく,削除するかを選択できるようにしておくと親切でしょう.

    ●レジストを行う必要ある共有DLL
    共有DLLのうち,OLEやCOMに関連する機能を持つ場合,レジストが必要になることが あります.そのようなDLLは,バージョン情報リソースの "StringFileInfo" セクションに "OLESelfRegister" というエントリを持っています.
    しかし,このエントリは必須ではありませんので,すべてのDLLがこのエントリを 持っているとはいえません.このため,自己登録を行う機能を持つかを判断するには, DLLがレジスト・アンレジストAPIを持っているかで判断するほうがようでしょう.
    DLLが新しくなった場合,レジストリに設定されている値が,変更されたり 追加されている可能性があります.それによって,モジュールを上書きしただけでは 正しく動作しない場合もあります.このため,レジストが必要なモジュールは, 必ずインストール(上書き)時に再度レジストを行う必要があります. レジストを行う方法は,ActiveXと同様に通常DLLがもっているため,REGSVR32.EXE のようなツールを用いて,DLLが持つ設定用APIをそれぞれ呼び出す必要が あります.
    (* VC++など開発環境のサンプルソースとして公開されている.)

    表15-2:レジスト・アンレジストAPI
    VOID DllRegisterServer(VOID)
    OLEサーバーの登録

    引数
    なし

    戻り値
    なし
    VOID DllUnregisterServer(VOID)
    OLEサーバーの登録

    引数
    なし

    戻り値
    なし

    15-7 誰が使っているDLLなのか?

    自分のアプリケーションでリンクしていないのに,ロードされているDLLがいくつか あります.(* アプリケーションがリンクしているモジュールは,VC++やWin32SDKに含まれる "DUMPBIN /DEPENDENTS モジュール名"で見ることができます.)そのようなDLLの 代表的なものに,IME関連モジュールはあります.
    IMEは,特にIME関連のAPIを利用しなくても,キー入力イベントを受け付けることが できるすべてのアプリケーションではロードされるようです.このモジュールは, アプリケーションの一部のDLLと同じように動作しますので,IME内部で異常終了を 起こすと,アプリケーション自身も異常終了させてしまいます.
    しかし,日本語版のWindowsは,IMEをロードせずにアプリケーションを動作させる ことができませんので,前向きにIMEの存在を受け入れるしかありません.
    IME以外にも,COMやOLEを利用した場合,インターフェースを構成しているDLLがどこに あるのかはっきりしない場合があります.このような機能を利用した場合,自分自身の モジュールがリンクしていなくても,システムディレクトリ内のモジュールを利用 している場合があることに注意してください.前述したMFCベースのActiveXを作成した 場合のように,再配布可能なOLEAUT32.DLL, OLEPRO32.DLL がOLEを経由して利用される 場合があります.これらのDLLは,アプリケーションをインストールしたディレクトリ に置いても動作する場合がありますが,基本的にシステムディレクトリに置いて利用 することが前提になっているため,それ以外の場所で実行することはお勧め できません.

    15-8 結論

    結局のところ,ServicePack がインストールされると再配布可能でないシステムDLLで さえ更新されますので,外部からインストールされたモジュールの影響を受けなくする ことはまず不可能でしょう.
    となると,「アプリケーションをインストールしたときや モジュールをアップグレードしたときにシステムの再起動を要求してもよいか?」 といったことも考慮して,どの方法がベターであるかを開発中のアプリケーションに 当てはめて考える必要があると思います.