DLL とは

この記事では、ダイナミック リンク ライブラリ (DLL) が何であるかと、DLL を使用するときに発生する可能性があるさまざまな問題について説明します。 また、独自の DLL を開発するときに考慮する必要がある高度な問題についてもいくつか説明します。

適用対象: Windows 10 - すべてのエディション
元の KB 番号: 815065

概要

この記事では、DLL の概要を説明しながら、ダイナミック リンクの方法、DLL の依存関係、DLL エントリ ポイント、DLL 関数のエクスポート、DLL のトラブルシューティング ツールについて説明します。

この記事では、DLL と Microsoft .NET Framework アセンブリの詳細な比較について説明します。

Windows オペレーティング システムでは、オペレーティング システムの機能の多くが DLL によって提供されています。 さらに、これらの Windows オペレーティング システムのいずれかでプログラムを実行すると、プログラムの機能の多くが DLL によって提供される場合があります。 たとえば、一部のプログラムにはさまざまなモジュールが含まれている場合があり、プログラムの各モジュールは DLL に含まれ、配布されます。

DLL を使用すると、コードのモジュール化、コードの再利用、メモリ使用効率の向上、ディスク領域の削減を促進することができます。 つまり、オペレーティング システム、プログラムの読み込み、実行速度を高速化し、コンピューター上のディスク領域を節約することができます。

プログラムが DLL を使用する場合、依存関係と呼ばれる問題により、プログラムが実行されない可能性があります。 プログラムが DLL を使用すると、依存関係が作成されます。 別のプログラムがこの依存関係を上書きして中断すると、元のプログラムが正常に実行されない可能性があります。

.NET Framework の導入により、ほとんどの依存関係の問題は、アセンブリを使用することで解消されました。

詳細

DLL は、複数のプログラムで同時に使用できるコードとデータを含むライブラリです。 たとえば、Windows オペレーティング システムでは、Comdlg32 DLL で一般的なダイアログ ボックス関連の機能を実行します。 この DLL に含まれる関数を使用して、各プログラムに [開く] ダイアログ ボックスを実装できます。 これは、コードの再利用とメモリ使用効率の向上を促進するのに役立ちます。

DLL を使用すると、1 つのプログラムを個別のコンポーネントにモジュール化できます。 たとえば、会計プログラムをモジュール別に販売することができます。 各モジュールは、そのモジュールがインストールされている場合、実行時にメイン プログラムに読み込むことができます。 モジュールは個別であるため、プログラムの読み込み時間が短くなります。 また、モジュールは、その機能が要求された場合にのみ読み込まれます。

さらに、プログラムの他の部分に影響を与えることなく、更新プログラムを各モジュールに簡単に適用することができます。 たとえば、給与プログラムがあり、税率が毎年変わる場合があります。 これらの変更を DLL に分離すると、プログラム全体をもう一度ビルドまたはインストールすることなく、更新プログラムを適用できます。

次の一覧は、Windows オペレーティング システムで DLL として実装されているファイルの一部を示しています。

  • ActiveX コントロール (.ocx) ファイル

    ActiveX コントロールの例としては、カレンダーから日付を選択できるカレンダー コントロールがあります。

  • コントロール パネル (.cpl) ファイル

    .cpl ファイルの例としては、コントロール パネルにあるアイテムがあります。 各アイテムが特殊な DLL です。

  • デバイス ドライバー (.drv) ファイル

    デバイス ドライバーの例としては、プリンターでの印刷を制御するプリンター ドライバーがあります。

DLL の利点

