在前一节中我们学习了进程之间窗体与窗体进行消息(自定义消息, 广播消息, 系统消息[WM_COPYDATA]) 通讯, 本章中将练习同一进程中线程与线程之间消息如何通讯.
一、演示方法
在主窗体线程中创建一子线程, 并子线程函数中设立消息循环, 同时我们定义三个消息用于测试验证
#define WM_CLIENTTOSERVER (WM_APP + 1001) // 子线程发送给主线程
#define WM_SERVERTOCLIENT (WM_APP + 1002) // 主线程发送给子线程
#define WM_QUITSUBTHREAD (WM_APP + 1003) // 主线程通知子线程退出
I. 演示主线程(即主窗体线程)发送消息给子线程
1. 主线程通过PostThreadMessage发送WM_SERVERTOCLIENT消息给子线程
2. 子线程消息循环中将捕获的消息打印查找是否有显示WM_SERVERTOCLIENT消息
II. 演示子线程发送消息给主线程
1. 主线程发送WM_QUITSUBTHREAD消息给子线程
2. 子线程从消息队列中收到WM_QUITSUBTHREAD消息
3. 通过PostMessage发送WM_CLIENTTOSERVER消息给主线程并退出线程函数
4. 判断主线程是否收到WM_CLIENTTOSERVER消息;
二、代码演示
DWORD CALLBACK ThreadProc(LPVOID lpData)
{
MSG Msg;
HWND hMainWnd = *(HWND*)lpData;
while (true)
{
while (PeekMessage(&Msg, NULL, NULL, NULL, PM_REMOVE))
{
TCHAR szTmp[128];
_stprintf(szTmp, _T("Message: 0x%06X wParam: 0x%06X lParam: 0x%06X\n"), Msg.message, Msg.wParam, Msg.lParam);
OutputDebugString(szTmp);
}
if (WM_QUITSUBTHREAD == Msg.message)
{
PostMessage(hMainWnd, WM_CLIENTTOSERVER, 200, 220);
break;
}
}
TCHAR szTmp[128];
_stprintf(szTmp, _T("Sub Thread exit\n"), Msg.message, Msg.wParam, Msg.lParam);
OutputDebugString(szTmp);
return 0;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
static const UINT IDC_BTNSEND = 1001;
static const UINT IDC_BTNQUIT = 1002;
static HANDLE hThread;
switch (nMsg)
{
case WM_CREATE:
{
static HWND hMainWnd = hWnd;
hThread = CreateThread(NULL, NULL, ThreadProc, &hMainWnd, NULL, 0);
CreateWindow(_T("Button"), _T("Send Msg To Tread"), WS_VISIBLE|WS_CHILD, 0, 0, 150, 50, hWnd, (HMENU)IDC_BTNSEND, NULL, 0);
CreateWindow(_T("Button"), _T("Quit Thread"), WS_VISIBLE|WS_CHILD, 180, 0, 150, 50, hWnd, (HMENU)IDC_BTNQUIT, NULL, 0);
break;
}
case WM_COMMAND:
{
switch (wParam)
{
case IDC_BTNSEND:
{
PostThreadMessage(GetThreadId(hThread), WM_SERVERTOCLIENT, 100, 110);
break;
}
case IDC_BTNQUIT:
{
PostThreadMessage(GetThreadId(hThread), WM_QUITSUBTHREAD, 100, 110);
break;
}
}
break;
}
case WM_CLIENTTOSERVER:
OutputDebugString(_T("I recieved a WM_CLIENTTOSERVER Message"));
break;
case WM_CLOSE:
{
PostQuitMessage(0);
break;
}
}
return ::DefWindowProc(hWnd, nMsg, wParam, lParam);
}
三、演示结果
1. 点击三次Send Msg To Thread 之后, 再点击QuitThread 结果
Message: 0x0083EA wParam: 0x000064 lParam: 0x00006E <= 点击 Send Msg To Thread 1次
Message: 0x0083EA wParam: 0x000064 lParam: 0x00006E <= 点击 Send Msg To Thread 2次
Message: 0x0083EA wParam: 0x000064 lParam: 0x00006E <= 点击 Send Msg To Thread 3次
Message: 0x0083EB wParam: 0x000064 lParam: 0x00006E <= 点击 Quit Thread 主线程发送WM_QUITSUBTHREAD之的之后,子线程发送WM_CLIENTTOSERVER
Sub Thread exit
线程 'Win32 线程' (0xc50) 已退出,返回值为 0 (0x0)。
I recieved a WM_CLIENTTOSERVER Message <= 主线程收到WM_CLIENTTOSERVER之后返回
四、练习启示
1. PostThreadMessage 用于发送异步消息给线程消息队列,不要用它把消息发给一个窗体的,测试过,收不到消息,所以只能通过PostMessage或SendMessage一类函数效劳;
2. PostMessage,PostThreadMessage 比较: 相同点-- 都是异步发送消息, 不同点 – PostMessage 发送消息给窗体, PostThreadMessage 发送消息给无窗体的消息队列;
3. CreateThread 中传给用子线程的函数参数不能用局部变量, 启动线程后可能创建线程的函数己退出局部变量也退出作用域而无效造成子线程函数使用此参数有问题.
参考文档