DLLの作成方法。(VC++4.0と6.0は、内容は同じ)
この場合、通常のMFCアプリケーションと同じようにプロジェクト内にCxxxAppクラスが作られ、defファイルも用意される。
この場合、プロジェクト内にファイルが一つも用意されない。
適当にソースファイルを作り、DllMain等をコーディングする。
#include <windows.h> extern "C" int APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) { switch(dwReason) { case DLL_PROCESS_ATTACH: //初期処理 break; case DLL_PROCESS_DETACH: //終了処理 break; } return 1; // OK }
__declspec(dllexport)
int __stdcall test(int a)
{
return a+1;
}
defファイルを使いたい場合は、新規ファイルで拡張子defのファイルを作り、そのファイルをプロジェクトへ追加してやる。
すると、ビルド時に自動的にそのdefファイルの定義が使われる。
このプロジェクトをビルドすると、dllファイルとlibファイルが生成される。
libファイルは、呼び出す側でDLLを暗黙的にリンクする際に使用する。
dllファイルは、呼び出す側の実行時に必要。
これらのファイルは、(呼び出す側からすると)所定の場所に置かれている方が都合がいい。
ビルド後の事後作業として自動的にコピーする設定をしておくと便利。
VC++からDLLを呼び出すには、暗黙的(静的)リンクと明示的(動的)リンクの2通りの方法がある。
どちらの方式を採用するかは、仕様に応じて判断する。
dllimportを使って呼びたい関数を宣言する。
この場合は、リンク時にlibファイルが必要となる。このファイルはDLL作成時に作られている(作っておく)ので、ソースファイルと同じディレクトリ(リンク時のカレントディレクトリ)に置いてやるか、環境変数LIBで指定され
るディレクトリに置く必要がある。
#include <stdio.h> #pragma comment(lib, "testDll.lib") __declspec(dllimport) int __stdcall test(int a); int main(int argc, char* argv[]) { int r = test(1); printf("%d\n",r); return 0; }
あと、当然ながら、実行時にはパスの通っている場所にdllファイルが必要。
これもlibファイルと同じくソースファイルと同じディレクトリに置くか、もしくはexeファイルと同じディレクトリに置いておけばよい。
(Developer Studioから実行するならどちらでもよいが、コンソールから実行するならexeファイルと同じ場所の方がいいかな?)
dllファイルは実行の準備段階でロードされる。dllファイルが見つからない 場合や(リンク時の)libファイルと整合性がとれていない場合は、実行されずにエラーとなる。
プログラム内で DLLファイル名や関数名をDLL用の関数(LoadLibrary・GetProcAddress)に渡して
関数ポインターを取得して使用する。[2006-08-22]
この場合はdllimport宣言は不要(そもそも関数宣言をしない)なので、libファイルも要らない。
#include <windows.h> #include <stdio.h> extern void printError(const char *name, int err); typedef int (__stdcall *FUNC_test)(int a); int main(int argc, char* argv[]) { HMODULE hDll = LoadLibrary("testDll.dll"); if (hDll == NULL) { printError("LoadLibrary", GetLastError()); return -1; } FUNC_test test = (FUNC_test)GetProcAddress(hDll, "test"); //関数名での指定 // FUNC_test test = (FUNC_test)GetProcAddress(hDll, (LPCSTR)1); //序数での指定 if (test == NULL) { printError("GetProcAddress", GetLastError()); return -2; } int rc = test(1); printf("%d\n",r); if (!FreeLibrary(hDll)) { printError("FreeLibrary", GetLastError()); return -3; } return 0; }
LoadLibraryの実行時にdllファイルを探しに行く。無ければNULLが返る。
→MSDNのLoadLibrary
関数ポインターの取得にはGetProcAddressを使う。これも関数が見つからない場合はNULLが返る。
→MSDNのGetProcAddress
返ってきた値には、本来の関数の型をtypedefしておいてキャストすると便利。(上記の例のFUNC_test)
(もちろん、DLLで定義されている実際の関数と同じシグニチャーでないと、暴走する)
GetProcAddressに指定する関数名は、DLLのソースコードに書いたものにはならない(ことが多い)。
どのような名称(あるいは序数)に変換されているかは、dumpbinや『Dependency
Walker』で確認できる。
なお、LoadLibraryやGetProcAddressでエラーとなった場合は、GetLastError()でエラーコードが取得できる。
以下のような関数を作っておくと、エラーコードからメッセージに変換して表示されるので分かりやすい。
void printError(const char *name, int err) { LPVOID lpMsgBuf; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char*)&lpMsgBuf, 0, NULL ); printf("%s: %d:%s\n", name, err, lpMsgBuf); LocalFree(lpMsgBuf); }
.NETフレームワークにおいては、C#でもVB.NETでも同じクラスライブラリが使える。[2006-07-28]
クラスライブラリは実際には拡張子DLLのファイルとなるが、旧来のDLLとは互換性は無さげ。
VC++.NETでも、旧来の形式のDLLを作ることは出来る。[2006-07-28]
コーディング方法は、VC++4.0と同じ。
#include <windows.h> BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { return TRUE; }