最近更新于 2023-02-02 10:14

环境

Windows 11 专业工作站版 22H2
Visual Studio 2022 专业版

使用 Unicode 字符集,C17 标准编译,64 位编译

添加对话框资源

先添加一个资源文件,再给它添加对话框资源
file

在设计界面有工具箱可以添加组件,这里主要是演示代码的调用,所以就用默认的就行
file

模式对话框

模式对话框会阻塞调用者,即弹出模式对话框后,原来的窗口就暂停了,也无法操作

#include <Windows.h>
#include "resource.h" // 引入对话框资源

HINSTANCE g_hInstance = {0}; // 用于保存当前实例句柄

/**
* @brief 对话框过程函数
* @param hWndDlg 对话框的窗口句柄
* @param msg 消息代码
* @param wParam 消息参数
* @param lParam 消息参数
* @return 返回 FALSE 调用内部默认处理
*/
INT_PTR CALLBACK DlgProc(HWND hWndDlg, UINT msg, WPARAM wParam, LPARAM lParam) // 和普通窗口的消息过程函数使用类似
{
    switch (msg)
    {
        case WM_INITDIALOG: // 相当于普通窗口的 WM_CREATE 消息
        {
            MessageBox(NULL, L"对话框初始化操作可以在这里执行", L"提醒", MB_OK);
            break;
        }
        case WM_COMMAND:
        {
            switch (LOWORD(wParam))
            {
                case IDOK:
                {
                    MessageBox(NULL, L"点击确定1", L"消息", MB_OK);
                    break;
                }
                case IDCANCEL:
                {
                    MessageBox(NULL, L"点击取消1", L"消息", MB_OK);
                    break;
                }
                default:
                {
                    break;
                }
            }
        }
        case WM_CLOSE:
        {
            // 销毁对话框窗口,同时解除阻塞
            EndDialog(hWndDlg,
                      LOWORD(wParam)); // DialogBox 的返回值,这里就将对话框按键状态传回去
            break;
        }
        default:
        {
            return FALSE;
        }
    }
    return TRUE;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
        case WM_LBUTTONUP: // 在窗口中点击鼠标打开对话框
        {
            // 创建对话框
            INT_PTR db = DialogBox(g_hInstance, MAKEINTRESOURCE(IDD_DIALOG1), hWnd, DlgProc);
            if (db == IDOK)
            {
                MessageBox(NULL, L"点击确定2", L"消息", MB_OK);
            }
            else if (db == IDCANCEL)
            {
                MessageBox(NULL, L"点击取消2", L"消息", MB_OK);
            }
            else
            {
                MessageBox(NULL, L"点击关闭2", L"消息", MB_OK);
            }
            break;
        }
        case WM_CLOSE:
        {
            DestroyWindow(hWnd);
            break;
        }
        case WM_DESTROY:
        {
            PostQuitMessage(0);
            break;
        }
        default:
        {
            return DefWindowProc(hWnd, msg, wParam, lParam);
        }
    }
    return 0;
}

int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPreInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow)
{
    g_hInstance = hInstance;

    PCWSTR szClassName = L"Desktop";
    WNDCLASSEX wc = {0};
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.lpszClassName = szClassName;
    wc.lpfnWndProc = WndProc;
    if (!RegisterClassEx(&wc))
    {
        MessageBox(NULL, L"注册窗口失败!", L"错误", MB_OK);
        return 1;
    }

    HWND hWnd = CreateWindowEx(0, szClassName, L"简单程序", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 300, 300, NULL, NULL, hInstance, NULL);
    if (!hWnd)
    {
        MessageBox(NULL, L"创建窗口失败!", L"错误", MB_OK);
        return 1;
    }

    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    MSG msg = {0};
    while (GetMessage(&msg, NULL, 0, 0))
    {
        DispatchMessage(&msg);
    }

    return (int)msg.wParam;
}

运行程序
file

在窗口空白处点以下左键触发弹出模式对话框
file
file

