已解决:
我在错误的窗口上拨打
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 消息发送给其直接父级。但在这种情况下情况并非如此,我不明白为什么。仅当使用回车键(回车键)时才会发生这种情况。
/// 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
g_hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
// 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);
// 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);
}
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);
}
已解决:
我在错误的窗口上拨打
IsDialogMessage
。
解决方案:
winapi 中子窗口编辑控件上的 WS_TABSTOP https://web.archive.org/web/20100926084558/http://support.microsoft.com/kb/71450