次の一覧は、プログラムで DLL を使用することで得られるいくつかの利点を示しています。

  • 使用するリソースの数を減らす

    複数のプログラムで同じ関数ライブラリを使用する場合、DLL によってディスクと物理メモリに読み込まれるコードの重複を減らすことができます。 これは、フォアグラウンドで実行されているプログラムだけでなく、Windows オペレーティング システムで実行されている他のプログラムのパフォーマンスにも大きな影響を与える可能性があります。

  • モジュール式アーキテクチャを促進する

    DLL は、モジュール型プログラムの開発を促進するのに役立ちます。 また、複数の言語バージョンを必要とする大規模なプログラムや、モジュール式アーキテクチャを必要とするプログラムの開発に役立ちます。 モジュール式プログラムの例としては、実行時に動的に読み込むことができる多くのモジュールを持つ会計プログラムがあります。

  • 展開とインストールを容易にする

    DLL 内の関数に更新プログラムまたは修正プログラムが必要な場合、プログラムを DLL に再リンクすることなく、DLL のデプロイとインストールを実行することができます。 さらに、複数のプログラムで同じ DLL を使用している場合、そのすべてのプログラムに更新プログラムまたは修正プログラムを適用することができます。 これは、定期的に更新または修正されるサード パーティの DLL を使用する場合に頻繁に役立ちます。

DLL の依存関係

プログラムまたは DLL が別の DLL で DLL 関数を使用すると、依存関係が作成されます。 プログラムが自己完結型ではなくなるため、依存関係が壊れた場合にプログラムで問題が発生することがあります。 たとえば、次のアクションのいずれかが発生した場合に、プログラムが実行されない可能性があります。

  • 依存 DLL を新しいバージョンにアップグレードする。
  • 依存 DLL を修正する。
  • 依存 DLL を、以前のバージョンで上書きする。
  • 依存 DLL をコンピューターから削除する。

これらのアクションは、DLL の競合と呼ばれます。 下位互換性が適用されていないと、プログラムが正常に実行されない可能性があります。

次の一覧では、依存関係の問題を最小限に抑えるために、Windows 2000 とそれ以降の Windows オペレーティング システムで導入された変更について説明しています。

  • Windows ファイル保護

    Windows ファイル保護では、承認されていないエージェントによるシステム DLL の更新または削除をオペレーティング システムで防ぎます。 プログラムをインストールする際にシステム DLL として定義されている DLL の削除または更新が試行されると、Windows ファイル保護が有効なデジタル署名を検索します。

  • プライベート DLL

    プライベート DLL を使用すると、共有 DLL に加えられた変更からプログラムを分離できます。 プライベート DLL は、バージョン固有の情報または空の .local ファイルを使用して、プログラムで使用される DLL のバージョンを適用します。 プライベート DLL を使用するには、プログラムのルート フォルダーで DLL を見つけます。 次に、新しいプログラムの場合は、バージョン固有の情報を DLL に追加します。 前のプログラムの場合は、空の .local ファイルを使用します。 各メソッドは、プログラムのルート フォルダーにあるプライベート DLL を使用するようにオペレーティング システムに指示します。

DLL のトラブルシューティング ツール

DLL の問題のトラブルシューティングに役立ついくつかのツールを使用できます。 これらのツールの一部を次に示します。

Dependency Walker

Dependency Walker ツールは、プログラムが使用するすべての依存 DLL を再帰的にスキャンできます。 Dependency Walker でプログラムを開くと、Dependency Walker は以下を確認します。

  • Dependency Walker は、欠落している DLL を確認します。
  • Dependency Walker は、無効なプログラム ファイルまたは無効な DLL を確認します。
  • Dependency Walker は、関数のインポートと関数のエクスポートが一致することを確認します。
  • Dependency Walker は、循環依存関係のエラーを確認します。
  • Dependency Walker は、異なるオペレーティング システム用であるために無効となっているモジュールを確認します。

Dependency Walker を使用すると、プログラムが使用するすべての DLL を文書化できます。 この文書は、今後発生する可能性がある DLL の問題を防ぎ、修正するのに役立つ場合があります。 Dependency Walker は、Visual Studio 6.0 のインストール時に、次のディレクトリに配置されます。

drive\Program Files\Microsoft Visual Studio\Common\Tools

DLL ユニバーサル トラブル シューター