此时,“简单程序”框就无法操作了,分别点击确定和取消都会弹出两次消息框,一个消息框是在对话框过程函数中产生的,另一个消息框是根据创建对话框函数传出的返回值产生的。
file
file

非模式对话框

相比于模式对话框,主要区别就在弹出对话框后,原来的窗口依然在运行,在上面做操作也会有响应。比如可以调出多个同样的对话框,当然这里的代码做了禁止重复弹出的措施。将对话框句柄变量设为全局的,在创建对话框之前先判断这个句柄是否已经是一个窗口句柄了,不是才创建,是就不创建,避免弹出多个同样对话框的情况。

#include <Windows.h>
#include "resource.h" // 引入对话框资源

HINSTANCE g_hInstance = {0}; // 用于保存当前实例句柄
HWND g_hWndDlg = NULL; // 用于保存对话框句柄

/**
* @brief 对话框过程函数
* @param hWndDlg 对话框的窗口句柄
* @param msg 消息代码
* @param wParam 消息参数
* @param lParam 消息参数
* @return 返回 FALSE 执行内部默认操作
*/
INT_PTR CALLBACK DlgProc(HWND hWndDlg, UINT msg, WPARAM wParam, LPARAM lParam) // 和普通窗口的消息过程函数使用类似
{
    switch (msg)
    {
        case WM_INITDIALOG: // 相当于普通窗口的 WM_CREATE 消息
        {
            break;
        }
        case WM_COMMAND:
        {
            switch (LOWORD(wParam))
            {
                case IDOK:
                {
                    MessageBox(hWndDlg, L"点击确定", L"消息", MB_OK);
                    break;
                }
                case IDCANCEL:
                {
                    MessageBox(hWndDlg, L"点击取消", L"消息", MB_OK);
                    break;
                }
                default:
                {
                    break;
                }
            }
        }
        case WM_CLOSE:
        {
            // 销毁对话框窗口
            DestroyWindow(hWndDlg);
            break;
        }
        default:
        {
            return FALSE;
        }
    }
    return TRUE;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
        case WM_LBUTTONUP: // 在窗口中点击鼠标打开对话框
        {
            if (!IsWindow(g_hWndDlg)) // 防止重复弹出对话框
            {
                 g_hWndDlg = CreateDialog(g_hInstance, // 当前实例句柄
                                            MAKEINTRESOURCE(IDD_DIALOG1), // 对话框资源
                                            hWnd, // 父窗口句柄
                                            DlgProc); // 对话框过程函数
                ShowWindow(g_hWndDlg, SW_SHOW); // 需要手动设置显示
            }
            break;
        }
        case WM_CLOSE:
        {
            DestroyWindow(hWnd);
            break;
        }
        case WM_DESTROY:
        {
            PostQuitMessage(0);
            break;
        }
        default:
        {
            return DefWindowProc(hWnd, msg, wParam, lParam);
        }
    }
    return 0;
}

int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPreInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow)
{
    g_hInstance = hInstance;

    PCWSTR szClassName = L"Desktop";
    WNDCLASSEX wc = {0};
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.lpszClassName = szClassName;
    wc.lpfnWndProc = WndProc;
    if (!RegisterClassEx(&wc))
    {
        MessageBox(NULL, L"注册窗口失败!", L"错误", MB_OK);
        return 1;
    }

    HWND hWnd = CreateWindowEx(0, szClassName, L"简单程序", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 300, 300, NULL, NULL, hInstance, NULL);
    if (!hWnd)
    {
        MessageBox(NULL, L"创建窗口失败!", L"错误", MB_OK);
        return 1;
    }

    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    MSG msg = {0};
    while (GetMessage(&msg, NULL, 0, 0))
    {
        DispatchMessage(&msg);
    }

    return (int)msg.wParam;
}

file
现在可以直接点击主窗口的关闭停止程序,而模式对话框弹出时,主窗口无法操作,只能先关闭对话框后才能操作主窗口。
file

作者 IYATT-yx