では、どうすれば人のウィンドウのハンドルを取得できるのでしょうか。 いろいろな方法がありますが簡単なのがWindowFromPoint関数です。
つまり、この関数でPoint構造体で示した座標にあるウィンドウのハンドルが 戻り値として取得できます。見えないウィンドウに対しては無効です。 また、同じ座標に複数のウィンドウがある場合は一番手前のものが取得されます。HWND WindowFromPoint( 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; }
まず、WM_PAINTメッセージの処理を考えましょう。キャプチャーが 行われていないときは、二重丸バッテンをクライアント領域の 真ん中に表示します。ビットマップリソースの表示方法を忘れた人は 第26章をみてください。//ウィンドウプロシージャ 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; }
次に、このビットマップをクライアント領域の中央に表示するには まずクライアント領域の大きさを取得します。何度も出てきている 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を調べてマウスの位置を取得します。 この時得られる座標はクライアント座標です。 これをスクリーン座標に変換する必要があります。 サンプルでは
のように行っています。POINTS構造体のメンバはshort型で POINT構造体のメンバはlong型です。long型の変数に short型の値を代入しても問題はないのであえて型キャストは していません。これでptにスクリーン座標が格納されます。 その後、WindowFromPoint(pt);で目的のウィンドウの ハンドルを取得することができます。 あとは、SetWindowText関数を実行すればよいですね。 そして、キャプチャを 終了します。bCapをFALSEにして自分のウィンドウの クライアント領域を再描画します。bCapがFALSEなので クライアント領域には二重丸バッテンが描画されます。pts = MAKEPOINTS(lp); pt.x = pts.x; pt.y = pts.y; ClientToScreen(hWnd, &pt);
さて、この二重丸バッテン印はどこかで見たことがないですか。 じつは、これはVC++におまけで付いてくる「スパイ」に動作を 似せて作っています。(多分実際にやっていることは、このサンプルとは 違うと思いますが・・・)さて、今回の応用として、人のウィンドウに ちゃっかり時計を表示するプログラムなんかも作れるのではないでしょうか。 (このようなフリーソフトをどこかで見たことがあります。) 挑戦してみてください。
Update Jan/25/1998 By Y.Kumei