DLL ユニバーサル トラブル シューター (DUPS) ツールを使用すると、DLL 情報の監査、比較、文書化、表示を実行することができます。 次の一覧には、DUPS ツールを構成するユーティリティの説明が記されています。

  • Dlister.exe

    このユーティリティは、コンピューター上のすべての DLL を列挙し、その情報をテキスト ファイルまたはデータベース ファイルに記録します。

  • Dcomp.exe

    このユーティリティは、2 つのテキスト ファイルに一覧表示されている DLL を比較し、相違点が記載された 3 番目のテキスト ファイルを生成します。

  • Dtxt2DB.exe

    このユーティリティは、Dlister.exe ユーティリティとDcomp.exe ユーティリティを使用して作成されたテキスト ファイルを dllHell データベースに読み込みます。

  • DlgDtxt2DB.exe

    このユーティリティは、Dtxt2DB.exe ユーティリティのグラフィカル ユーザー インターフェイス (GUI) バージョンを提供します。

DLL ヘルプ データベース

DLL ヘルプ データベースは、Microsoft ソフトウェア製品によってインストールされた DLL の特定のバージョンを見つけるのに役立ちます。

DLL の開発

このセクションでは、独自の DLL を開発するときに考慮する必要がある問題と要件について説明します。

DLL の種類

アプリケーションに DLL を読み込むと、2 つのリンク方法を使用してエクスポートされた DLL 関数を呼び出すことができます。 2 つのリンク方法とは、読み込み時のダイナミック リンクと実行時のダイナミック リンクです。

読み込み時のダイナミック リンク

読み込み時のダイナミック リンクを使用すると、アプリケーションはローカル関数などのエクスポートされた DLL 関数を明示的に呼び出します。 読み込み時のダイナミック リンクを使用するには、アプリケーションをコンパイルしてリンクするときに、ヘッダー (.h) ファイルとインポート ライブラリ (.lib) ファイルを指定します。 これを行うと、リンカーは DLL を読み込み、読み込み時にエクスポートされた DLL 関数の場所を特定するために必要な情報をシステムに提供します。

実行時のダイナミック リンク

実行時のダイナミック リンクでは、アプリケーションは LoadLibrary 関数または LoadLibraryEx 関数のいずれかを呼び出して、実行時に DLL を読み込みます。 DLL が正常に読み込まれたら、GetProcAddress 関数を使用して、呼び出したいエクスポートされた DLL 関数のアドレスを取得します。 実行時のダイナミック リンクを使用する場合、インポート ライブラリ ファイルは必要ありません。

次の一覧では、読み込み時のダイナミック リンクを使用するタイミングと実行時のダイナミック リンクを使用するタイミングに関するアプリケーション基準について説明します。

  • 起動時のパフォーマンス

    アプリケーションの初期スタートアップ パフォーマンスが重要な場合は、実行時のダイナミック リンクを使用する必要があります。

  • 使いやすさ

    読み込み時のダイナミック リンクでは、エクスポートされた DLL 関数がローカル関数に似ています。 つまり、読み込み時のダイナミック リンクを使用することで、これらの関数を簡単に呼び出すことができます。

  • アプリケーション ロジック

    実行時のダイナミック リンクでは、必要に応じてアプリケーションを分岐して異なるモジュールを読み込むことができます。 これは、複数言語バージョンの開発時に重要となります。

DLL エントリ ポイント

DLL を作成するときに、必要に応じてエントリ ポイント関数を指定できます。 エントリ ポイント関数は、プロセスまたはスレッドが DLL にアタッチされるか、DLL からデタッチされるときに呼び出されます。 エントリ ポイント関数を使用すると、DLL で必要に応じてデータ構造の初期化や破棄を実行できます。 さらに、アプリケーションがマルチスレッドの場合は、スレッド ローカル ストレージ (TLS) を使用して、エントリ ポイント関数の各スレッドにプライベートなメモリを割り当てることができます。 次のコードは、DLL エントリ ポイント関数の例です。

BOOL APIENTRY DllMain(
HANDLE hModule,// Handle to DLL module
DWORD ul_reason_for_call,// Reason for calling function
LPVOID lpReserved ) // Reserved
{
    switch ( ul_reason_for_call )
    {
        case DLL_PROCESS_ATTACHED: // A process is loading the DLL.
        break;
        case DLL_THREAD_ATTACHED: // A process is creating a new thread.
        break;
        case DLL_THREAD_DETACH: // A thread exits normally.
        break;
        case DLL_PROCESS_DETACH: // A process unloads the DLL.
        break;
    }
    return TRUE;
}

