ソフトウェア開発 Win32プログラミング

アイコンドラッグでウィンドウを選択する

戻る


Spy++などで右のようにアイコンをドラッグしてウィンドウを選択するようなダイアログを見たことがあるだろうか。 今回はこれを実現する。

#include <windows.h>
#include <dlgs.h>

HINSTANCE g_hInstance;
HWND g_hMainWnd, g_hStc1;
HICON g_hIcon, g_hIconNull;
HCURSOR g_hCursor;
WNDPROC g_fnStc1OldWndProc;
HWND g_hwndOver, g_hwndOverOld, g_hwndSelected;
HPEN g_hPen;
RECT g_rc;
DWORD g_nDelayCount;
 
LRESULT CALLBACK
Stc1WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    RECT rc;
    POINT pt;
    switch(uMsg)
    {
    case WM_LBUTTONDOWN:
        SetCapture(hWnd);   // dragging starts
        SendMessage(g_hStc1, STM_SETICON, (WPARAM) g_hIconNull, 0);
        g_hwndOver = g_hwndOverOld = NULL;
        g_nDelayCount = 0;
        SetCursor(g_hCursor);
        break;
 
    case WM_MOUSEMOVE:
        if (GetCapture() != hWnd) break;
 
        if (g_nDelayCount < 8)    // delay hiding window
        {
            g_nDelayCount++;
            if (g_nDelayCount == 8)
            {
                GetWindowRect(g_hMainWnd, &g_rc);
                // move window to invisible area
                MoveWindow(g_hMainWnd, -32768, -32768, 0, 0, TRUE);
            }
            SetCursor(g_hCursor);
            break;
        }
        if (g_nDelayCount < 12)   // delay for redraw
        {
            g_nDelayCount++;
            SetCursor(g_hCursor);
            break;
        }
 
        GetCursorPos(&pt);
        g_hwndOverOld = g_hwndOver;
        g_hwndOver = WindowFromPoint(pt);
        if (g_hwndOverOld != g_hwndOver)
        {
            HWND hwndDesktop = GetDesktopWindow();
            HDC hdc = GetWindowDC(hwndDesktop);
            SetROP2(hdc, R2_XORPEN);
            SelectObject(hdc, g_hPen);
            SelectObject(hdc, GetStockObject(NULL_BRUSH));
            // erase previous box
            if (g_hwndOverOld != NULL)
            {
                GetWindowRect(g_hwndOverOld, &rc);
                InflateRect(&rc, -2, -2);
                Rectangle(hdc, rc.left, rc.top, rc.right, rc.bottom);
            }
            // draw box
            if (g_hwndOver != NULL)
            {
                GetWindowRect(g_hwndOver, &rc);
                InflateRect(&rc, -2, -2);
                Rectangle(hdc, rc.left, rc.top, rc.right, rc.bottom);
            }
            ReleaseDC(hwndDesktop, hdc);
        }
        SetCursor(g_hCursor);
        break;
 
    case WM_LBUTTONUP:
        if (GetCapture() != hWnd) break;
 
        ReleaseCapture();   // dragging ends
        if (g_hwndOver != NULL)
        {
            g_hwndSelected = g_hwndOver;
            TCHAR szClsName[20], szText[32], sz[20 + 32 + 32];
            GetClassName(g_hwndSelected, szClsName, 20);
            GetWindowText(g_hwndSelected, szText, 32);
            wsprintf(sz, TEXT("[%s]\r\n%s\r\nhWnd: 0x%08X"),
                szClsName, szText, g_hwndSelected);
            SetDlgItemText(g_hMainWnd, stc2, sz);
            EnableWindow(GetDlgItem(g_hMainWnd, IDOK), TRUE);
        }
        SendMessage(g_hStc1, STM_SETICON, (WPARAM) g_hIcon, 0);
        ShowWindow(g_hMainWnd, SW_SHOWNORMAL);  // activate
        break;
 
    case WM_CAPTURECHANGED:
        if (g_hwndOverOld != NULL)
        {
            // erase previous box
            HWND hwndDesktop = GetDesktopWindow();
            HDC hdc = GetWindowDC(hwndDesktop);
            SetROP2(hdc, R2_XORPEN);
            SelectObject(hdc, g_hPen);
            SelectObject(hdc, GetStockObject(NULL_BRUSH));
            GetWindowRect(g_hwndOverOld, &rc);
            InflateRect(&rc, -2, -2);
            Rectangle(hdc, rc.left, rc.top, rc.right, rc.bottom);
            ReleaseDC(hwndDesktop, hdc);
        }
 
        // restore pos & size
        MoveWindow(g_hMainWnd, g_rc.left, g_rc.top,
            g_rc.right - g_rc.left, g_rc.bottom - g_rc.top, TRUE);
        break;
 
    default:
        return CallWindowProc(g_fnStc1OldWndProc, hWnd, uMsg, wParam, lParam);
    }
    return 0;
}
 
