在聚焦时按下 Enter 键时,子窗口向意外的父窗口发送 WM_COMMAND 消息

问题描述 投票:0回答:1

已解决:

我在错误的窗口上拨打

IsDialogMessage

解决方案:

winapi 中子窗口编辑控件上的 WS_TABSTOP https://web.archive.org/web/20100926084558/http://support.microsoft.com/kb/71450


根据:

https://learn.microsoft.com/en-us/windows/win32/menurc/wm-command

什么消息导致按钮发送 WM_COMMAND 消息

(还有很多其他帖子)

窗口控件将其 WM_COMMAND 消息发送给其直接父级。但在这种情况下情况并非如此,我不明白为什么。仅当使用回车键(回车键)时才会发生这种情况。

/// Marcos:
#define IDC_BUTTON1 100;
#define IDC_BUTTON2 101;

/// Windows:
HWND g_hWnd;      // Main application window.
HWND g_Container; // Static window (Act like a container)
HWND g_Button1;   // Button 1
HWND g_Button2;   // Button 2
  1. 在WinMain入口,创建主窗口,没什么特别的:
g_hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
   CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
  1. 在WM_CREATE消息中:
// Button 1 is a direct child of the main window.
g_Button1 = CreateWindowExW(NULL, L"BUTTON", L"Button", WS_VISIBLE | WS_CHILD | WS_TABSTOP, 100, 100, 130, 40, g_hWnd, (HMENU)IDC_BUTTON1, NULL, NULL);
if (!g_Button1)
   exit(EXIT_FAILURE);

// Create the container window(static).
g_Container = CreateWindowExW(WS_EX_CONTROLPARENT, L"STATIC", L"", WS_VISIBLE | WS_CHILD | SS_NOPREFIX | WS_CLIPCHILDREN,
            300, 300, 500, 500, g_hWnd, NULL, NULL, NULL);
// Subclassing the container window.
if (!g_Container || !SetWindowSubclass(g_Container, WindowProcedure_Container, NULL, NULL))
   exit(EXIT_FAILURE);

// Button 2 is a direct child of the container window.
g_Button2 = CreateWindowExW(NULL, L"BUTTON", L"Button", WS_VISIBLE | WS_CHILD | WS_TABSTOP, 100, 100, 130, 40, g_Container, (HMENU)IDC_BUTTON2, NULL, NULL);
if (!g_Button2)
    exit(EXIT_FAILURE);
  1. 主窗口过程和容器窗口过程定义:
// Main window procedure:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_COMMAND:
    {
       switch (LOWORD(wParam))
       {
       // The main window is the direct parent of button 1, so this message is expected (on click/enter or space key press on focus).
       case IDC_BUTTON1:
       MessageBox(NULL, L"Button 1 clicked", L"Main window", MB_OK);
       return 0;
       // I was not expecting this message to be sent here, but it is, and only when the enter key is pressed.
       // The space key or normal click with the mouse still sends the message to the container window as expected.
       case IDC_BUTTON2:
       MessageBox(NULL, L"Button 2 clicked", L"Main window", MB_OK);
       return 0;
       }
       break;
    }
     // Other unrelated messages handling ...
    default:
       return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}
