第124章 dllからダイアログボックスを出す


今回は、dllからダイアログボックスを出します。 そして、このダイアログボックスに入力した文字列を メモリマップトファイルに書き込んで他のプロセスからも アクセスできるようにします。



まず、メインのプログラムを複数起動しておきます。 そのうちの一つのプログラムのメニューから「ダイアログボックス」 「文字列の入力」を選択すると・・・



左のようなダイアログボックスが出てきます。 これに、文字列を入力して「OK」ボタンを押すと 起動しているプログラムのすべてのクライアント領域に この文字列が表示されます。



さて、前章でもそうでしたが複数のプロセスからアクセスできるということは 同時に読み書きなどが起こってしまうことも考えられます。 従って排他制御の仕組みが必要と思われますが、プログラムを 簡略にするためにこの仕組みを導入していません。腕に自信のある人は 是非やってみてください。

まずは、dll側から見ていくことにしましょう。

// dll05.h #define EXPORT extern "C" __declspec(dllexport) EXPORT void CALLBACK str_cat(char *); EXPORT BOOL CALLBACK Dlg(HWND);

ヘッダファイルです。単に関数の宣言をしているだけです。

// dll05.cpp #include <windows.h> #include <windowsx.h> #include "dll05.h" #include "resource.h" HANDLE hStr; char *lpMapAddress; HINSTANCE hInstance; LRESULT CALLBACK MyDlgProc(HWND, UINT, WPARAM, LPARAM); int WINAPI DllMain(HINSTANCE hInst, DWORD fdReason, PVOID pvReserved) { switch (fdReason) { case DLL_PROCESS_ATTACH: hInstance = hInst; hStr = CreateFileMapping((HANDLE)0xFFFFFFFF, NULL, PAGE_READWRITE, 0, 1024, "myownobject"); if (hStr == 0) { MessageBox(NULL, "失敗です", "失敗", MB_OK); return FALSE; } lpMapAddress = (char *)MapViewOfFile(hStr, FILE_MAP_ALL_ACCESS, 0, 0, 0); if (lpMapAddress == NULL) { MessageBox(NULL, "MapViewOfFile失敗", "失敗", MB_OK); return FALSE; } break; case DLL_PROCESS_DETACH: if(UnmapViewOfFile(lpMapAddress) != 0) MessageBox(NULL, "UnmapViewOfFile成功", "ok", MB_OK); if (CloseHandle(hStr) != 0) MessageBox(NULL, "ハンドルクローズ成功", "OK", MB_OK); break; default: break; } return TRUE; }

今まで作ってきたdllのDllMain関数は何もしていませんでした。 今回はちゃんと仕事をしています。ウィンドウズはdllに対して 重要なイベントの発生を DLL_PROCESS_ATTACH, DLL_PROCESS_DETACH, DLL_THREAD_ATTACH, DLL_THREAD_DETACHのいずれかで知らせます。 (これがDllMainの2番目の引数fdReasonです。) プロセスが終了(またはライブラリの開放)したりすると前2者 がきます。
プロセスが新しいスレッドを作成したり、スレッドが 終了しようとしているときに後2者が来ます。 今回は前2者のみを処理します。

dllが新しいプロセスにアタッチしようとしている時にメモリマップトファイルを 作り、マッピングをしています。

また、プロセスが終了しようとするときこれらを解放しています。

また、DllMainの最初のところで第1引数をグローバル変数に コピーしています。ダイアログボックスを作るときには このインスタンスハンドルを使わないと失敗します。

EXPORT void CALLBACK str_cat(char *str) { if (strcmp(lpMapAddress, "") == 0) { strcpy(str, "文字列なし"); } else { strcpy(str, lpMapAddress); } }

これは、単にlpMapAddressをstrにコピーしているだけの 関数です。

EXPORT BOOL CALLBACK Dlg(HWND hWnd) { if(DialogBox(hInstance, "MYDLG", hWnd, (DLGPROC)MyDlgProc) == -1) { MessageBox(NULL, "ダイアログ失敗", "OK", MB_OK); return FALSE; } return TRUE; }

これは、ダイアログボックスを呼び出す関数です。 注意すべき点は、DialogBoxの第1引数はDllMainでもらった インスタンスハンドルを使うという点です。うっかりDlg(HINSTANCE hInstance, ...) などとして呼び出し側からインスタンスハンドルをもらうと失敗します。 それと、親のウィンドウハンドルは呼び出し側からもらいます。 これをNULLにしてもダイアログボックスは出てきますが 複数のダイアログボックスが出てしまうことがあります。 (つまり、ダイアログボックスが出ている間もメニューの「文字列の入力」 を選択できてしまいます。)つまらないことですが、このへんで まちがうと結構バグ探しが面倒です。