INT_PTR CALLBACK DialogProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    RECT rc;
    switch(uMsg)
    {
    case WM_INITDIALOG:
        g_hMainWnd = hDlg;
        g_hStc1 = GetDlgItem(hDlg, stc1);
 
        // adjust size
        SetRect(&rc, 0, 0, 32, 32);
        AdjustWindowRectEx(&rc, GetWindowLong(g_hStc1, GWL_STYLE),
            FALSE, GetWindowLong(g_hStc1, GWL_EXSTYLE));
        SetWindowPos(g_hStc1, NULL, 0, 0,
            rc.right - rc.left, rc.bottom - rc.top,
            SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOOWNERZORDER);
 
        g_hIcon = LoadIcon(g_hInstance, MAKEINTRESOURCE(2));
        g_hIconNull = LoadIcon(g_hInstance, MAKEINTRESOURCE(3));
        g_hCursor = LoadCursor(g_hInstance, MAKEINTRESOURCE(2));
        SendMessage(g_hStc1, STM_SETICON, (WPARAM) g_hIcon, 0);
 
        g_fnStc1OldWndProc = (WNDPROC) SetWindowLongPtr(
            g_hStc1, GWLP_WNDPROC, (LONG_PTR) Stc1WndProc);
        g_hwndSelected = NULL;
        EnableWindow(GetDlgItem(g_hMainWnd, IDOK), FALSE);
        g_hPen = CreatePen(PS_SOLID, 3, RGB(255, 255, 255));
        return TRUE;
 
    case WM_COMMAND:
        switch(LOWORD(wParam))
        {
        case IDOK:
        case IDCANCEL:
            SetWindowLongPtr(
                g_hStc1, GWLP_WNDPROC, (LONG_PTR) g_fnStc1OldWndProc);
            DeleteObject(g_hPen);
            EndDialog(hDlg, (INT) LOWORD(wParam));
            break;
        }
    }
    return FALSE;
}
 
INT WINAPI WinMain(
    HINSTANCE   hInstance,
    HINSTANCE   hPrevInstance,
    LPSTR       pszCmdLine,
    INT         nCmdShow)
{
    g_hInstance = hInstance;
    DialogBox(hInstance, MAKEINTRESOURCE(1), NULL, DialogProc);
    return 0;
}

まず、STATICコントロールでSS_NOTIFYスタイル、WS_EX_CLIENTEDGE拡張スタイルを設定しておく。 SS_NOTIFYスタイルは、WM_LBUTTONDOWNなどのメッセージを捕らえるためだ。 WS_EX_CLIENTEDGEを設定しておくと少しへこんだ感じになる。

さらにSTATICコントロールにアイコンを設定した後、サブクラス化する。 サブクラス化のウィンドウプロシージャでは、ドラッグしたときの処理を記述する。

WM_LBUTTONDOWNが来たら、何も描画しないアイコンを設定する。ここで単なるNULLを設定すると、へこんだ感じがなくなるので注意する。そしてSetCaptureでウィンドウから離れてもメッセージが来るようにする。

WM_MOUSEMOVEが来たらキャプチャーがあるか確認し、ダイアログを隠す。その後、カーソルの上にあるウィンドウの上を描画する。 ダイアログを隠すときは、ShowWindowを使うのではなくMoveWindowで見えない位置に移動させる。これはキャプチャーを成功させるためである。

WM_LBUTTONUPが来たら、カーソルの上にあるウィンドウを選ぶ。

Alt+Tabが押される、ReleaseCaptureが呼ばれるなどして、キャプチャーが解放されたら必ずWM_CAPTURECHANGEDが来るので、これを処理する。 WM_CAPTURECHANGEDでは、描画した枠を消し、ダイアログを元に戻す。

ソース: selwnd.zip


戻る

©片山博文MZ
katayama.hirofumi.mz@gmail.com

inserted by FC2 system