エントリ ポイント関数が FALSE 値を返す場合、読み込み時のダイナミック リンクを使用しているとアプリケーションが起動しません。 実行時のダイナミック リンクを使用していると、個々の DLL のみが読み込まれません。

エントリ ポイント関数は、単純な初期化タスクを実行する場合にのみ使用し、他の DLL 読み込み関数や終了関数を呼び出すために使用しないでください。 たとえば、エントリ ポイント関数を使用して、LoadLibrary 関数や LoadLibraryEx 関数を直接または間接的に呼び出すことはできません。 また、プロセスの終了時に FreeLibrary 関数を呼び出さないでください。

注:

マルチスレッド アプリケーションでは、データの破損を防ぐために、DLL グローバル データへのアクセスが同期されている (スレッド セーフになっている) ことを確認してください。 これを行うには、TLS を使用してスレッドごとに一意のデータを提供します。

DLL 関数をエクスポートする

DLL 関数をエクスポートするには、エクスポートされた DLL 関数に関数キーワードを追加するか、エクスポートされた DLL 関数を一覧表示するモジュール定義 (.def) ファイルを作成します。

関数キーワードを使用するには、エクスポートする各関数を次のキーワードで宣言する必要があります。
__declspec(dllexport)

エクスポートされた DLL 関数をアプリケーションで使用するには、インポートする各関数を次のキーワード (keyword)で宣言する必要があります。__declspec(dllimport)

通常、define ステートメントと ifdef ステートメントを持つ 1 つのヘッダー ファイルを使用して、エクスポート ステートメントと import ステートメントを区切ります。

モジュール定義ファイルを使用して、エクスポートされた DLL 関数を宣言することもできます。 モジュール定義ファイルを使用する場合、エクスポートされた DLL 関数に関数キーワードを追加する必要はありません。 モジュール定義ファイルでは、DLL の LIBRARY ステートメントと EXPORTS ステートメントを宣言します。 次のコードは、定義ファイルの例です。

// SampleDLL.def
//
LIBRARY "sampleDLL"
EXPORTS HelloWorld

サンプル DLL とアプリケーション

Visual C++ 6.0 では、Win32 ダイナミック リンク ライブラリ プロジェクト タイプまたは MFC AppWizard (dll) プロジェクト タイプのいずれかを選択して DLL を作成できます。

次のコードは、Win32 ダイナミック リンク ライブラリ プロジェクト タイプを使用して Visual C++ で作成された DLL の例です。

// SampleDLL.cpp
//

#include "stdafx.h"
#define EXPORTING_DLL
#include "sampleDLL.h"
BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved
)
{
    return TRUE;
}

void HelloWorld()
{
    MessageBox( NULL, TEXT("Hello World"), TEXT("In a DLL"), MB_OK);
}

// File: SampleDLL.h
//
#ifndef INDLL_H
    #define INDLL_H
    #ifdef EXPORTING_DLL
        extern __declspec(dllexport) void HelloWorld();
    #else
        extern __declspec(dllimport) void HelloWorld();
    #endif

#endif

次のコードは、SampleDLL でエクスポートされた DLL 関数を呼び出す Win32 アプリケーション プロジェクトの例です。

// SampleApp.cpp
//
#include "stdafx.h"
#include "sampleDLL.h"
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    HelloWorld();
    return 0;
}

注:

読み込み時のダイナミック リンクでは、SampleDLL プロジェクトをビルドするときに作成される SampleDLL.lib インポート ライブラリをリンクする必要があります。

実行時のダイナミック リンクでは、次のコードのようなコードを使用して、SampleDLL.dll でエクスポートされた DLL 関数を呼び出します。

...
typedef VOID (*DLLPROC) (LPTSTR);
...
HINSTANCE hinstDLL;
DLLPROC HelloWorld;
BOOL fFreeDLL;

hinstDLL = LoadLibrary("sampleDLL.dll");
if (hinstDLL != NULL)
{
    HelloWorld = (DLLPROC) GetProcAddress(hinstDLL, "HelloWorld");
    if (HelloWorld != NULL)
        (HelloWorld);
    fFreeDLL = FreeLibrary(hinstDLL);
}
...

