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

メッセージボックスを中央に寄せる

戻る


メッセージボックスを親ウィンドウの中央に寄せる方法を以下に示す。

// メッセージボックス中央寄せ。
// Copyright (C) 2013 片山博文MZ.  All rights reserved.
#include <windows.h>

...

// メッセージボックスフック用。
static HHOOK s_hMsgBoxHook = NULL;

// ダイアログを中央によせる関数。
VOID WINAPI CenterDialog(HWND hwnd)
{
    // 子ウィンドウか?
    BOOL bChild = !!(GetWindowLong(hwnd, GWL_STYLE) & WS_CHILD);

    // オーナーウィンドウ(親ウィンドウ)を取得する。
    HWND hwndOwner;
    if (bChild)
        hwndOwner = GetParent(hwnd);
    else
        hwndOwner = GetWindow(hwnd, GW_OWNER);

    // オーナーウィンドウ(親ウィンドウ)の座標を取得する。
    // オーナーウィンドウ(親ウィンドウ)がないときはワークエリアを使う。
    RECT rc, rcOwner;
    if (hwndOwner != NULL)
        GetWindowRect(hwndOwner, &rcOwner);
    else
        SystemParametersInfo(SPI_GETWORKAREA, 0, &rcOwner, 0);

    // ウィンドウの座標をスクリーン座標で取得する。
    GetWindowRect(hwnd, &rc);

    // スクリーン座標で中央寄せの位置を計算する。
    POINT pt;
    pt.x = rcOwner.left +
        ((rcOwner.right - rcOwner.left) - (rc.right - rc.left)) / 2;
    pt.y = rcOwner.top +
        ((rcOwner.bottom - rcOwner.top) - (rc.bottom - rc.top)) / 2;

    // 子ウィンドウなら、スクリーン座標をクライアント座標に変換する。
    if (bChild && hwndOwner != NULL)
        ScreenToClient(hwndOwner, &pt);

    // ウィンドウの位置を設定する。
    SetWindowPos(hwnd, NULL, pt.x, pt.y, 0, 0,
        SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);

    // ワークエリアからはみでていたら修正する。
    SendMessage(hwnd, DM_REPOSITION, 0, 0);
}

// メッセージボックスフック用の関数。
LRESULT CALLBACK
MsgBoxCbtProc(INT nCode, WPARAM wParam, LPARAM lParam)
{
    if (nCode == HCBT_ACTIVATE)
    {
        // ウィンドウがアクティブ化されようとしている。
        // ウィンドウハンドルを取得。
        HWND hwnd = (HWND)wParam;

        // ウィンドウクラスの確認。
        TCHAR szClassName[MAX_PATH];
        GetClassName(hwnd, szClassName, MAX_PATH);
        if (lstrcmpi(szClassName, TEXT("#32770")) == 0)
        {
            // ダイアログだった。おそらくメッセージボックス。
            // 中央寄せする。
            CenterDialog(hwnd);

            // フックを解除する。
            if (s_hMsgBoxHook != NULL && UnhookWindowsHookEx(s_hMsgBoxHook))
                s_hMsgBoxHook = NULL;
        }
    }
    // allow the operation
    return 0;
}

// 中央寄せメッセージボックスを表示する。
INT WINAPI
CenterMessageBox(HWND hwnd, LPCTSTR pszText, LPCTSTR pszCaption, UINT uType)
{
    // フックされていたらフックを解除する。
    if (s_hMsgBoxHook != NULL && UnhookWindowsHookEx(s_hMsgBoxHook))
        s_hMsgBoxHook = NULL;

    // フックを開始。
    DWORD dwThreadID = GetCurrentThreadId();
    s_hMsgBoxHook = SetWindowsHookEx(WH_CBT, MsgBoxCbtProc, NULL, dwThreadID);

    // メッセージボックスを表示する。
    INT nID = MessageBox(hwnd, pszText, pszCaption, uType);

    // フックされていたらフックを解除する。
    if (s_hMsgBoxHook != NULL && UnhookWindowsHookEx(s_hMsgBoxHook))
        s_hMsgBoxHook = NULL;

    return nID;
}

...

メッセージボックスは、MessageBox APIで表示されるわけだが、グローバルフックで、 そのメッセージボックス(ダイアログでできている)の位置を修正している。

追記。澤田豊さんが、メールで連絡があり、いくつか修正すべきところを以下のように指摘された。

フックコードには HCBT_ACTIVATE よりも HCBT_CREATEWND を使った ほうが簡単でした。 LPARAM が CREATESTRUCT のアドレスなので、 直接ウインドウの位置を書き変えることができ、 変更した値でウインドウが作成されます。

次のフックプロシージャへのチェーン SetWindowsHookExの解説に書いてるのですが、 CallNextHookExを呼び出して終わった方が無難なようです。

同じスレッド用にプロセス内のコールバック関数を呼び出すのなら、 インスタンスを指定する必要は無いみたいです。 というか、NULL にしろと明記されてます。

これを受けて、インスタンスhInstをNULLにするように修正した。 そのほかのところは修正していない。

ソース: cmsgbox.zip


戻る

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

inserted by FC2 system