上周末真他娘的废,刚花了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值,于是循环就中止啦。