Windows 消息循环初解
场景
- Win32窗口编程, 肯定涉及到消息循环. Win32编程基本就是通过处理消息来处理系统任务, 所以Win32窗口编程, 弄懂消息循环是必然的.
说明
1.对于GUI线程,Win32消息循环使用两个队列来来存储消息, 一个是系统消息队列, 一个线程指定的队列. 消息队列是fifo的队列.
The system maintains a single system message queue and one thread-specific message
queue for each GUI thread.
2.系统在创建线程时不会自动创建一个消息队列, 因为这样会浪费资源,并不是每个线程都需要响应系统消息的. 只有用户执行了一个需要消息队列的操作时系统才会创建消息队列. 比如GetMessage(),PeekMessage().
The system does not automatically create a message queue for each thread. Instead, the system creates a message queue only for threads that perform operations which require a message queue.
3.线程指定的消息循环是自己创建的, 消息队列是系统创建的, 一般的格式如下.
MSG msg;
BOOL bRet;
while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
{
if (bRet == -1)
{
// handle the error and possibly exit
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
4.PostMessage 会发送一个消息到消息队列末尾,并立即返回.
PostMessage places a message at the end of a thread's message queue and returns immediately, without waiting for the thread to process the message.
5.SendMessage 发送消息MSDN没说是发送到队列末尾, 但应该不会违反规则. SendMessage 发送消息到队列末尾并等待消息循环执行完消息才返回. 窗口过程仍旧是在GUI线程里执行,不是在其他线程里执行.
The SendMessage function is used to send a message directly to a window procedure. SendMessage calls a window procedure and waits for that procedure to process the message and return a result.
6.注意, 线程指定的消息循环不一定需要创建窗口. 我们也可以利用线程消息循环来处理非窗口任务, 看以下例子, 可以使用线程指定内置的消息队列来线程安全的执行消息任务. 这样就不需要自己写临界区或加锁在queue上了.
// test_message_loop1.cpp : 定义应用程序的入口点。
//
#include "stdafx.h"
#include <windows.h>
#include <assert.h>
#include <iostream>
#include "test_message_loop1.h"
int PressAnyKey( const wchar_t *prompt )
{
DWORD mode;
HANDLE hstdin;
INPUT_RECORD inrec;
DWORD count;
wchar_t default_prompt[] = L"Press a key to continue...\n";
/* Set the console mode to no-echo, raw input, */
/* and no window or mouse events. */
hstdin = GetStdHandle( STD_INPUT_HANDLE );
if (hstdin == INVALID_HANDLE_VALUE
|| !GetConsoleMode( hstdin, &mode )
|| !SetConsoleMode( hstdin, 0 ))
return 0;
if (!prompt) prompt = default_prompt;
/* Instruct the user */
WriteConsole(
GetStdHandle( STD_OUTPUT_HANDLE ),
prompt,
lstrlen( prompt ),
&count,
NULL
);
FlushConsoleInputBuffer( hstdin );
/* Get a single key RELEASE */
do ReadConsoleInput( hstdin, &inrec, 1, &count );
while ((inrec.EventType != KEY_EVENT) || inrec.Event.KeyEvent.bKeyDown);
/* Restore the original console mode */
SetConsoleMode( hstdin, mode );
return inrec.Event.KeyEvent.uChar.AsciiChar;
}
typedef enum MyMessage1
{
kMessageCopyFile = WM_USER+100
}MyMessage;
DWORD WINAPI StartThread(void* data)
{
MSG msg;
BOOL bRet;
std::cout << "StartThread: " << ::GetCurrentThreadId() << std::endl;
while( (bRet = GetMessage( &msg, (HWND)-1, 0, 0 )) != 0)
{
std::cout << "GetMessage: " << msg.message
<< " ThreadID: " << ::GetCurrentThreadId()
<< " wParam: " << msg.wParam
<< " lParam: " << msg.lParam
<< std::endl;
Sleep(2000);
if (bRet == -1)
{
// handle the error and possibly exit
}
else
{
//TranslateMessage(&msg);
//DispatchMessage(&msg);
}
}
std::cout << "StartThread END: " << ::GetCurrentThreadId() << std::endl;
return 0;
}
class A
{
public:
virtual ~A(){std::cout << "~A" << std::endl;}
};
class B : public A
{
public:
~B(){std::cout << "~B" << std::endl;}
};
int main(int argv,char* args[])
{
std::cout << "main: " << ::GetCurrentThreadId() << std::endl;
DWORD IDThread;
HANDLE h1 = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE) StartThread,NULL,0,&IDThread);
assert(h1);
DWORD IDThread2;
HANDLE h2 = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE) StartThread,NULL,0,&IDThread2);
assert(h2);
int code = 0;
while((code = PressAnyKey(NULL))!= 'q')
{
std::cout << code << std::endl;
if(code == 'c')
{
PostThreadMessage(IDThread,WM_QUIT,8,9);
PostThreadMessage(IDThread2,WM_QUIT,8,9);
}else
{
PostThreadMessage(IDThread,kMessageCopyFile,8,9);
PostThreadMessage(IDThread2,kMessageCopyFile,8,9);
}
}
return 0;
}