[C++] メッセージボックスを親ウィンドウの中央に表示
メッセージボックス(MessageBox)は標準でディスプレイの中央に表示される挙動ですが、メッセージボックスを親ウィンドウの中央に表示したい場合など任意の位置に表示させるには、フックを使うことでメッセージウィンドウの位置を調整することができます。
メッセージボックスのフックを行うにはフックプロシージャと言われる関数を作成し、メッセージボックスを表示する直前で SetWindowsHookEx 関数に引数としてフックプロシージャを渡します。
こうするとメッセージボックスが表示される直前で、フックプロシージャがシステムから呼び出されます(nCode = HCBT_ACTIVATEとして呼び出される)ので、そこでメッセージウィンドウの位置を調整します。ここで忘れてはいけないのがフックを解除(UnhookWindowsHookEx)することです。フックの解除しないと対象のメッセージボックス以外のウィンドウに対してもフックプロシージャが呼び出されてしまいます。
#pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") #include <windows.h> TCHAR szClassName[] = TEXT("Window"); HHOOK g_hHook; // フックプロシージャ LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam) { if (nCode == HCBT_ACTIVATE) { // フックを解除する UnhookWindowsHookEx(g_hHook); const HWND hMessageBox = (HWND)wParam; const HWND hParentWnd = GetParent(hMessageBox); RECT rectMessageBox, rectParentWnd; GetWindowRect(hMessageBox, &rectMessageBox); GetWindowRect(hParentWnd, &rectParentWnd); SetWindowPos( hMessageBox, hParentWnd, (rectParentWnd.right + rectParentWnd.left - rectMessageBox.right + rectMessageBox.left) >> 1, (rectParentWnd.bottom + rectParentWnd.top - rectMessageBox.bottom + rectMessageBox.top) >> 1, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); } return 0; } LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { static HWND hButton; switch (msg) { case WM_CREATE: hButton = CreateWindow(TEXT("BUTTON"), TEXT("メッセージボックスを表示"), WS_VISIBLE | WS_CHILD, 0, 0, 0, 0, hWnd, (HMENU)IDOK, ((LPCREATESTRUCT)lParam)->hInstance, 0); break; case WM_SIZE: MoveWindow(hButton, 10, 10, 256, 32, TRUE); break; case WM_COMMAND: if (LOWORD(wParam) == IDOK) { // フックを有効にする g_hHook = SetWindowsHookEx(WH_CBT, CBTProc, 0, GetCurrentThreadId()); MessageBox(hWnd, TEXT("親ウィンドウの中央に表示"), TEXT("確認"), 0); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, msg, wParam, lParam); } return 0; } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPreInst, LPSTR pCmdLine, int nCmdShow) { MSG msg; WNDCLASS wndclass = { CS_HREDRAW | CS_VREDRAW, WndProc, 0, 0, hInstance, 0, LoadCursor(0,IDC_ARROW), (HBRUSH)(COLOR_WINDOW + 1), 0, szClassName }; RegisterClass(&wndclass); HWND hWnd = CreateWindow( szClassName, TEXT("メッセージボックスを親ウィンドウの中央に表示"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, 0, 0, hInstance, 0 ); ShowWindow(hWnd, SW_SHOWDEFAULT); UpdateWindow(hWnd); while (GetMessage(&msg, 0, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return (int)msg.wParam; }
また、C# で実装すると下記のようになります。
using System; using System.Windows.Forms; using System.Runtime.InteropServices; namespace WindowsFormsApplication1 { public partial class Form1 : Form { // Win32 APIのインポート [DllImport("user32.dll")] private static extern IntPtr GetParent(IntPtr hWnd); [DllImport("user32.dll")] private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect); [DllImport("user32.dll")] private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags); [DllImport("user32.dll")] private static extern bool UnhookWindowsHookEx(IntPtr hHook); [DllImport("kernel32.dll")] private static extern IntPtr GetCurrentThreadId(); [DllImport("user32.dll")] private static extern IntPtr SetWindowsHookEx(int idHook, HOOKPROC lpfn, IntPtr hInstance, IntPtr threadId); private delegate IntPtr HOOKPROC(int nCode, IntPtr wParam, IntPtr lParam); // 定数の定義 private const int HCBT_ACTIVATE = 5; private const int SWP_NOSIZE = 0x0001; private const int SWP_NOZORDER = 0x0004; private const int SWP_NOACTIVATE = 0x0010; private const int WH_CBT = 5; // 変数 private IntPtr m_hHook; // RECT の定義 private struct RECT { public int left; public int top; public int right; public int bottom; } private IntPtr CBTProc(int nCode, IntPtr wParam, IntPtr lParam) { if (nCode == HCBT_ACTIVATE) { // フックを解除する。 UnhookWindowsHookEx(m_hHook); IntPtr hMessageBox = wParam; IntPtr hParentWnd = GetParent(hMessageBox); RECT rectParentWnd, rectMessageBox; GetWindowRect(hMessageBox, out rectMessageBox); GetWindowRect(hParentWnd, out rectParentWnd); SetWindowPos( hMessageBox, hParentWnd, (rectParentWnd.right + rectParentWnd.left - rectMessageBox.right + rectMessageBox.left) >> 1, (rectParentWnd.bottom + rectParentWnd.top - rectMessageBox.bottom + rectMessageBox.top) >> 1, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); } return IntPtr.Zero; } public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { m_hHook = SetWindowsHookEx(WH_CBT, CBTProc, IntPtr.Zero, GetCurrentThreadId()); MessageBox.Show("親ウィンドウの中央に表示", "確認"); } } }