我在处理Windows窗口时遇到问题,即使我以前用过这种方式也是如此,它工作正常。在阅读了针对此问题的最常见建议后,它仍然存在。有人能告诉我为什么输入处理被打破了吗?
预期行为:
观察到的行为:
结论:消息处理已损坏。 我无法弄清楚为什么......
系统:
VS 2017社区版的编译器:
vcvarsall.bat amd64
cl -MTd -nologo -FC -Zi -W4 -WX -wd4100 -wd4312 FirstTry.cpp / link User32.lib Gdi32.lib
#include "windows.h"
static bool bAppIsRunning = false;
static bool bMessageAlreadyShown = false;
LRESULT CALLBACK win_MainWNDCallback(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam){
LRESULT result = 0;
switch(msg){
case WM_SYSKEYDOWN:
case WM_SYSKEYUP:
case WM_KEYDOWN:
case WM_KEYUP:{
WPARAM vKeyCode = wParam;
bool bWasDown = ((lParam & (1 << 30)) != 0);
bool bIsDown = ((lParam & (1 << 31)) == 0);
if (bWasDown != bIsDown)
{
switch (vKeyCode)
{
case VK_ESCAPE:{
bAppIsRunning = false;
}break;
default:{
result = DefWindowProc(wnd,msg,wParam,lParam);
}break;
}
}
}break;
default:{
result = DefWindowProc(wnd,msg,wParam,lParam);
}break;
}
return result;
}
int CALLBACK WinMain(HINSTANCE HInstance, HINSTANCE HPrevInstance, LPSTR LpCmdLine, int NCmdShow){
WNDCLASSA wndCLass = {};
wndCLass.style = CS_HREDRAW | CS_VREDRAW;
wndCLass.lpfnWndProc = win_MainWNDCallback;
wndCLass.hInstance = HInstance;
wndCLass.lpszClassName = (LPCSTR)"WindowClass";
if(RegisterClassA(&wndCLass)){
HWND wnd = CreateWindowExA(
0, wndCLass.lpszClassName, (LPCSTR)"FirstTry",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT,
1240, 720,
0, 0, HInstance, 0);
if(wnd){
bAppIsRunning = true;
HDC DeviceContext = GetDC(wnd);
PatBlt(DeviceContext, 0, 0, 1240, 720, BLACKNESS);
ReleaseDC(wnd, DeviceContext);
while(bAppIsRunning){
if(!bMessageAlreadyShown){
MessageBoxA(NULL, (LPCSTR)"Successfully entered loop.", (LPCSTR)"Success!", MB_ICONINFORMATION | MB_OK);
bMessageAlreadyShown = true;
}
MSG msg;
while(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)){
switch(msg.message){
case WM_SYSKEYDOWN:
case WM_SYSKEYUP:
case WM_KEYDOWN:
case WM_KEYUP:{
WPARAM vKeyCode = msg.wParam;
bool bWasDown = ((msg.lParam & (1<<30)) != 0);
bool bIsDown = ((msg.lParam & (1<<31)) != 0);
if(bIsDown != bWasDown){
switch(vKeyCode){
case 'W':{
bMessageAlreadyShown = false;
}break;
default:{
TranslateMessage(&msg);
DispatchMessageA(&msg);
}break;
}
}
}
}
}
}
MessageBoxA(NULL, (LPCSTR)"Closing Application.", (LPCSTR)"Bye bye!", MB_ICONINFORMATION | MB_OK);
}
}
return ERROR_SUCCESS;
}
答案 0 :(得分:1)
您的代码的主要问题是,只有在收到某些按键消息时才会调用TranslateMessage()
和DispatchMessage()
。您需要在主消息循环中为所有消息调用它们。你应该处理你的WndProc回调中的所有消息。
您还在使用基于TCHAR
的API,但却误用了LPCTSTR
类型转换。在将字符串/字符文字转换为TEXT()
时,您需要使用TCHAR
宏。
尝试更像这样的东西:
#include <windows.h>
static bool bMessageAlreadyShown = false;
LRESULT CALLBACK win_MainWNDCallback(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_SYSKEYDOWN:
case WM_SYSKEYUP:
case WM_KEYDOWN:
case WM_KEYUP: {
WPARAM vKeyCode = wParam;
bool bWasDown = ((lParam & (1 << 30)) != 0);
bool bIsDown = ((lParam & (1 << 31)) == 0);
if (bWasDown != bIsDown) {
switch (vKeyCode) {
case 'W':
case VK_ESCAPE:
DestroyWindow(wnd);
return 0;
}
}
break;
}
case WM_ERASEBKGND:
PatBlt((HDC)wParam, 0, 0, 1240, 720, BLACKNESS);
return 0;
}
return DefWindowProc(wnd, msg, wParam, lParam);;
}
int CALLBACK WinMain(HINSTANCE HInstance, HINSTANCE HPrevInstance, LPSTR LpCmdLine, int NCmdShow) {
WNDCLASS wndCLass = {};
wndCLass.style = CS_HREDRAW | CS_VREDRAW;
wndCLass.lpfnWndProc = win_MainWNDCallback;
wndCLass.hInstance = HInstance;
wndCLass.lpszClassName = TEXT("WindowClass");
if (RegisterClass(&wndCLass)) {
HWND wnd = CreateWindowEx( 0, wndCLass.lpszClassName, TEXT("FirstTry"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 1240, 720, 0, 0, HInstance, 0);
if (wnd) {
MSG msg;
while (GetMessage(&msg, 0, 0, 0)) {
if (!bMessageAlreadyShown) {
bMessageAlreadyShown = true;
MessageBox(NULL, TEXT("Successfully entered loop."), TEXT("Success!"), MB_ICONINFORMATION | MB_OK);
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
MessageBox(NULL, TEXT("Closing Application."), TEXT("Bye bye!"), MB_ICONINFORMATION | MB_OK);
return ERROR_SUCCESS;
}
请注意,我删除了您的bAppIsRunning
变量,因为一旦消息循环处理了WM_QUIT
消息,它就变得多余了。
我还删除了对 ALT-F4 的处理,因为操作系统会自动为您处理。它关闭窗口,触发WM_CLOSE
消息。默认情况下,DefWindowProc()
会通过销毁窗口来处理WM_CLOSE
,从而触发WM_DESTROY
消息。
我还为WM_ERASEBKGND
添加了处理以在窗口上绘制背景。从消息循环外部绘制是错误的。只要需要在屏幕上刷新窗口,您执行的任何绘图都会丢失,因此您必须重绘所有内容以响应WM_ERASEBKGND
和WM_PAINT
。