c - 自定义 WM_APP 消息未发布在消息队列中
问题描述
当 WM_SIZE 发送到窗口过程时,我正在尝试发送自定义消息 (WM_APP + 1)。我希望能够使用 PeekMessage 从其他函数中捕获它并做一些事情。但是当我测试它时,消息似乎没有发送到队列中。添加一些 printf 语句告诉我它进入了窗口过程。奇怪的是,当我在调试器中单步执行代码时,它工作正常,但是当我正常运行时,它又回到不工作状态。
有问题的工作示例程序,调整窗口大小进行测试:
#include <windows.h>
#include <stdio.h>
#pragma comment(lib, "user32.lib")
#define OBG_EVENT_QUIT 0
#define OBG_EVENT_RESIZE 1
#define OBG_EVENT_NO -1
#define OBG_EVENT_UNKNOWN -2
//user defined event
#define OBG_WM_RESIZE (WM_APP + 1)
typedef union
{
int type;
struct
{
int type;
int width;
int height;
} resizeEvent;
} obg_event;
LRESULT CALLBACK obgpf_DefaultWindowCallback(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
{
LRESULT result = 0;
switch(message)
{
case WM_CLOSE:
{
PostMessageA(window, WM_QUIT, 0, 0);
} break;
//this should be handled by OBGGetEvent
case OBG_WM_RESIZE:
{
printf("MESSAGE WENT THROUGH. DON'T WANT THIS\n");
} break;
case WM_SIZE:
{
PostMessageA(window, OBG_WM_RESIZE, wParam, lParam);
} break;
default:
{
result = DefWindowProc(window, message, wParam, lParam);
} break;
}
return result;
}
int OBGGetEvent(obg_event *event)
{
int moreMessages = 0;
MSG message;
if(PeekMessage(&message, 0, 0, 0, PM_REMOVE))
{
moreMessages = 1;
switch(message.message)
{
case WM_QUIT:
{
event->type = OBG_EVENT_QUIT;
} break;
case OBG_WM_RESIZE:
{
event->type = OBG_EVENT_RESIZE;
event->resizeEvent.type = OBG_EVENT_RESIZE;
event->resizeEvent.width = LOWORD(message.lParam);
event->resizeEvent.height = HIWORD(message.lParam);
} break;
default:
{
event->type = OBG_EVENT_UNKNOWN;
TranslateMessage(&message);
DispatchMessage(&message);
} break;
}
}
else
{
event->type = OBG_EVENT_NO;
}
return moreMessages;
}
int main()
{
HINSTANCE instance = GetModuleHandleA(0);
WNDCLASSEX windowClass = {0};
windowClass.cbSize = sizeof(windowClass);
windowClass.style = CS_HREDRAW | CS_VREDRAW;
windowClass.lpfnWndProc = obgpf_DefaultWindowCallback;
windowClass.hInstance = instance;
windowClass.lpszClassName = "testClass";
windowClass.hIcon = LoadIcon(0, IDI_APPLICATION);
windowClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
windowClass.hIconSm = LoadIcon(0, IDI_APPLICATION);
windowClass.hCursor = LoadCursorA(0, IDC_ARROW);
HWND window;
if(RegisterClassEx(&windowClass))
{
window = CreateWindowEx(0,
windowClass.lpszClassName,
"test window",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT,
CW_USEDEFAULT,
500,
300,
0,
0,
instance,
0);
if(window)
{
int appIsRunning = 1;
obg_event event = {0};
event.type = -1;
while(appIsRunning)
{
while(OBGGetEvent(&event))
{
if(event.type == OBG_EVENT_QUIT)
{
printf("event quit\n");
appIsRunning = 0;
break;
}
else if(event.type == OBG_EVENT_RESIZE)
{
printf("window resized: width %d height %d\n", event.resizeEvent.width, event.resizeEvent.height);
}
}
Sleep(33);
}
}
else
{
printf("window error\n");
}
}
else
{
printf("windowClass error\n");
}
return 0;
}
我尝试使用 SendMessage 而不是 PeekMessage 来执行此操作,但发生了同样的事情。不知道我遗漏了什么或误解了什么,但感谢您的帮助!
编辑:添加了一个完整的工作程序来重现问题
解决方案
感谢您提供示例代码。
您看到的行为是因为当使用鼠标调整窗口大小时,操作系统会进入模式消息循环来处理鼠标输入。在此循环期间,您的消息循环不会运行 - 这意味着您PeekMessage()
的特殊消息处理也不会运行。相反,消息只是像往常一样简单地发送到您的窗口过程。
有两种解决方案立即浮现在脑海中,但如何处理这实际上取决于程序的设计以及为什么要以这种方式处理大小事件。
我的第一个想法是跟踪您是否处于模态大小调整循环中,并推迟发布通知消息直到循环完成。下面是如何使用您提供的窗口过程执行此操作的示例。
第二种解决方案是在获得时直接调用 resize 事件处理程序WM_SIZE
(或者,如果您必须通过事件系统,请将其处理程序放在窗口过程中,而不是使用PostMessage
)。
第一个建议的示例代码:
LRESULT CALLBACK obgpf_DefaultWindowCallback(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
{
LRESULT result = 0;
static bool fResized = false;
static bool fInSizeLoop = false;
switch(message)
{
case WM_CLOSE:
{
PostMessageA(window, WM_QUIT, 0, 0);
} break;
//this should be handled by OBGGetEvent
case OBG_WM_RESIZE:
{
printf("MESSAGE WENT THROUGH. DON'T WANT THIS\n");
} break;
case WM_SIZE:
{
if (fInSizeLoop) // in modal size loop, defer notification
fResized = true;
else
PostMessageA(window, OBG_WM_RESIZE, wParam, lParam);
} break;
case WM_ENTERSIZEMOVE:
fInSizeLoop = true; // begin modal size loop
break;
case WM_EXITSIZEMOVE:
fInSizeLoop = false; // left modal size loop
// post resize notification now
if (fResized) {
RECT rc;
GetClientRect(window, &rc);
PostMessageA(window, OBG_WM_RESIZE, 0, MAKELPARAM(rc.right - rc.left, rc.bottom - rc.top));
fResized = false;
}
break;
default:
{
result = DefWindowProc(window, message, wParam, lParam);
} break;
}
return result;
}
(抱歉,这是 C++ 代码,我刚刚注意到您已标记为 C - 但原理是相同的)。
推荐阅读
- flutter - 电话会议就像放大颤动
- python - Python exit() 命令触发无限循环(已解决)
- python - 如何在 Gekko 中使用 apm_get?
- python - numpy(setup.py)的构建轮......错误
- angular - 如何在 observable 中等待异步函数?
- go - 调用 Google Apps 脚本
- javascript - 是否可以在设备级别而不是浏览器级别使用 javascript 生成指纹?
- data-structures - 通过删除/更新查找最小值的数据结构建议
- c - 在 C 的 sprintf 中添加 \
- javascript - 如何飞到反应传单中的位置