次の方法で共有


テクニカル ノート 11: DLL の構成要素としての MFC

ここでは、MFC ライブラリを Windows ダイナミック リンク ライブラリ (DLL) の一部として使えるようにする標準 DLL について説明します。 このテクニカル ノートは、Windows DLL とそのビルド方法に精通したプログラマを対象としています。 MFC ライブラリの拡張機能を作成するための MFC 拡張 DLL の詳細については、「テクニカル ノート 33: MFC の DLL バージョン」を参照してください。

DLL インターフェイス

標準 DLL では、アプリケーションと DLL の間のインターフェイスは、C スタイルの関数または明示的にエクスポートしたクラスで指定されることが前提となります。 MFC のクラス インターフェイスはエクスポートできません。

DLL とアプリケーションの両方で MFC を使用する場合は、共有バージョンの MFC ライブラリを使用するか、ライブラリのコピーに静的にリンクするかをそれぞれが選択できます。 アプリケーションと DLL の両方が、標準バージョンの MFC ライブラリの 1 つを使用することもあります。

標準 DLL には次のような利点があります。

  • DLL を使用するアプリケーションは、MFC を使用する必要がありません。また、Visual C++ アプリケーションでなくてもかまいません。

  • MFC と静的にリンクする標準 DLL のサイズは、使用されてリンクされる MFC と C のランタイム ルーチンにのみ依存します。

  • MFC と動的にリンクする標準 DLL では、MFC の共有バージョンを使用することで大幅なメモリの節約を期待できます。 ただし、共有 DLL の Mfc<version>.dll と Msvvcrt<version>.dll を DLL と一緒に配布する必要があります。

  • DLL のデザインはクラスの実装方法に依存しません。 独自のデザインの DLL では、必要な API だけをエクスポートします。 そのため、実装が変わっても、標準 DLL は同じように使用できます。

  • MFC と静的にリンクする標準 DLL では、DLL とアプリケーションの両方で MFC を使用する場合に、MFC のバージョンがアプリケーションと DLL で異なっていても問題はありません。 MFC ライブラリは各 DLL または EXE と静的にリンクされるので、どのバージョンを使っても問題はありません。

API の制約

MFC の機能には、DLL バージョンでは使用できないものがあります。これは、技術上の制約による場合と、そのサービスが通常はアプリケーションから提供されるためという理由による場合があります。 現在のバージョンの MFC では、使用できない関数は CWinApp::SetDialogBkColor だけです。

独自の DLL のビルド

MFC と静的にリンクする標準 DLL をコンパイルするときは、シンボル _USRDLL と _WINDLL を定義する必要があります。 また、独自の DLL のコードは次のコンパイラ スイッチを使ってコンパイルする必要があります。

  • /D_WINDLL は DLL のコンパイルを意味します。

  • /D_USRDLL は標準 DLL のビルドを意味します。

MFC と動的にリンクする標準 DLL をコンパイルするときも、これらのシンボルを定義し、コンパイラ スイッチを使用する必要があります。 さらに、シンボル _AFXDLL を定義し、次のコンパイラ スイッチを使用して DLL のコードをコンパイルする必要があります。

  • /D_AFXDLL は、MFC と動的にリンクする標準 DLL のビルドを意味します。

アプリケーションと DLL 間のインターフェイス (API) は明示的にエクスポートしてください。 独自のインターフェイスは低帯域に定義して、できるだけ C インターフェイスを使うことをお勧めします。 直接的な C インターフェイスの方が、複雑な C++ クラスより保守が簡単です。

独自の API は別のヘッダー ファイルに記述し、C と C++ の両方のファイルにインクルードできるようにします。 例については、『MFC サンプル』の DLLScreenCap のヘッダー ファイル ScreenCap.h を参照してください。 独自の関数をエクスポートするには、関数をモジュール定義 (.DEF) ファイルの EXPORTS セクションに入れるか、関数定義に __declspec(dllexport) を含めます。 これらの関数をクライアント実行可能ファイルにインポートするには、__declspec(dllimport) を使用します。

MFC と動的にリンクする標準 DLL では、エクスポートされるすべての関数の先頭に AFX_MANAGE_STATE マクロを追加する必要があります。 このマクロは、現在のモジュール ステートを DLL 用に設定します。 このマクロを使用するには、DLL からエクスポートされる関数の先頭に次のコード行を追加します。

AFX_MANAGE_STATE(AfxGetStaticModuleState( ))

WinMain -> DllMain

MFC ライブラリでは、Win32 の標準の DllMain エントリ ポイントが定義され、CWinApp からの派生オブジェクトが通常の MFC アプリケーションと同じように初期化されます。 DLL 固有の初期化処理はすべて、通常の MFC アプリケーションと同様に InitInstance メソッドに記述します。

CWinApp::Run の機構は DLL では使用できません。これは、アプリケーションがメイン メッセージ ポンプを所有しているためです。 DLL でモードレス ダイアログを表示したり、独自のメイン フレーム ウィンドウを作成したりする場合は、まず DLL によってエクスポートされたルーチンをアプリケーションのメイン メッセージ ポンプから呼び出し、次にそのルーチンから CWinApp::PreTranslateMessage を呼び出す必要があります。

この関数の使い方については、DLLScreenCap サンプルを参照してください。

MFC の提供する DllMain 関数は、DLL がアンロードされる前に、CWinApp から派生したクラスの CWinApp::ExitInstance メソッドを呼び出します。

独自の DLL のリンク

MFC と静的にリンクする標準 DLL では、独自の DLL を Nafxcwd.lib または Nafxcw.lib とリンクし、C ランタイム ライブラリの Libcmt.lib ともリンクする必要があります。 これらのライブラリは既にビルドされており、Visual C++ のセットアップの実行時に指定してインストールできます。

サンプル コード

サンプル全体については、「MFC サンプル」のサンプル プログラム DLLScreenCap を参照してください。 このサンプルでは、次の点に注目してください。

  • コンパイラ フラグは、DLL とアプリケーションの間で異なります。

  • リンク行と .DEF ファイルは、DLL とアプリケーションの間で異なります。

  • DLL を使用するアプリケーションは、C++ で作成されていなくてもかまいません。

  • アプリケーションと DLL の間のインターフェイスは、C または C++ で使用できる API であり、DLLScreenCap.def でエクスポートされます。

次の例では、MFC と静的にリンクする標準 DLL で定義される API を示します。 この例では、C++ ユーザーのために宣言を extern "C" { } ブロックで囲んでいます。 この方法は、いくつかの利点があります。 まず、DLL API を非 C++ クライアント アプリケーションでも使えます。 第 2 の利点として、C++ の名前修飾がエクスポート名に適用されないため、DLL のオーバーロードが減ります。 第 3 の利点として、.DEF ファイルへの明示的な追加がさらに簡単になり (序数によるエクスポート)、名前の変更を心配する必要がなくなります。

#ifdef __cplusplus
extern "C" {
#endif  /* __cplusplus */

struct TracerData
{
    BOOL    bEnabled;
    UINT    flags;
};

BOOL PromptTraceFlags(TracerData FAR* lpData);

#ifdef __cplusplus
}
#endif

API で使用する構造体は、MFC クラスから派生するのではなく、API ヘッダーで定義します。 これにより、DLL とアプリケーションの間のインターフェイスが複雑になるのを避けられるほか、C プログラムからも DLL を使用できるようになります。

参照

その他の技術情報

番号順テクニカル ノート

カテゴリ別テクニカル ノート