【问题标题】:Implementing callback function for dialog-based application为基于对话框的应用程序实现回调函数
【发布时间】:2015-08-23 14:48:03
【问题描述】:

我正在阅读一篇关于如何在 Visual C++ 中创建基于对话框的 GUI 应用程序的非常古老的教程(source - 葡萄牙语)。基于我对 WinAPI 编程的薄弱知识,我决定修改提议的代码主要是为了实现两件事:

1) 与 Unicode 编程标准保持一致(例如使用 wWinMainMessageBoxW 而不是 WinMainMessageBox/MessageBoxA)。这样的“标准”似乎在各地都得到了执行。示例:this question (comments)

2)在创建非空Win32 项目时与 Visual Studio 呈现的模型保持一致。我这样做并注意到:

  • “关于”对话框的回调函数类型为INT_PTR,而不是BOOL
  • WndProc 函数返回 0,而不是教程中的 TRUE(即 1);
  • msg 变量的 switch 语句默认返回 DefWindowProc() 而不是 FALSE;
  • 未指定 WM_CLOSE 处理(我猜对话框对某些事件有默认处理)。

_
结果,出现了一个奇怪的行为,即名为 Confirm 的 MessageBox 失焦 - 即我无法单击 OK 和 Cancel 按钮。

问题:我的假设是否正确,即 Visual Studio 生成的模板代码比教程中的代码更正确,这对我来说似乎不可信?如果是,我是不是忘了什么?我的代码有什么问题,为什么我不能点击 Messagebox 按钮?

我的最终代码如下:

#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include "resource.h"

INT_PTR CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    case WM_COMMAND:
        switch (LOWORD(wParam))
        {
        case IDOK:
            if (MessageBoxW(hWnd, L"Close?", L"Confirm", MB_OKCANCEL) == IDOK)
                DestroyWindow(hWnd);
            break;
        // more code to place here
        }
        break;
    default:
        return DefWindowProc(hWnd, msg, wParam, lParam);
    }
    return 0;
}

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
{
    int ret = DialogBoxW(hInstance, MAKEINTRESOURCEW(IDD_DIALOG1), NULL, WndProc);

    return 0;
}

为了完整起见,下面是基于教程的代码,实际上可以正常工作:

BOOL CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_INITDIALOG:
        return TRUE;
    case WM_CLOSE:
        DestroyWindow(hWnd);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    case WM_COMMAND:
        switch (LOWORD(wParam))
        {
        case IDOK:
            if (MessageBoxW(hWnd, L"Close?", L"Confirm", MB_OKCANCEL) == IDOK)
                DestroyWindow(hWnd);
            break;
        }
        break;
    default:
        return FALSE;
    }

    return TRUE;
}

【问题讨论】:

  • 关于你的第2点,对话框函数与窗口过程略有不同(对话框函数由对话框窗口的窗口过程调用)。也有一些演变。对话框函数返回有些棘手; &lt;windowsx.h&gt; 提供了一个处理事情的宏,但它大多没有记录。
  • 教程代码不正确,构建64位版本时会随机失败。您的代码不正确,消息框失败,因为您正在调用 DefWindowProc()。对话过程不应该这样做。只需删除该代码即可解决您的问题。检查this answer
  • 您真正需要的只是将 WndProc 的名称更改为 DialogProc 之类的名称,以防止将来出现混淆。 (我认为 Hans Passant 指的是原始函数具有旧式 BOOL 返回类型,而不是较新的 INT_PTR 返回类型。但我认为它在针对 64 位时实际上不会产生影响。)
  • 操作系统期望函数返回一个 64 位值。使用 BOOL 只会返回 32 位值。高 32 位往往是 0 意外,但这不是保证。如果不是,则完全无法诊断出故障。
  • @HansPassant 不,优化器不会按照您的建议执行。在任何情况下,唯一可以在对话过程中合法返回的内容是(取决于消息)TRUE 或 FALSE、转换为 BOOL 的值或转换为 INT_PTR 的 GDI 画笔句柄。这些都是 32 位值,包括 Win64 下的 GDI 句柄。操作系统从不期望返回 64 位值。

标签: c++ c visual-studio user-interface winapi


【解决方案1】:

所以,我明白了。 Visual Studio 向导创建的代码确实演示了 Windows API 的正确实现。

注意事项:

  • 对话框过程不应调用DefWindowProc()。这就是为什么 MessageBox 不工作的原因,正如 Hans Passant 在 cmets 中所指出的那样。

  • 顾名思义,VS 生成的WndProc() 函数的目的是为主应用程序窗口处理消息。还有另一个函数About(),它处理“关于”对话框的消息。

  • 对话框过程的返回类型是INT_PTR,这是对BOOL的改进,因为它通过在64位环境中具有不同的大小来解决平台差异,避免便携性问题。

  • switch 块不需要default 子句:

    与窗口过程不同,对话框过程从不调用 DefWindowProc 函数。相反,如果它处理一个 消息,否则为 FALSE。
    (Dialog Box Programming Considerations - MSDN)

  • 对话框对事件有默认处理:

    (DialogBox) 函数 (...) 启动自己的消息循环以检索 并为对话框发送消息。
    (DialogBox function - MSDN)

    没有必要像我一样处理WM_DESTROY - 甚至WM_DESTROY。其实这样做可能会出问题as seen here

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-25
    • 1970-01-01
    • 1970-01-01
    • 2015-07-21
    相关资源
    最近更新 更多