S-JIS[2006-07-27/2007-09-28] 変更履歴

DLL作成方法・呼出方法

DLLの作成方法。(VC++4.0と6.0は、内容は同じ)


(VC++4.0 MFC)プロジェクトの作成

  1. メニューバーの「ファイル(F)」→「新規作成(N)」で「新規作成」ダイアログを開く。
  2. 「作成するファイルの種類(N)」で「プロジェクトワークスペース」を選んでOKボタンを押す。
  3. 「タイプ(T)」に「MFC AppWizard (dll)」を選ぶ。あとは通常のワークスペースの作り方と同じ。

この場合、通常のMFCアプリケーションと同じようにプロジェクト内にCxxxAppクラスが作られ、defファイルも用意される。


(VC++4.0 SDK)プロジェクトの作成

  1. メニューバーの「ファイル(F)」→「新規作成(N)」で「新規作成」ダイアログを開く。
  2. 「作成するファイルの種類(N)」で「プロジェクトワークスペース」を選んでOKボタンを押す。
  3. 「タイプ(T)」に「Dynamic-Link Library」を選ぶ。あとは通常のワークスペースの作り方と同じ。

この場合、プロジェクト内にファイルが一つも用意されない。

適当にソースファイルを作り、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ファイルは、呼び出す側の実行時に必要。

これらのファイルは、(呼び出す側からすると)所定の場所に置かれている方が都合がいい。
ビルド後の事後作業として自動的にコピーする設定をしておくと便利。


DLL呼び出し方法

VC++からDLLを呼び出すには、暗黙的(静的)リンク明示的(動的)リンクの2通りの方法がある。
どちらの方式を採用するかは、仕様に応じて判断する。


DLL呼び出し方法(暗黙的・静的リンク)

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ファイル名や関数名をDLL用の関数(LoadLibraryGetProcAddressに渡して 関数ポインターを取得して使用する。[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』で確認できる。


なお、LoadLibraryGetProcAddressでエラーとなった場合は、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);
}

(VC++.NET)クラスライブラリの作成方法

.NETフレームワークにおいては、C#でもVB.NETでも同じクラスライブラリが使える。[2006-07-28]
クラスライブラリは実際には拡張子DLLのファイルとなるが、旧来のDLLとは互換性は無さげ。

  1. 「新しいプロジェクト」ダイアログを開く。
  2. 「プロジェクトの種類(P)」ペインで「Visual C++ プロジェクト」⇒「.NET」を選択する。
  3. 「テンプレート(T)」ペインで「クラス ライブラリ(.NET)」を選択する。

(VC++.NET)旧形式のDLL作成方法

VC++.NETでも、旧来の形式のDLLを作ることは出来る。[2006-07-28]

  1. 「新しいプロジェクト」ダイアログを開く。
  2. 「プロジェクトの種類(P)」ペインで「Visual C++ プロジェクト」⇒「Win32」を選択する。
  3. 「テンプレート(T)」ペインで「Win32 プロジェクト」を選択する。
  4. プロジェクト名を入力を入力する。
  5. 「Win32 アプリケーション ウィザード」ダイアログで「アプリケーションの設定」を選び、
    「アプリケーションの種類」は「DLL(D)」を選択する。
  6. (「追加のオプション」の「空のプロジェクト(E)」にチェック)
  7. 「完了」ボタンを押す。

コーディング方法は、VC++4.0と同じ。

#include <windows.h>

BOOL APIENTRY DllMain( HANDLE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
	return TRUE;
}

DLLの補足へ行く / VC++ページへ戻る / 技術メモへ戻る
メールの送信先:ひしだま