SampleDLL アプリケーションをコンパイルしてリンクすると、Windows オペレーティング システムは SampleDLL DLL を次の順序で検索します。

  1. アプリケーション フォルダー

  2. 現在のフォルダー

  3. Windows システム フォルダー

    注:

    GetSystemDirectory 関数は、Windows システム フォルダーのパスを返します。

  4. Windows フォルダー

    注:

    GetWindowsDirectory 関数は、Windows フォルダーのパスを返します。

.NET Framework アセンブリ

.NET と .NET Framework の導入により、DLL に関連する問題のほとんどは、アセンブリを使用することで解消されました。 アセンブリは、.NET共通言語ランタイム (CLR) の制御下で実行される機能の論理ユニットです。 アセンブリは、.dll ファイルまたは.exe ファイルとして物理的に存在します。 ただし、内部的には、アセンブリは Microsoft Win32 DLL とは異なります。

アセンブリ ファイルには、アセンブリ マニフェスト、型メタデータ、Microsoft 中間言語 (MSIL) コード、およびその他のリソースが含まれています。 アセンブリ マニフェストには、アセンブリを自己記述するために必要なすべての情報を提供するアセンブリ メタデータが含まれています。 アセンブリ マニフェストには、次の情報が含まれています。

  • アセンブリ名
  • バージョン情報
  • カルチャ情報
  • 厳密な名前情報
  • ファイルのアセンブリ リスト
  • 型参照情報
  • 参照アセンブリ情報と依存アセンブリ情報

アセンブリに含まれる MSIL コードを直接実行することはできません。 代わりに、MSIL のコード実行は CLR を通じて管理されます。 既定では、アセンブリを作成すると、アセンブリはアプリケーションに対してプライベートになります。 共有アセンブリを作成するには、アセンブリに厳密な名前を割り当て、アセンブリをグローバル アセンブリ キャッシュに発行する必要があります。

以下に、Win 32 DLLの機能と比較したアセンブリの一部の機能を示します。

  • 自己記述

    アセンブリを作成すると、CLR でアセンブリを実行するために必要なすべての情報がアセンブリ マニフェストに含まれます。 アセンブリ マニフェストには、依存アセンブリの一覧が含まれています。 したがって、CLR は、アプリケーションで使用される一貫したアセンブリ セットを維持できます。 Win32 DLL では、共有 DLL を使用するときにアプリケーションで使用される DLL のセット間の一貫性を維持することはできません。

  • バージョン管理

    アセンブリ マニフェストでは、CLR によってバージョン情報が記録され、適用されます。 さらに、バージョン ポリシーを使用すると、バージョン固有の使用法を適用できます。 Win32 DLL では、オペレーティング システムではバージョン管理を適用できません。 DLL に下位互換性があることを確認する必要があります。

  • 並行展開

    アセンブリは並行展開をサポートします。 1 つのアプリケーションでアセンブリの 1 つのバージョンを使用でき、別のアプリケーションでは異なるバージョンのアセンブリを使用できます。 Windows 2000 以降、並行展開は、アプリケーション フォルダーで DLL を見つけることでサポートされます。 また、Windows ファイル保護により、システム DLL が無許可のエージェントで上書きまたは置き換えられることがなくなります。

  • 内蔵と分離

    アセンブリを使用して開発されたアプリケーションは、コンピューター上で実行されている他のアプリケーションから内蔵および分離できます。 この機能は、影響のないインストールをするのに役立ちます。

  • 実行

    アセンブリは、アセンブリ マニフェストで提供されるセキュリティのアクセス許可で実行され、CLR によって制御されます。

  • 言語非依存

    アセンブリは、サポートされている .NET 言語のいずれかを使用して開発できます。 たとえば、Microsoft Visual C# でアセンブリを開発し、Visual Basic .NET プロジェクトでアセンブリを使用することができます。

データ収集

Microsoft サポートの支援が必要な場合は、「展開関連の問題について TSS を使用して情報を収集する」に記載している手順に従って情報を収集することをお勧めします。

関連情報