第102章 ウィンドウタイトルを変更する


今回は、ちょっと変わったことをします。表題のようにウィンドウタイトル を変更するわけですが、自分のウィンドウではありません。 人様のウィンドウタイトルを変更するプログラムを考えます。

プログラムを実行すると左のようなウィンドウが出現します。 真ん中の二重丸にバッテン印にカーソルをあわせてドラッグします。 すると、この絵が消えてカーソルが二重丸バッテンになります。 ユーザーはこの絵がとれて、カーソルになったような錯覚に陥ります。 そして、タイトルを変更したいウィンドウのタイトルバーのところで ボタンを離します。すると・・

おおっ!VC++のタイトルが変わったぞ!

ということになります。

では、どのようにすればよいのでしょうか。自分のウィンドウの キャプションを設定するにはSetWindowText関数でできました。 この関数の第一引数はキャプションを変更するウィンドウのハンドルでした。 つまり、変更したいウィンドウのハンドルを取得すれば良いことがわかります。

では、どうすれば人のウィンドウのハンドルを取得できるのでしょうか。 いろいろな方法がありますが簡単なのがWindowFromPoint関数です。

HWND WindowFromPoint( POINT Point // point構造体 );

つまり、この関数でPoint構造体で示した座標にあるウィンドウのハンドルが 戻り値として取得できます。見えないウィンドウに対しては無効です。 また、同じ座標に複数のウィンドウがある場合は一番手前のものが取得されます。

では、この座標を取得するにはどうしたらよいでしょうか。 前章でやったマウスキャプチャーを使えばいいですね。 これだけ、わかればすぐに作れます。

左の図のようなビットマップと、カーソルをリソースエジタで作っておきます。 名前は"MYBMP", "MYCURSOR"とでもしておきましょう。また、マウスカーソルを 作ったときホットスポットをバッテン印の 中心にしておきます。

// mcap02.rcの一部です ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "終了(&X)", IDM_END END END ///////////////////////////////////////////////////////////////////////////// // // Cursor // MYCURSOR CURSOR DISCARDABLE "mycursor.cur" ///////////////////////////////////////////////////////////////////////////// // // Bitmap // MYBMP BITMAP DISCARDABLE "bitmap1.bmp"

別に何の変哲もないリソース・スクリプトです。

// mcap02.cpp #define STRICT #include <windows.h> #include "resource.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); BOOL InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); char szClassName[] = "mcap02"; //ウィンドウクラス BOOL bCap = FALSE; HINSTANCE hInst; //インスタンスハンドル 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 = NULL; 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 hInstance, int nCmdShow) { HWND hWnd; hWnd = CreateWindow(szClassName, "猫でもわかるマウスキャプチャー",//タイトルバーにこの名前が表示されます WS_OVERLAPPEDWINDOW, //ウィンドウの種類 CW_USEDEFAULT, //X座標 CW_USEDEFAULT, //Y座標 250, //幅 150, //高さ NULL,//親ウィンドウのハンドル、親を作るときはNULL NULL,//メニューハンドル、クラスメニューを使うときはNULL hInst,//インスタンスハンドル NULL); if (!hWnd) return FALSE; hInst = hInstance; //インスタンスハンドルを保存 ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; }

ここも、いつもと同じです。

//ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id; POINTS pts; POINT pt; HWND hTarget; HDC hdc, hdc_mem; PAINTSTRUCT ps; HBITMAP hBit; BITMAP bmp_info; static int wx, wy; RECT rc; switch (msg) { case WM_PAINT: if (!bCap) { GetClientRect(hWnd, &rc); hdc = BeginPaint(hWnd, &ps); hBit = LoadBitmap(hInst, "MYBMP"); GetObject(hBit, sizeof(BITMAP), &bmp_info); wx = bmp_info.bmWidth; wy = bmp_info.bmHeight; hdc_mem = CreateCompatibleDC(hdc); SelectObject(hdc_mem, hBit); BitBlt(hdc, (rc.right - wx)/2, (rc.bottom-wy)/2, wx, wy, hdc_mem, 0, 0, SRCCOPY); DeleteDC(hdc_mem); DeleteObject(hBit); EndPaint(hWnd, &ps); } break; case WM_COMMAND: switch(LOWORD(wp)) { case IDM_END: SendMessage(hWnd, WM_CLOSE, 0, 0L); break; } break; case WM_LBUTTONDOWN: bCap = TRUE; SetCapture(hWnd); SetCursor(LoadCursor(hInst, "MYCURSOR")); InvalidateRect(hWnd, NULL, TRUE); break; case WM_MOUSEMOVE: if (bCap) { SetCursor(LoadCursor(hInst, "MYCURSOR")); } else SetCursor(LoadCursor(NULL, IDC_ARROW)); break; case WM_LBUTTONUP: SetCursor(LoadCursor(NULL, IDC_ARROW)); pts = MAKEPOINTS(lp); pt.x = pts.x; pt.y = pts.y; ClientToScreen(hWnd, &pt); hTarget = WindowFromPoint(pt); if (hTarget == NULL) { MessageBox(hWnd, "失敗です", "失敗", MB_OK); return (DefWindowProc(hWnd, msg, wp, lp)); } SetWindowText(hTarget, "猫でもわかるプログラミング"); ReleaseCapture(); bCap = FALSE; InvalidateRect(hWnd, NULL, TRUE); break; case WM_CREATE: SetCursor(LoadCursor(NULL, IDC_ARROW)); break; case WM_CLOSE: id = MessageBox(hWnd, (LPCSTR)"終了してもよいですか", (LPCSTR)"終了確認", 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; }

まず、WM_PAINTメッセージの処理を考えましょう。キャプチャーが 行われていないときは、二重丸バッテンをクライアント領域の 真ん中に表示します。ビットマップリソースの表示方法を忘れた人は 第26章をみてください。

次に、このビットマップをクライアント領域の中央に表示するには まずクライアント領域の大きさを取得します。何度も出てきている GetClientRect関数(第5章)を使います。 ビットマップの大きさは作成時にすでにわかっているので、それをそのまま 使っても良いですし、サンプルのプログラムのようにGetObject関数 (第26章)を使っても良いです。 クライアント領域の大きさがcx, cy ビットマップの大きさがwx, wy とすると((cx-wx)/2, (cy-wy)/2)からビットマップを描画すれば 中央に来ます。

キャプチャーが行われているときは何もしません。

左ボタンが押されたら(WM_LBUTTONDOWN)bCapをTRUEにして、 マウスキャプチャーを開始します。 そして、カーソルを自分で用意した二重丸バッテンにします。 このままでは、クライアント領域が更新されないのでInvalidateRect関数を 実行します。WM_PAINTのところではbCapがTRUEの時は何もしないので ビットマップが消えます。ユーザーにはクライアント領域のビットマップが 剥がれて(?)カーソルになったように見えます。

マウスが移動中(WM_MOUSEMOVE)の時は、キャプチャー中は カーソルを二重丸バッテンに、そうでないときは普通のカーソルに します。このWM_MOUSEMOVWメッセージの処理は不要に思われるかも しれませんが、これがないとちょっと困ったことが起きます。 マウスをドラッグして自分のウィンドウの境界のところで ボタンを離します。そして、マウスポインタをまた自分のクライアント 領域に戻すとどうなるでしょうか。このような動作を行うと マウスカーソルは両端に矢印のついたサイズ変更時のカーソル形状の ままになってしまいます。実験するとすぐにわかります。

ボタンが離されたときは(WM_LBUTTONUP)カーソルを元に戻します。 そして、LPARAMを調べてマウスの位置を取得します。 この時得られる座標はクライアント座標です。 これをスクリーン座標に変換する必要があります。 サンプルでは

pts = MAKEPOINTS(lp); pt.x = pts.x; pt.y = pts.y; ClientToScreen(hWnd, &pt);

のように行っています。POINTS構造体のメンバはshort型で POINT構造体のメンバはlong型です。long型の変数に short型の値を代入しても問題はないのであえて型キャストは していません。これでptにスクリーン座標が格納されます。 その後、WindowFromPoint(pt);で目的のウィンドウの ハンドルを取得することができます。 あとは、SetWindowText関数を実行すればよいですね。 そして、キャプチャを 終了します。bCapをFALSEにして自分のウィンドウの クライアント領域を再描画します。bCapがFALSEなので クライアント領域には二重丸バッテンが描画されます。

さて、この二重丸バッテン印はどこかで見たことがないですか。 じつは、これはVC++におまけで付いてくる「スパイ」に動作を 似せて作っています。(多分実際にやっていることは、このサンプルとは 違うと思いますが・・・)さて、今回の応用として、人のウィンドウに ちゃっかり時計を表示するプログラムなんかも作れるのではないでしょうか。 (このようなフリーソフトをどこかで見たことがあります。) 挑戦してみてください。


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

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