LRESULT CALLBACK MyDlgProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp) { char dlgstr[1024]; switch (msg) { case WM_COMMAND: switch (LOWORD(wp)) { case IDOK: Edit_GetText(GetDlgItem(hDlg, IDC_EDIT1), dlgstr, sizeof(dlgstr)); strcpy(lpMapAddress, dlgstr); EndDialog(hDlg, IDOK); return TRUE; case IDCANCEL: EndDialog(hDlg, IDCANCEL); return TRUE; } break; } return FALSE; }

ダイアログボックスのプロシージャです。 これは、今までのダイアログプロシージャと全く同じ書き方です。 OKボタンが押されたらエジットコントロールの内容を dlgstrに取り込みます。そしてstrcpy関数でlpMapAddressに 書き込みます。

// dll05.rcの一部 ///////////////////////////////////////////////////////////////////////////// // // Dialog // MYDLG DIALOG DISCARDABLE 0, 0, 131, 63 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "文字列入力" FONT 9, "MS Pゴシック" BEGIN DEFPUSHBUTTON "OK",IDOK,7,42,50,14 PUSHBUTTON "キャンセル",IDCANCEL,73,42,50,14 EDITTEXT IDC_EDIT1,7,7,118,25,ES_AUTOHSCROLL END

これは、ごく普通のダイアログリソースです。

dllのビルドの仕方を復習すると(VC++5.0の場合)

1.「ファイル」「新規作成」で「プロジェクト」タブを選択する 2.プロジェクトの種類をWin32 Dynamic-Link Libraryにする 3.適当なプロジェクト名(ここではdll05)を付けて「OK」ボタンを押す 4.ソースファイル(dll05.cpp)を作成してプロジェクトに参加させる 5.ヘッダファイル(dll05.h)を作成して4.と同じディレクトリに保存する 6.リソースエジタでダイアログボックスを作り保存する(dll05.rc) 7.6.で作ったリソース・スクリプトをプロジェクトに参加させる 8.「リリース」または「デバッグ」のどちらかに決める 9.ビルドボタンを押す

これで、dll05.dllができます。また、この時dll05.libという ファイルもできますが、これは後で使います。

では、呼び出し側のプログラムを見てみましょう。

// dll05test.rcの一部 ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "終了(&X)", IDM_END END POPUP "ダイアログボックス(&D)" BEGIN MENUITEM "文字列入力(&I)", IDM_DLG END END

何の変哲もないメニューリソースです。

// dll05test.cpp #define STRICT #define ID_MYTIMER 32767 #include <windows.h> #include "dll05.h" #include "resource.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); BOOL InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); char szClassName[] = "dll05test"; //ウィンドウクラス 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; }

dllを作ったときに作ったdll05.hをインクルードしています。 dll05.hをこのプロジェクトのディレクトリにコピーしておきます。 あとは、いつもと同じです。

//ウィンドウ・クラスの登録 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 = "MYMENU"; //メニュー名 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座標 220,//幅 150,//高さ NULL,//親ウィンドウのハンドル、親を作るときはNULL NULL,//メニューハンドル、クラスメニューを使うときはNULL hInst,//インスタンスハンドル NULL); if (!hWnd) return FALSE; ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; }

これも、いつもと同じですがウィンドウの最初の大きさを指定しています。

//ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id; HDC hdc; PAINTSTRUCT ps; char szStr[1024]; switch (msg) { case WM_CREATE: SetTimer(hWnd, ID_MYTIMER, 500, NULL); break; case WM_TIMER: InvalidateRect(hWnd, NULL, TRUE); break; case WM_PAINT: hdc = BeginPaint(hWnd, &ps); str_cat(szStr); TextOut(hdc, 0, 30, szStr, strlen(szStr)); EndPaint(hWnd, &ps); break; case WM_COMMAND: switch (LOWORD(wp)) { case IDM_END: SendMessage(hWnd, WM_CLOSE, 0, 0); break; case IDM_DLG: Dlg(hWnd); break; } break; case WM_CLOSE: id = MessageBox(hWnd, "終了してもよいですか", "終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { DestroyWindow(hWnd); } break; case WM_DESTROY: KillTimer(hWnd, ID_MYTIMER); PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0L; }

ウィンドウができたらすぐにSetTimer関数で0.5秒ごとに WM_TIMERメッセージを発生させています。

WM_TIMERメッセージが来たらクライアント領域を 再描画させています。

WM_PAINTメッセージが来たらdllのstr_cat関数を呼んで szStrにマッピングされた文字列をコピーします。 (str_catという命名は失敗でした!最初はstrcat関数の ような文字列を追加する関数を考えていた・・・) そのあと、それをTextOutで描画しています。

さて、メニューの「文字列の入力」が選択されたら dllのDlg関数を呼んでダイアログボックスを表示させます。

WM_DESTROYの時に忘れずにタイマーを殺します。

今回は、簡単なようでいざ作ってみると結構 面倒な部分があります。


[SDK第2部 Index] [総合Index] [Previous Chapter] [Next Chapter]

Update 10/May/1998 By Y.Kumei
当ホーム・ページの一部または全部を無断で複写、複製、 転載あるいはコンピュータ等のファイルに保存することを禁じます。