たとえば、ユーザーがメニューからa, b, cの機能を 選択するとします。それぞれの機能はdllで実現しているとします。 機能cは滅多に選択されず、かつ膨大なプログラムが必要とします。 そうするとcの機能を実現するdllはユーザーがcを選択したときのみ ロードされるのが理想的ですね。 では、早速プログラムを見てみましょう。
dll側はたったこれだけです。TestFunc関数は引数の和を求めて それを戻り値として返しているだけです。// dll04.cpp #include <windows.h> #define EXPORT extern "C" __declspec(dllexport) EXPORT int TestFunc( int a, int b ) { return( a + b ); }
dllのビルド方法は今までと同じです。
では、呼び出し側ではどのようにすればよいのでしょうか。
なお、関数へのポインタはわかりにくいのでC言語編第35章 を参照してください。1.呼び出したい関数へのポインタをtypedefで定義 typedef int (*PTESTFUNC)(int, int); 2.dllをLoadLibrary関数でロードする hLib = LoadLibrary("dll04.dll"); 3.1.で定義した型の変数を宣言する PTESTFUNC fpTestFunc; 4.この変数にGetProcAddress関数を使って呼び出したい関数の アドレスを代入する fpTestFunc = (PTESTFUNC)GetProcAddress(hLib, "TestFunc"); 5.これを実行します (*fpTestFunc)(a, b);
では、呼び出し側のプログラムを作ってみましょう。
呼び出し側のプログラムでは今までのように、 libをリンクする必要はありません。// dll4test.cpp #define STRICT #include <windows.h> LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); BOOL InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); char szClassName[] = "dll4test"; //ウィンドウクラス int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { MSG msg; if (!InitApp(hCurInst)) return FALSE; if (!InitInstance(hCurInst, nCmdShow)) return FALSE; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; }
このへんはいつもと同じです。//ウィンドウ・クラスの登録 BOOL InitApp(HINSTANCE hInst) { WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; //プロシージャ名 wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst;//インスタンス wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = NULL; //メニュー名 wc.lpszClassName = (LPCSTR)szClassName; wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); return (RegisterClassEx(&wc)); } //ウィンドウの生成 BOOL InitInstance(HINSTANCE hInst, int nCmdShow) { HWND hWnd; hWnd = CreateWindow(szClassName, "猫でもわかるdll",//タイトルバーにこの名前が表示されます WS_OVERLAPPEDWINDOW, //ウィンドウの種類 CW_USEDEFAULT, //X座標 CW_USEDEFAULT, //Y座標 140,//幅 230,//高さ NULL,//親ウィンドウのハンドル、親を作るときはNULL NULL,//メニューハンドル、クラスメニューを使うときはNULL hInst,//インスタンスハンドル NULL); if (!hWnd) return FALSE; ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; }
dllのTestFunc関数のアドレスをfpTestFuncに代入して//ウィンドウプロシージャ typedef int (*PTESTFUNC)(int, int); LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id, x, i; HINSTANCE hMyLib; PTESTFUNC fpTestFunc; char str[64]; char *str_org = "%2d + %2d = %3d"; HDC hdc; PAINTSTRUCT ps; switch (msg) { case WM_PAINT: hMyLib = LoadLibrary( "dll04.DLL"); if (hMyLib) { fpTestFunc = (PTESTFUNC)GetProcAddress(hMyLib, "TestFunc"); if(fpTestFunc == NULL) { FreeLibrary(hMyLib); return (DefWindowProc(hWnd, msg, lp, wp)); } hdc = BeginPaint(hWnd, &ps); for (i = 0; i <=9; i++) { x = (*fpTestFunc)(i * 10, i + 10); wsprintf(str, str_org, i * 10, i + 10, x); TextOut(hdc, 0, i * 20, (LPCTSTR)str, strlen(str)); } EndPaint(hWnd, &ps); } else { return (DefWindowProc(hWnd, msg, lp, wp)); } FreeLibrary(hMyLib); break; case WM_CLOSE: id = MessageBox(hWnd, "終了してもよいですか", "終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0L; }
(*fpTestFunc)(a, b);
で実行していると考えるとわかりやすいですね。
左のようになれば大成功です。簡単なプログラムなのですが
どこかでつまずくとバグを探すのは結構大変です。
もちろんdllはこのプログラムから見えるところになくてはいけません。
Update Apr/27/1998 By Y.Kumei