我编写了一个小型测试应用程序,通过延迟呈现将文件(带有硬编码的路径)插入到当前活动的文件夹/应用程序中。它如预期的那样工作。但是我有一个问题--为什么PeekMessage总是返回FALSE?但是,如果删除PeekMessage调用,则永远不会调用Wndproc。我读了一个类似的帖子,但是我在同一个线程中创建了一个窗口,在这个线程中我试图处理消息。
代码:
static LRESULT CALLBACK WindProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
switch (Msg) {
case WM_RENDERALLFORMATS: {
OpenClipboard(hWnd);
EmptyClipboard();
}
case WM_RENDERFORMAT: {
printf("WM_RENDERFORMAT received");
<Here the file paths are copied to the clipboard>
if (Msg == WM_RENDERALLFORMATS)
CloseClipboard();
return 0;
}
case WM_DESTROYCLIPBOARD:
return 0;
}
return DefWindowProc(hWnd, Msg, wParam, lParam);
}
HWND hwnd_;
void thread_(void* ignored) {
WNDCLASSEX wcx = { 0 };
wcx.cbSize = sizeof(WNDCLASSEX);
wcx.lpfnWndProc = WindProc;
wcx.hInstance = GetModuleHandle(NULL);
wcx.lpszClassName = TEXT("my_class");
RegisterClassEx(&wcx);
hwnd_ = CreateWindowEx(0, TEXT("my_class"), TEXT(""), 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, GetModuleHandle(NULL), NULL);
MSG msg;
while (true) {
if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
printf("PeekMessage returned TRUE\n");
TranslateMessage(&msg);
DispatchMessage(&msg);
break;
}
Sleep(1000);
}
}
void main() {
CloseHandle((HANDLE)_beginthread(thread_, 0, NULL));
// let's give some time to thread to create msg window
Sleep(100);
if (OpenClipboard(hwnd_)) {
EmptyClipboard();
SetClipboardData(CF_HDROP, NULL);
CloseClipboard();
}
while (true) {
Sleep(100);
}
}发布于 2022-07-26 15:43:42
PeekMessage() (和GetMessage())只返回通过PostMessage()或PostThreadMessage()发送到调用线程的消息队列的消息。当调用时没有已发布的消息可用时,PeekMessage()返回FALSE。GetMessage()会阻塞,直到发布的消息可用为止。
但是,所讨论的消息(WM_RENDERFORMAT和WM_RENDERALLFORMATS)将通过SendMessage...()直接发送到目标窗口。但是,这些消息是由另一个线程发送的,因此仍然需要PeekMessage() (或GetMessage()),因为它们内部分派跨线程边界发送的消息。
这在[经]文件中有说明。
发布传入的非队列消息,检查线程消息队列中的已发布消息,并检索消息(如果存在的话)。 ..。 在此调用期间,系统分发(**
DispatchMessage**)挂起的、非排队的消息,即使用SendMessage**,**SendMessageCallback**,**SendMessageTimeout**,或**SendNotifyMessage函数将消息发送到调用线程拥有的窗口。然后检索与指定筛选器匹配的第一个排队消息。系统还可以处理内部事件。如果未指定筛选器,则按以下顺序处理邮件:
以及文档
从调用线程的消息队列中检索消息。函数发送传入的发送消息,直到发布的消息可供检索。 ..。 在此调用期间,系统传递挂起的、非排队的消息,即使用
SendMessage**,**SendMessageCallback**,**SendMessageTimeout**,或**SendNotifyMessage函数将消息发送到调用线程拥有的窗口。然后检索与指定筛选器匹配的第一个排队消息。系统还可以处理内部事件。如果未指定筛选器,则按以下顺序处理邮件:
如果指定的窗口是由调用线程创建的,则将立即作为子例程调用窗口过程。如果指定的窗口是由不同的线程创建的,则系统切换到该线程并调用适当的窗口过程。只有当接收线程执行消息检索代码时,才会处理在线程之间发送的消息。发送线程被阻塞,直到接收线程处理该消息。但是,发送线程将在等待其消息被处理时处理传入的非队列消息。要防止这种情况,请将
SendMessageTimeout与SMTO_BLOCK集一起使用。有关非排队邮件的详细信息,请参阅非排队消息。
尽管如此,您对WM_RENDERALLFORMATS消息的处理是错误的。一方面,它不能调用EmptyClipboard(),另一方面,在呈现其数据之前,它不检查您的应用程序是否仍然是剪贴板所有者。有关这些要点为何重要,请参见雷德尔福玛斯?。
此外,您的主线程中有一个竞赛条件。在调用OpenClipboard()之前,它只休眠100 is,等待首先创建窗口,但不能保证在这100 is内分配了hwnd_。例如,只需要花那么长的时间才能使工作线程开始运行。
更好的选择是,在创建窗口时让辅助线程向事件发出信号,然后让主线程等待该事件(更好的方法是在创建窗口后将初始的SetClipboardData()调用移动到辅助线程本身,但您已经排除了该选项)。
尝试更像这样的东西:
static LRESULT CALLBACK WindProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
/*
case WM_CREATE:
if (OpenClipboard(hWnd)) {
EmptyClipboard();
SetClipboardData(CF_HDROP, NULL);
CloseClipboard();
}
return 0;
}
*/
case WM_RENDERALLFORMATS: {
if (OpenClipboard(hWnd)) {
if (GetClipboardOwner() == hWnd) {
SendMessage(hWnd, WM_RENDERFORMAT, CF_HDROP, 0);
}
CloseClipboard();
}
return 0;
}
case WM_RENDERFORMAT: {
printf("WM_RENDERFORMAT received");
if (wParam == CF_HDROP) {
// <Here the file paths are copied to the clipboard>
}
return 0;
}
case WM_DESTROYCLIPBOARD:
return 0;
}
return DefWindowProc(hWnd, Msg, wParam, lParam);
}
HWND hwnd_ = NULL;
void thread_(void* arg) {
HANDLE hEvent = (HANDLE)arg;
WNDCLASSEX wcx = { 0 };
wcx.cbSize = sizeof(WNDCLASSEX);
wcx.lpfnWndProc = WindProc;
wcx.hInstance = GetModuleHandle(NULL);
wcx.lpszClassName = TEXT("my_class");
RegisterClassEx(&wcx);
hwnd_ = CreateWindowEx(0, TEXT("my_class"), TEXT(""), 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, GetModuleHandle(NULL), NULL);
SetEvent(hEvent);
if (hwnd_ == NULL) {
return 0;
}
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
printf("GetMessage returned a message\n");
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
int main() {
HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!hEvent)
return 1;
uintptr_t res = _beginthread(thread_, 0, hEvent);
if (res == -1L) {
CloseHandle(hEvent);
return 1;
}
WaitForSingleObject(hEvent, INFINITE);
CloseHandle(hEvent);
if (hwnd_ != NULL) {
if (OpenClipboard(hwnd_)) {
EmptyClipboard();
SetClipboardData(CF_HDROP, NULL);
CloseClipboard();
}
}
HANDLE hThread = (HANDLE)res;
WaitForSingleObject(hThread, INIFINTE);
CloseHandle(hThread);
return 0;
}发布于 2022-07-26 15:18:18
为什么
PeekMessage总是返回FALSE?
假设传递给PeekMessage的窗口句柄是有效的,那么PeekMessage返回FALSE的原因只是队列中没有消息。
从文档可以看出情况是这样的,它说:
如果消息可用,则返回值为非零。 如果没有可用的消息,则返回值为零。
https://stackoverflow.com/questions/73123875
复制相似问题