テクニカル ノート 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 プログラムで使用できるようになります。