當我們啟動一個線程,並且要給線程函數傳遞的參數是窗口句柄時,我們應該這樣做:
HWND hHwnd = GetSafeHwnd();
HANDLE hThread;
DWORd dwThreadId;
hThread = ::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)DeviceOnLine, (LPVOID)hHwnd, 0, &dwThreadId);
// DeviceOnLine是線程函數,原型static UINT DeviceOnLine(LPVOID pParam);
// 注意:參數的形式是這樣的 (LPVOID)hHwnd,沒有地址符,注意啊!為什么要傳遞的參數非得是句柄呢?(對啊,為什么?)應為我們要使線程函數和主線程通信,要使用PostMessage(...)函數,所以要談到Windows的消息機制。
// -----------------------下面是Windows消息機制
Windows系統是一個消息驅動的OS,什么是消息呢?我很難說得清楚,也很難下一個定義(誰在噓我),我下面從不同的幾個方面講解一下,希望大家看了后有一點了解。
1、消息的組成:一個消息由一個消息名稱(UINT),和兩個參數(WPARAM,LPARAM)。當用戶進行了輸入或是窗口的狀態發生改變時系統都會發送消息到某一個窗口。例如當菜單轉中之后會有WM_COMMAND消息發送,WPARAM的高字中(HIWORD(wParam))是命令的ID號,對菜單來講就是菜單ID。當然用戶也可以定義自己的消息名稱,也可以利用自定義消息來發送通知和傳送數據。
2、誰將收到消息:一個消息必須由一個窗口接收。在窗口的過程(WNDPROC)中可以對消息進行分析,對自己感興趣的消息進行處理。例如你希望對菜單選擇進行處理那么你可以定義對WM_COMMAND進行處理的代碼,如果希望在窗口中進行圖形輸出就必須對WM_PAINT進行處理。
3、未處理的消息到那里去了:M$為窗口編寫了默認的窗口過程,這個窗口過程將負責處理那些你不處理消息。正因為有了這個默認窗口過程我們才可以利用Windows的窗口進行開發而不必過多關注窗口各種消息的處理。例如窗口在被拖動時會有很多消息發送,而我們都可以不予理睬讓系統自己去處理。
4、窗口句柄:說到消息就不能不說窗口句柄,系統通過窗口句柄來在整個系統中唯一標識一個窗口,發送一個消息時必須指定一個窗口句柄表明該消息由那個窗口接收。而每個窗口都會有自己的窗口過程,所以用戶的輸入就會被正確的處理。例如有兩個窗口共用一個窗口過程代碼,你在窗口一上按下鼠標時消息就會通過窗口一的句柄被發送到窗口一而不是窗口二。
5、示例:下面有一段偽代碼演示如何在窗口過程中處理消息
LONG yourWndProc(HWND hWnd,UINT uMessageType,WPARAM wP,LPARAM)
{
switch(uMessageType)
{//使用SWITCH語句將各種消息分開
case(WM_PAINT):
doYourWindow(...);//在窗口需要重新繪制時進行輸出
break;
case(WM_LBUTTONDOWN):
doYourWork(...);//在鼠標左鍵被按下時進行處理
break;
default:
callDefaultWndProc(...);//對於其它情況就讓系統自己處理
break;
}
}
接下來談談什么是消息機制:系統將會維護一個或多個消息隊列,所有產生的消息都回被放入或是插入隊列中。系統會在隊列中取出每一條消息,根據消息的接收句柄而將該消息發送給擁有該窗口的程序的消息循環。每一個運行的程序都有自己的消息循環,在循環中得到屬於自己的消息並根據接收窗口的句柄調用相應的窗口過程。而在沒有消息時消息循環就將控制權交給系統所以Windows可以同時進行多個任務。下面的偽代碼演示了消息循環的用法:
while(1)
{
id=getMessage(...);
if(id == quit)
break;
translateMessage(...);
}
當該程序沒有消息通知時getMessage就不會返回,也就不會占用系統的CPU時間。 下圖為消息投遞模式
在16位的系統中系統中只有一個消息隊列,所以系統必須等待當前任務處理消息后才可以發送下一消息到相應程序,如果一個程序陷如死循環或是耗時操作時系統就會得不到控制權。這種多任務系統也就稱為協同式的多任務系統。Windows3.X就是這種系統。
而32位的系統中每一運行的程序都會有一個消息隊列,所以系統可以在多個消息隊列中轉換而不必等待當前程序完成消息處理就可以得到控制權。這種多任務系統就稱為搶先式的多任務系統。Windows95/NT就是這種系統。
// -------------------------------------------
不知道,你還記得那個線程函數嗎?下面是定義
UINT DeviceOnLine(LPVOID pParam)
{
HWND hHwnd = (HWND)pParam; // 轉化參數
...
CString str;
str.Format("test");
::PostMessage(hHwnd, WM_MY_MESSAGE, (WPARAM)str, NULL); // 向hHwnd句柄PostMessage
// 要是static類型的,記住static函數的使用方法:一個版本,僅僅與類對話,沒有this指針。
...
}
如果要使用PostMessage很多次,我們可以象下面這樣獨立出一個函數(由於我們是在static的線程函數里面使用,所以聲明的也是static):
static int AddMessage(HWND hWnd, CString str)
{
if(str.GetLength() <= 0)
return 0;
char *newMess = new char[str.GetLength() + 1];
strcpy(newMess,str);
::PostMessage(hWnd,WM_MY_MESSAGE,(WPARAM)newMess,0);
return 0;
}
現在,我們已經把我們需要的消息發了出去,那我們就還要處理它,不然我們還發它們干嗎呢,呵呵(廢話,快點說吧)!
首先:在類中聲明處理函數,比如 afx_msg LRESULT AddMessageEx(WPARAM wPapam, LPAPAM lPapam);
其次:在Message Map中加入映射,比如:
BEGIN_MESSAGE_MAP(CTestDlg, CDialog)
ON_MESSAGE(WM_MY_MESSAGE, AddMessageEx)
END_MESSAGE_MAP
最后,實現處理函數:
LRESULT CTestDlg::AddMessageEx(WPARAM wParam, LPARAM lParam)
{
char* newMsg = (char*)wParam;
if(newMsg == NULL)
return -1;
... // 這里就可以使用我們接收的消息啦,哈哈!目的完成。
delete newMsg;
return 0;
}
對了,還有一個比不可少的,就是在stdafx.h文件中加入,自己定義的消息
#define WM_MY_MESSAGE (WM_USER+123)
原文地址:http://blog.chinaunix.net/uid-20680966-id-1896390.html