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

このノートでは、MFC ライブラリを Windows ダイナミック リンク ライブラリ (DLL) の一部として使用できるようにする標準 MFC DLL について説明します。 ここでは、Windows DLL とそのビルド方法に精通していることを前提とします。 MFC ライブラリの拡張機能を作成可能な MFC 拡張 DLL の詳細については、「MFC の DLL バージョン」を参照してください。

DLL インターフェイス

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

DLL とアプリケーションの両方で MFC を使用する場合は、MFC ライブラリの共有バージョンを使用するか、ライブラリのコピーに静的にリンクするかのどちらかを選択できます。 アプリケーションと DLL はどちらも、MFC ライブラリの標準バージョンのいずれかを使用する可能性があります。

標準 MFC DLL には、次のようなメリットがあります。

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

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

  • MFC に動的にリンクされた標準 MFC DLL を使用する場合は、MFC の共有バージョンの使用によるメモリの節約が重要になる可能性があります。 ただし、DLL を使用して、共有 DLL の Mfc<version>.dll と Msvvcrt<version>.dll を配布する必要があります。

  • DLL 設計は、クラスがどのように実装されているかとは無関係です。 DLL 設計は、必要な API にのみエクスポートされます。 そのため、実装が変更されても、標準 MFC DLL は有効なままです。

  • MFC に静的にリンクされた標準 MFC DLL を使用すると、DLL とアプリケーションの両方で MFC が使用されている場合に、DLL とは異なるバージョンの MFC を必要とするアプリケーションで問題が発生しません。その逆も同様です。 MFC ライブラリは各 DLL または EXE に静的にリンクされるため、使用するバージョンを意識する必要がありません。

API の制限

MFC 機能の中には、DLL のバージョンに適用されないものがあります。これは、技術的な制限や、このようなサービスの多くがアプリケーションから提供されることによります。 現在のバージョンの MFC では、適用できない唯一の関数は CWinApp::SetDialogBkColor です。

DLL のビルド

MFC に静的にリンクされた標準 MFC DLL をコンパイルする場合は、シンボルの _USRDLL_WINDLL が定義されている必要があります。 また、次のコンパイラ スイッチを使用して、DLL コードをコンパイルする必要があります。

  • /D_WINDLL は、コンパイルが DLL 用であることを示します。

  • /D_USRDLL は、標準 MFC DLL をビルドすることを示します。

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

  • /D_AFXDLL は、MFC と動的にリンクされた標準 MFC DLL をビルドすることを示します。

アプリケーションと DLL 間のインターフェイス (API) を明示的にエクスポートする必要があります。 インターフェイスを低帯域幅になるように定義し、可能な場合は、C インターフェイスのみを使用することをお勧めします。 ダイレクト C インターフェイスは、より複雑な C++ クラスよりも保守が容易です。

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

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

AFX_MANAGE_STATE(AfxGetStaticModuleState( ))

WinMain -> DllMain

MFC ライブラリは、一般的な MFC アプリケーションと同様に、CWinApp 派生オブジェクトを初期化する標準の Win32 DllMain エントリ ポイントを定義します。 一般的な MFC アプリケーションと同様に、すべての DLL 固有の初期化を InitInstance メソッドに配置します。

アプリケーションはメイン メッセージ ポンプを備えているため、CWinApp::Run メカニズムが DLL に適用されないことに注意してください。 DLL がモードレス ダイアログを表示するか、独自のメイン フレーム ウィンドウを備えている場合は、アプリケーションのメイン メッセージ ポンプから、CWinApp::PreTranslateMessage を呼び出す、DLL がエクスポートしたルーチンを呼び出す必要があります。

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

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

DLL のリンク

MFC に静的にリンクされた標準 MFC DLL を使用する場合は、DLL を Nafxcwd.lib または Nafxcw.lib にリンクして、Libcmt.lib という名前の C ランタイムのバージョンにリンクする必要があります。 これらのライブラリは、事前にビルドされており、Visual C++ セットアップの実行時に指定することによってインストールできます。

サンプル コード

完全なサンプルについては、MFC の高度な概念のサンプル プログラム DLLScreenCap を参照してください。 このサンプルで注目すべき興味深い点は次のとおりです。

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

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

  • DLL を使用するアプリケーションは、C++ 内に存在する必要はありません。

  • アプリケーションと DLL 間のインターフェイスは、C または C++ によって使用可能な API であり、DLLScreenCap.def を使用してエクスポートされます。

次の例は、MFC と静的にリンクされた標準 MFC DLL で定義されている API を示しています。 この例では、宣言が C++ ユーザー用の extern "C" { } ブロックで囲まれています。 これには次のようなメリットがあります。 まず、C++ 以外のクライアント アプリケーションで DLL API を使用できます。 次に、C++ の名前修飾がエクスポートされた名前に適用されないため、DLL のオーバーヘッドが低減されます。 最後に、名前修飾を気にすることなく、.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 とアプリケーション間のインターフェイスの複雑さが軽減され、DLL を C プログラムで使用できるようになります。

関連項目

番号順テクニカル ノート
カテゴリ別テクニカル ノート