消息處理函數
消息處理函數又叫窗口過程,在這個函數中,不同的消息將被switch語句分配到不同的處理程序中去。Windows的消息處理函數的原型是這樣定義的:
LRESULT CALLBACK WindowProc(
HWND hwnd, //接收消息窗口的句柄
UINT uMsg, //主消息值
WPARAM wParam, //副消息值1
LPARAM lParam //副消息值2
);
消息處理函數必須按照上面的這個樣式來定義,當然函數名稱可以隨便取。
第一次代碼中的WinProc( )函數就是一個典型的消息處理函數。在這個函數中明確地處理了幾個消息,分別是WM_KEYDOWN(擊鍵)、WM_LBUTTONDOWN(鼠標左鍵按下)、WM_RBUTTONDOWN(鼠標右鍵按下)、WM_CLOSE(關閉窗口)、WM_DESTROY(銷燬窗口)。值得注意的是,應用程序發送到窗口的消息遠遠不止以上這幾條,像WM_SIZE、WM_MINIMIZE、WM_CREATE、WM_MOVE等頻繁使用的消息就有幾十條。在附錄中可以查到Windows常見消息列表。
爲了減輕編程的負擔,Windows提供了DefWindowProc( )函數來處理這些最常用的消息,調用了這個函數後,這些消息將按照系統默認的方式得到處理。因此,在消息處理函數中,只須處理那些有必要進行特別響應的消息,其餘的消息都可交給DefWindowProc( )函數來處理。
常用Windows函數
顯示對話框
MessageBox函數可以用來顯示對話框,它的原型是:
int MessageBox(HWND hwndParent, LPCSTRlpszText, LPCSTR lpszTitle, UINT fuStyle);
其中的四個參數依次爲:窗口句柄,文字內容,標題,風格。常用風格有:MB_OK、MB_OKCANCEL、MB_RETRYCANCEL、MB_YESNO、MB_YESNOCANCEL,代表對話框有哪些按鈕。常用返回值有IDCANCEL、IDNO、IDOK、IDRETRY、IDYES,代表哪個按鈕被按下。
定時器
定時器可以使程序每隔一段時間執行一個函數。用法如下:
SetTimer(HWND hwnd, UINT ID, UINT Elapse, TIMERPROC TimerFunc);
四個參數依次爲窗口句柄、定時器標識(同一程序內各個定時器的標識應不相同,一般從1、2、3...一直排下去)、每隔多少毫秒(千分之一秒)執行一次程序,要執行的過程。
這個要執行的過程應這樣定義:
void CALLBACK MyTimer(HWND hwnd,UINT uMsg,UINT idEvent,DWORD dwTime);
這幾個規定的參數用法在實例中會瞭解到,一般很少用到。
注意:定時器的優先級不高,當處理器很忙時我們需要定時執行的程序常常不能按時執行;無論你把定時器的Elapse(時間間隔)設得多小,它實際上最小隻能是55ms。
得到時間
我們的程序經常需要得到當前的準確時間來完成測試速度等工作。這時我們可以使用GetTickCount(),因爲該函數可以返回Windows已經運行了多少毫秒。
等待操作
很多時候,我們在程序的一些位置需要一段延時。雖然可以用for( )等循環語句實現,但不精確,而且都白白地佔用了CPU時間。那麼我們該調用什麼語句呢?Sleep( )。括號內填入你想等待的微秒數即可。
播放聲音
我們可以使用MCI來簡易地實現在程序中播放MP3等聲音。使用它需要預先聲明,我們需要在文件頭包含頭文件#include<mmsystem.h>,鏈接函數庫#pragma comment(lib, "winmm.lib")
下面先讓我們看看播放MP3的過程。首先我們要打開設備:
mciSendString("open + MP3路徑 alias 音樂在程序裏的代名詞", NULL, 0, NULL);
接着就可以播放MP3了:
mciSendString("play + 打開時定下的音樂在程序裏的代名詞", NULL, 0, NULL);
停止播放:
mciSendString("stop + 打開時定下的音樂在程序裏的代名詞", NULL, 0, NULL);
最後要關閉設備:
mciSendString("close + 打開時定下的音樂在程序裏的代名詞", NULL, 0, NULL);
多線程編程
基礎知識
多線程編程技術在編程中是有不少應用的。舉個例子,如果我們要在我們編寫的遊戲中使用一個超大的場景,那麼我們要一邊顯示圖形一邊預讀數據才能使遊戲進行得平滑和流暢。那麼如何實行在顯示圖形得同時預讀數據呢?這就可以使用多線程技術。
什麼是線程?我們知道,一個程序的運行又被稱爲一個進程(Process),而一個進程可以擁有多個線程(Thread),它們共享進程的各種資源,幫助進程同時做幾件事。每個線程都擁有一個優先級別,它決定了此線程執行的優先程度。注意在線程間切換是需要一些時間的,所以同時使用多個優先度很高的線程不是一個好主意。
使用多線程的基本方法並不複雜,有點像使用定時器的方法。我們來看一個實例吧:
首先,給出線程函數的定義:
void f( ) { i++; }
然後在主程序裏創建一個線程:
DWORD ThreadID;
HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)f, NULL, 0, &ThreadID);
CreateThread( )的原形如下:
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes, //設定安全屬性,一般NULL
SIZE_T dwStackSize,//堆棧大小,一般給它0,即使用默認值
LPTHREAD_START_ROUTINE lpStartAddress, //指向線程函數的指針
LPVOID lpParameter, //線程函數使用的參數(最多一個)的指針
DWORD dwCreationFlags, //如爲CREATE_SUSPENDED則暫時掛起線程
LPDWORD lpThreadId //返回線程的標識
);
由於我們把dwCreationFlags設爲0,現在f線程就會開始執行;如果把它設爲CREATE_SUSPENDED或在此後執行了SuspendThread( hThread );(掛起進程),執行ResumeThread ( hThread );(繼續掛起的進程) 可以讓它繼續執行。
我們可以執行SetThreadPriority(hThread, nPriority )設定線程的優先級,nPriority的可能取值有:
nPriority取值 |
含義 |
THREAD_PRIORITY_IDLE |
線程爲最低優先級 |
THREAD_PRIORITY_LOWEST |
線程爲很低優先級 |
THREAD_PRIORITY_BELOW_NORMAL |
線程爲較低優先級 |
THREAD_PRIORITY_NORMAL |
線程爲一般優先級 |
THREAD_PRIORITY_ABOVE_NORMAL |
線程爲較高優先級 |
THREAD_PRIORITY_HIGHEST |
線程爲很高優先級 |
THREAD_PRIORITY_TIME_CRITICAL |
線程爲最高優先級 |
其實線程的實際優先級還取決於進程的優先級,進程的優先級可以通過這種方法設定:
得到進程:HANDLE hProcess = GetCurrentProcess( );
設定進程優先級:SetPriorityClass(hProcess, dwPriorityClass );
dwPriorityClass的各種取值意義如下表:
dwPriorityClass取值 |
含義 |
IDLE_PRIORITY_CLASS |
進程爲最低優先級 |
BELOW_NORMAL_PRIORITY_CLASS |
進程爲較低優先級(系統須爲Win2000或XP) |
NORMAL_PRIORITY_CLASS |
進程爲一般優先級 |
ABOVE_NORMAL_PRIORITY_CLASS |
進程爲較高優先級(系統須爲Win2000或XP) |
HIGH_PRIORITY_CLASS |
進程爲很高優先級 |
REALTIME_PRIORITY_CLASS |
進程爲最高優先級(慎用) |
一般來說,要停止一個線程應該在線程函數中執行ExitThread(返回值 );。如果在線程外需要強行停止線程,只能調用TerminateThread( hThread, 返回值 );,該函數會造成諸多不良後果,不宜使用。
協調線程
多線程技術的真正難點在於如何協調多個線程。關鍵問題是如何控制各個線程對進程數據的訪問。多個線程操作相同的數據時,一般是需要按順序訪問的,否則會多個線程重複控制一個變量,數據錯亂。爲解決這個問題,就需要引入互斥變量,讓每個線程都按順序地訪問變量。
CRITICAL_SECTION CriticalSection1; //我們假想此全局變量代表了i的使用權
InitializeCriticalSection(&CriticalSection1);//使i成爲臨界資源,也就是說一次只能有一個線程訪問的資源
設置完以後,假如有第二個線程訪問臨界資源,就會先掛起,等待第一個線程用完臨界資源、"離開"
EnterCriticalSection(&CriticalSection1);//此語句會自動等待
i--; //可以放心地使用i;
使用完數據後記得“離開”:
LeaveCriticalSection(&CriticalSection1);
最後要解除臨界資源:
DeleteCriticalSection(&CriticalSection1);
還有一個就是等待另一個線程結束,這可以通過調用下面的語句實現:
WaitForSingleObject(hThread, dwMilliseconds );
其中dwMilliseconds爲最多等待多少毫秒,若爲INFINITE,則永遠等下去。
如果等待多個線程,可以這樣做:
WaitForMultipleObjects( 線程數,線程數組的指針, bWaitAll, dwMilliseconds );
bWaitAll如果爲true,此語句等待至全部線程完成;若爲false,此語句等待至線程數組中任一線程完成。