Dll-writing
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,
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関数を使用するには、次のキーワードを使用してインポートする各関数を宣言する必要があります-
__declspec(dllimport)
通常、 define ステートメントと ifdef ステートメントを持つ1つのヘッダーファイルを使用して、エクスポートステートメントとインポートステートメントを分離します。
モジュール定義ファイルを使用して、エクスポートされたDLL関数を宣言することもできます。 モジュール定義ファイルを使用する場合、エクスポートされたDLL関数にfunctionキーワードを追加する必要はありません。 モジュール定義ファイルで、DLLの LIBRARY ステートメントと EXPORTS ステートメントを宣言します。 次のコードは、定義ファイルの例です。
//SampleDLL.def
//
LIBRARY "sampleDLL"
EXPORTS
HelloWorld
サンプルDLLを書く
Microsoft Visual C ++ 6.0では、 Win32 Dynamic-Link Library プロジェクトタイプまたは* MFC AppWizard(dll)*プロジェクトタイプのいずれかを選択して、DLLを作成できます。
次のコードは、Win32 Dynamic-Link Libraryプロジェクトタイプを使用して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
サンプルDLLの呼び出し
次のコードは、SampleDLL DLLのエクスポートされた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インポートライブラリをリンクする必要があります。
ランタイムダイナミックリンクでは、次のコードに類似したコードを使用して、エクスポートされたDLL関数SampleDLL.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を検索します-
- アプリケーションフォルダー
- 現在のフォルダ
- Windowsシステムフォルダー( GetSystemDirectory 関数はWindowsシステムフォルダーのパスを返します)。
- Windowsフォルダー( GetWindowsDirectory 関数はWindowsフォルダーのパスを返します)。