Win32 C++ 实现对话框居中显示

69 篇文章 0 订阅
44 篇文章 0 订阅

        使用 MessageBox 对话框显示信息时, 对话框位置总是在屏幕中间, 而不是主窗口的中间, 如何以最简单的方式将对话框移到父窗口中间呢?  那就是使用 CBT 钩子 , 在窗口创建完成前(窗口句柄已经创建完成), 修改窗口的位置, 即可实现对话框在父窗口上居中显示.

首先简单写一个 CBT 钩子类, 这个类暂且叫做 CMessageBoxCenter

MessageBoxCenter.h

#pragma once
#include <windows.h>

class CMessageBoxCenter
{

public:

    CMessageBoxCenter();
    ~CMessageBoxCenter();

    // 获取子窗口位于父窗口的居中位置
    static POINT GetChildWindowCenterPos(
        int nWidth,         //子窗口宽度
        int nHeight,        //子窗口高度
        HWND hParent        //父窗口句柄
    );

private:

    // WH_CBT 
    // 线程或全局
    // https://learn.microsoft.com/zh-cn/windows/win32/winmsg/cbtproc
    // 安装用于接收对 CBT 应用程序有用的通知的挂钩过程
    static LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam);

private:

    static HHOOK m_hHook;
};

MessageBoxCenter.cpp

#include "MessageBoxCenter.h"

HHOOK CMessageBoxCenter::m_hHook;

CMessageBoxCenter::CMessageBoxCenter()
{
    m_hHook = ::SetWindowsHookEx(WH_CBT, CBTProc, NULL, ::GetCurrentThreadId());
}

CMessageBoxCenter::~CMessageBoxCenter()
{
    ::UnhookWindowsHookEx(m_hHook);
}

POINT CMessageBoxCenter::GetChildWindowCenterPos(
    int nWidth,         //子窗口宽度
    int nHeight,        //子窗口高度
    HWND hParent        //父窗口句柄
)
{
    RECT rectParent = { 0 };
    LONG nParentW = 0;
    LONG nParentH = 0;

    // 如果父窗口句柄为空, 则以桌面窗口为父窗口
    if (NULL == hParent)
    {
        hParent = ::GetDesktopWindow();
    }

    // 获取并统计父窗口宽度和高度
    ::GetWindowRect(hParent, &rectParent);
    nParentW = rectParent.right - rectParent.left;
    nParentH = rectParent.bottom - rectParent.top;

    return {rectParent.left + (nParentW - nWidth) / 2, rectParent.top + (nParentH - nHeight) / 2};
}

LRESULT CALLBACK CMessageBoxCenter::CBTProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    // 即将创建一个窗口
    if (HCBT_CREATEWND == nCode)
    {
        HWND hWndNew = (HWND)wParam;
        LPCBT_CREATEWND pStruct = (LPCBT_CREATEWND)lParam;
        HWND hParent = pStruct->lpcs->hwndParent;

        // 检查是对话框, 则修改显示位置为相对父窗口居中位置
        if (32770 == ::GetClassLongPtr(hWndNew, GCW_ATOM))
        {
            POINT nPos = GetChildWindowCenterPos(pStruct->lpcs->cx, pStruct->lpcs->cy, hParent);
            pStruct->lpcs->x = nPos.x;
            pStruct->lpcs->y = nPos.y;
        }
    }

    return ::CallNextHookEx(m_hHook, nCode, wParam, lParam);
}

以下测试例子简单写了个对话框过程, 仅简单测试对话框

main.cpp

#include <windows.h>
#include <tchar.h>
#include <locale.h>
#include"resource.h"
#include "MessageBoxCenter.h"

#pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")

INT_PTR WINAPI DialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(
    _In_ HINSTANCE hInstance,
    _In_opt_ HINSTANCE hPrevInstance,
    _In_ LPSTR lpCmdLine,
    _In_ int nShowCmd)
{
    setlocale(LC_ALL, "");

    UNREFERENCED_PARAMETER(hInstance);
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);
    UNREFERENCED_PARAMETER(nShowCmd);

    WORD wID = ::DialogBoxParam(GetModuleHandle(NULL),  MAKEINTRESOURCE(IDD_DIALOG_FRAME), nullptr, DialogProc, (LPARAM)0);
    return wID;
}

INT_PTR WINAPI DialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    if (WM_INITDIALOG == uMsg)
    {

        return (INT_PTR)TRUE;
    }

    if (WM_CLOSE == uMsg)
    {
        ::EndDialog(hWnd, 0);
        return (INT_PTR)TRUE;
    }

    if (WM_COMMAND == uMsg)
    {
        //消息源   HIWORD(wParam)      LOWORD(wParam)     lParam 
        //菜单     0                   菜单ID             0
        //快捷键   1                   快捷键ID           0
        //控件     控件定义的通知码    控件ID             控件窗口句柄
        WORD wNotify = HIWORD(wParam);
        WORD wID = LOWORD(wParam);
        HWND hWndCtrl = (HWND)lParam;

        if (wID >= IDOK && wID <= IDNO)
        {
            ::EndDialog(hWnd, wID);
        }

        if (IDC_BUTTON_CBT == wID)
        {
            CMessageBoxCenter boxCenter;
            MessageBox(hWnd, _T("WH_CBT 钩子测试, 居中显示对话框"), _T("WH_CBT 钩子"), MB_OK);
        }

        return (INT_PTR)TRUE;
    }

    return (INT_PTR)FALSE;
}

resource.h

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ 生成的包含文件。
// 供 Win32Hook.rc 使用
//
#define IDD_DIALOG_FRAME                101
#define IDC_BUTTON_CBT                  1001

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        103
#define _APS_NEXT_COMMAND_VALUE         40001
#define _APS_NEXT_CONTROL_VALUE         1002
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

Win32Hook.rc

// Microsoft Visual C++ generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
/
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"

/
#undef APSTUDIO_READONLY_SYMBOLS

/
// 中文(简体,中国) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED

#ifdef APSTUDIO_INVOKED
/
//
// TEXTINCLUDE
//

1 TEXTINCLUDE 
BEGIN
    "resource.h\0"
END

2 TEXTINCLUDE 
BEGIN
    "#include ""winres.h""\r\n"
    "\0"
END

3 TEXTINCLUDE 
BEGIN
    "\r\n"
    "\0"
END

#endif    // APSTUDIO_INVOKED


/
//
// Dialog
//

IDD_DIALOG_FRAME DIALOGEX 0, 0, 309, 176
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "WinHook"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
    DEFPUSHBUTTON   "确定",IDOK,73,142,50,14
    PUSHBUTTON      "取消",IDCANCEL,181,141,50,14
    PUSHBUTTON      "WH_CBT 居中显示对话框",IDC_BUTTON_CBT,19,18,91,14
END


/
//
// DESIGNINFO
//

#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO
BEGIN
    IDD_DIALOG_FRAME, DIALOG
    BEGIN
        LEFTMARGIN, 7
        RIGHTMARGIN, 302
        TOPMARGIN, 7
        BOTTOMMARGIN, 169
    END
END
#endif    // APSTUDIO_INVOKED


/
//
// AFX_DIALOG_LAYOUT
//

IDD_DIALOG_FRAME AFX_DIALOG_LAYOUT
BEGIN
    0
END

#endif    // 中文(简体,中国) resources
/



#ifndef APSTUDIO_INVOKED
/
//
// Generated from the TEXTINCLUDE 3 resource.
//


/
#endif    // not APSTUDIO_INVOKED

对话框效果

未使用钩子效果

使用钩子后效果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值