上週末真他孃的廢,剛花了200大元買了輛拉風的二手自行車,結果今天就下雨,我一路風馳電掣騎到公司,連蛋蛋都溼透了, 太遜了!週末跟女朋友去看了場電影,去美國大爺家吃了個漢堡,甚是愜意.
第三章上篇當中,哥說過,第三章主要可以用兩句話來展開“創建和顯示窗口,接受和處理消息”,最後就剩下了個處理消息了,處理消息這是個很大的棋啊,搞不好就給繞進去了.
首先,你說處理什麼的消息?那肯定是你創建的窗口的消息,着他孃的絕對正確.
其次, 是誰調用的這個窗口 處理函數? 你創建的窗口? 媽的錯誤, 是操作系統,爲啥是操作系統, 狗日的微軟就那麼設計的,還記得在WNDCLASS中指定的lpfnWndProc字段嗎?賦值的就是這個回調函數名.
第三,什麼是回調函數? 就是這個函數! 我操,恭喜你,脫了褲子放屁,答對了,但是正確的理解應該是:這個函數不是直接由你生成的那個窗口直接調用的,而是操作系統調用的,操作系統什麼時候調用?記得那個DispatchMessage函數嗎?就是那個時候。
ok, 上面扯完了, 既然是操作系統調用的,所以回調函數有固定的格式:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
這裏說的格式固定指的是函數返回值( LRESULT),調用規範(CALLBACK)和參數列表(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam),而不是函數名!你丫願意起啥名就起啥,只要符合規範。
下面看看回調函數的代碼:
#include <windows.h>
#include <mmsystem.h>
#pragma comment(lib,"winmm.lib")
LRESULT CALLBACK callBackWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
PSTR szCmdLine,
int iCmdLine)
{
TCHAR* className = "firstWindow";
WNDCLASS wndClass;
wndClass.hInstance = hInstance;
wndClass.style = CS_HREDRAW|CS_VREDRAW;
wndClass.lpszClassName = className;
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0;
wndClass.lpszMenuName = NULL;
wndClass.lpfnWndProc = callBackWndProc; //暫時假設回調函數名字爲callBackWndProc
RegisterClass(&wndClass);
HWND hwnd = CreateWindow(className,
TEXT("title"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL
);
UpdateWindow(hwnd);
ShowWindow(hwnd, SW_SHOWNORMAL);
MSG msg;
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK callBackWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch(message)
{
case WM_CREATE:
PlaySound(TEXT("陳奕迅-好久不見.wav"), NULL, SND_FILENAME|SND_ASYNC);
return 0;
case WM_PAINT:
PAINTSTRUCT ps;
RECT rect;
HDC hdc = BeginPaint(hWnd, &ps);
GetClientRect(hWnd, &rect);
DrawText(hdc, TEXT("HELLO 鳳姐!"),lstrlen(TEXT("HELLO 鳳姐!")), &rect, DT_SINGLELINE|DT_CENTER|DT_VCENTER);
EndPaint(hWnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
回調函數callBackWndProc中我們處理了三種消息,WM_CREATE, WM_PAINT, WM_DESTROY, 還 記得我們前面說過,消息分爲兩種,第一種是操作系統放在每個窗口的消息循環中,等待GetMessage消息循環從裏面獲取,另一種是操作系統直接把消息發送給回調函數,而不放到消息循環中.
這裏可能有哥們問什麼情況下系統會直接把消息發送給回調函數而不通過消息堆棧, 一般情況下,通過直接調用系統API函數產生的消息是直接發送給回調函數的,比如創建窗口的時候UpdateWindow產生第一條WM_CREATE,這個就是直接發送給回調函數.
case WM_CREATE:這個裏面就一個PlaySound函數,使用這個函數要注意,工程要包含mmsystem.h和winmm.lib,所以在開頭的代碼中包含了#include <mmsystem.h> #pragma comment(lib,"winmm.lib")兩行代碼.
PlaySound有三個參數,第一個是 wav文件目錄,有的草包可能使用了說"媽的,根本不能播放", 我操,你首先要確定目錄
是不是正確,其次要確定是不是wav文件,別弄成mp3的了, 第二個參數,只要不是資源文件,都設成NULL,如果是資源文件就設置成應用程序的實例句柄,至於怎麼獲取這個句柄,要麼你自己baidu,要麼等以後再說,因爲再展開,老子今晚就不用睡覺了.
介紹WM_PAINT之前,我想問問你丫看A片的電腦是啥牌子的? DELL? 三星? iMac?...總值他孃的很多牌子的顯示器,你想,那麼多牌子的顯示設備,那麼多不同的分辨率, 都是相同的windows操作系統運行在上面, 你說在顯示之前,操作系統是不是要判斷以及獲取以下我們的顯示設備的信息? 這個東西就叫做設備環境,在操作系統中我們一般用句柄表示這個對象,HDC,設備環境句柄, 怎麼獲取設備環境句柄? BeginPaint,這個函數需要兩個參數,其中一個是HWND,廢話,當然要處理哪個窗口,就要獲取這個窗口所在環境的設備句柄,第二個是PAINTSTRUCT結構體,用來存儲設備環境的一些信息,這裏注意BeginPaint都是與EndPaint同時存在使用的,並且只在同一個消息處理中使用,你丫不能在WM_CREATE中寫一個BeginPaint函數,然後在WM_Paint中寫一個EndPaint函數,必須在同一個消息中。
其次,你想要在窗口上顯示信息,你顯示在窗口的什麼地方? 中間?還是兩邊?不管顯示在哪,你總要知道窗口大小才能計算中間在哪吧?這就要獲取窗口大小。RECT ,上下左右四部分界定大小,這個用GetClientRect函數獲取。
好了再看看WM_PAINT,就只有一個DrawText函數沒說了,這個也不說了,因爲大部分人都喜歡用TextOut。
最後一個WM_DESTROY,這個就是你關閉窗口時發送的消息,有的娘們可能又要問,既然已經關閉了,爲什麼還要扯淡的寫個PostQuitMessage(0);,你可以試一下,如果不寫這個函數,窗口是能關閉,但是,你在資源管理器中仍然可以看到進程仍在運行, why? 草,因爲GetMessage函數還在那浪蕩,沒有退出循環, 我們曾提到過當GetMessage函數獲取WM_QUIT的時候就會返回0值,於是循環就中止啦。