// Container window procedure:
LRESULT CALLBACK WindowProcedure_Container(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
    switch (uMsg)
    {
    case WM_COMMAND:
    {
        switch (LOWORD(wParam))
        {
        // Triggered normally on button click or space key press on focus but not the enter key,
        // The WM_COMMAND message triggered by the enter key is sent to the main window instead of its parent, and I don't know why.
        case IDC_BUTTON2:
        {
            MessageBox(NULL, L"Button 2 clicked", L"Container window", MB_OK);
            return 0;
        }
        default:
            break;
        }
        break;
    }
     // Remove the window subclass callback.
    case WM_NCDESTROY:
    {
        // Removes installed subclass callback from the window.
        if (!RemoveWindowSubclass(hWnd, &WindowProcedure_Container, uIdSubclass))
        {
            exit(EXIT_FAILURE);
        }

        return 0;
    }
    }

    return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
  1. 消息循环
MSG message = { 0 };
while (GetMessageW(&message, NULL, 0, 0))
{
    if (!IsDialogMessageW(g_hWnd, &message))
    {
        TranslateMessage(&message);
        DispatchMessageW(&message);
    }
}

我尝试了搜索引擎并使用 Spy++ 进行了实验。 我的第一个猜测是有什么东西挡住了回车键,并且它的消息从未到达按钮,但 Spy++ 结果却表明情况并非如此。它确实收到了关键消息(KEYDOWN,KEYUP,DLGCODE,..);它只是将 WM_COMMAND 消息发送到主窗口。

我碰壁了,不知道下一步该做什么。 我最终尝试对按钮进行子类化并“抑制”所有内容并为按钮实现我自己的按键处理(它有效) 但我觉得如果我知道问题的根源,这个问题可以更容易解决。

有人知道是什么导致了这种行为吗?谢谢你。

编辑: 附上单个文件可编译代码。

重复步骤:-> 打开应用程序-> 使用 Tab 键在按钮之间导航。导航到第二个按钮并按 Enter 键。它会触发一个不应该发生的对话框。

Visual Studio 2022:空项目 链接器子系统:Windows C++17

#include <Windows.h>  // Windows API: Winapi header.
#include <Windowsx.h> // Windows API: Winapi marcos.
#include <Uxtheme.h>  // Windows API: Visual themes and styles.
#include <commctrl.h> // Windows API: Common controls.
#include <string>

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

// Directive: Include necessary libs:
#pragma comment(lib, "UxTheme.lib")
#pragma comment(lib, "Comctl32.lib")

// Marcos:
#define IDC_BUTTON1 100
#define IDC_BUTTON2 101

// Global variables:
HINSTANCE g_hInstance; // Global instance.
HWND g_hWnd;           // Main application window.
HWND g_Container;      // Static window (Act like a container)
HWND g_Button1;        // Button 1
HWND g_Button2;        // Button 2

// Forward declarations:
LRESULT CALLBACK WindowProcedure(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK WindowProcedure_Container(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);

// WinMain Entry:
int WINAPI wWinMain(
    _In_ HINSTANCE hInstance,
    _In_opt_ HINSTANCE hPrevInstance,
    _In_ LPWSTR lpCmdLine,
    _In_ int nShowCmd)
{
    // Create my custom window class.
    WNDCLASSW my_class = { 0 };
    my_class.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    my_class.hCursor = LoadCursorW(NULL, IDC_ARROW);
    my_class.hIcon = LoadIconW(NULL, IDI_APPLICATION);
    my_class.hInstance = hInstance;
    my_class.lpszClassName = L"MyWindowClass";
    my_class.lpfnWndProc = WindowProcedure;
    g_hInstance = hInstance;

    // Register my window class.
    if (!RegisterClassW(&my_class))
    {
        MessageBoxW(NULL, L"Failed to register the window class.", L"", MB_OK | MB_ICONERROR);
        return -1;
    }

    g_hWnd = CreateWindowExW(NULL, L"MyWindowClass", L"Window title", WS_OVERLAPPEDWINDOW,
        200, 200, 400, 350, nullptr, nullptr, hInstance, nullptr);
    if (!g_hWnd)
    {
        MessageBoxW(NULL, L"Failed to create the main window.", L"", MB_OK | MB_ICONERROR);
        return -1;
    }

    ShowWindow(g_hWnd, SW_SHOWDEFAULT);

    // Enter the message loop.
    MSG message = { 0 };
    while (GetMessageW(&message, NULL, 0, 0))
    {
        if (!IsDialogMessageW(g_hWnd, &message))
        {
            TranslateMessage(&message);
            DispatchMessageW(&message);
        }
    }

    return 0;
}

// Procedure definitions:
LRESULT CALLBACK WindowProcedure(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_CREATE:
    {
        g_Button1 = CreateWindowExW(NULL, WC_BUTTON, L"Button 1", WS_VISIBLE | WS_CHILD | WS_TABSTOP, 5, 5, 130, 40, hWnd, (HMENU)IDC_BUTTON1, NULL, NULL);
        if (!g_Button1)
            return -1;

        // Create the container window(static).
        g_Container = CreateWindowExW(WS_EX_CONTROLPARENT, WC_STATIC, L"", WS_BORDER | WS_VISIBLE | WS_CHILD | SS_NOPREFIX | WS_CLIPCHILDREN,
            5, 55, 300, 200, hWnd, NULL, NULL, NULL);
        // Subclassing the container window.
        if (!g_Container || !SetWindowSubclass(g_Container, WindowProcedure_Container, NULL, NULL))
            return -1;

        // Button 2 is a direct child of the container window.
        g_Button2 = CreateWindowExW(NULL, L"BUTTON", L"Button", WS_VISIBLE | WS_CHILD | WS_TABSTOP, 5, 5, 130, 40, g_Container, (HMENU)IDC_BUTTON2, NULL, NULL);
        if (!g_Button2)
            return -1;

        return 0;
    }
    case WM_COMMAND:
    {
        switch (LOWORD(wParam))
        {
            // The main window is the direct parent of button 1, so this message is expected (on click/enter or space key press on focus).
        case IDC_BUTTON1:
            MessageBox(NULL, L"Button 1 clicked (Expected message)", L"Main window", MB_OK);
            return 0;
            // I was not expecting this message to be sent here, but it is, and only when the enter key is pressed.
            // The space key or normal click with the mouse still sends the message to the container window as expected.
        case IDC_BUTTON2:
            MessageBox(NULL, L"Button 2 clicked (Unexpected message, main window is not direct parent of button 2 but it send the message here?)", L"Main window", MB_OK);
            return 0;

        default:
            break;
        }

        break;
    }
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        break;
    }

    return DefWindowProcW(hWnd, message, wParam, lParam);
}
LRESULT CALLBACK WindowProcedure_Container(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
    switch (uMsg)
    {
    case WM_COMMAND:
    {
        switch (LOWORD(wParam))
        {
            // Triggered normally on button click or space key press on focus but not the enter key,
            // The WM_COMMAND message triggered by the enter key is sent to the main window instead of its parent, and I don't know why.
        case IDC_BUTTON2:
        {
            MessageBox(NULL, L"Button 2 clicked (Expected message)", L"Container window", MB_OK);
            return 0;
        }
        default:
            break;
        }
        break;
    }
    // Remove the window subclass callback.
    case WM_NCDESTROY:
    {
        // Removes installed subclass callback from the window.
        if (!RemoveWindowSubclass(hWnd, &WindowProcedure_Container, uIdSubclass))
        {
            exit(EXIT_FAILURE);
        }

        return 0;
    }
    }

    return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
c windows winapi
1个回答
0
投票
© www.soinside.com 2019 - 2024. All rights reserved.