这次主要是记录了对消息机制的理解,对窗口的一些消息的说明与理解,如于标准(本人学的是Win程序设计第五版)有偏差,或哪里有不妥,欢迎大家给予斧正!
首先是Win的消息机制,每当程序被创建时系统会为其分配相应的实例句柄,以及消息队列等
一个Win程序的入口为
int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
(这个时VS创建时生成的)
其中APIENTRY也就是WINAPI(说白了就是_stdcall这个函数调用惯例)
第一个就是实例句柄
第二个时在早期版本时当一个程序被多次运行时通过检查这个参数就可以得知该程序以及其他程序的运行状况,并且可以从前面的实例中将某些数据挪来自己的数据区(这个参数已经废用)
第三个参数是命令行参数
第四个是指出这个程序初始化显示的方式的
下面来看一个例子:(书中的第一个程序)HelloWin.c(原书源码未做任何修改)
/*------------------------------------------------------------
HELLOWIN.C -- Displays "Hello, Windows 98!" in client area
(c) Charles Petzold, 1998
------------------------------------------------------------*/
#include
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT ("HelloWin") ;
HWND hwnd ;
MSG msg ;
WNDCLASS wndclass ;
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc = WndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = szAppName ;
if (!RegisterClass (&wndclass))
{
MessageBox (NULL, TEXT ("This program requires Windows NT!"),
szAppName, MB_ICONERROR) ;
return 0 ;
}
hwnd = CreateWindow (szAppName, // window class name
TEXT ("The Hello Program"), // window caption
WS_OVERLAPPEDWINDOW, // window style
CW_USEDEFAULT, // initial x position
CW_USEDEFAULT, // initial y position
CW_USEDEFAULT, // initial x size
CW_USEDEFAULT, // initial y size
NULL, // parent window handle
NULL, // window menu handle
hInstance, // program instance handle
NULL) ; // creation parameters
ShowWindow (hwnd, iCmdShow) ;
UpdateWindow (hwnd) ;
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return msg.wParam ;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc ;
PAINTSTRUCT ps ;
RECT rect ;
switch (message)
{
case WM_CREATE:
PlaySound (TEXT ("hellowin.wav"), NULL, SND_FILENAME | SND_ASYNC) ;
return 0 ;
case WM_PAINT:
hdc = BeginPaint (hwnd, &ps) ;
GetClientRect (hwnd, &rect) ;
DrawText (hdc, TEXT ("Hello, Windows 98!"), -1, &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) ;
}
通过大概浏览可知,这个简单的Win32程序有两个函数,第一个是main入口,第二个是WndProc也就是窗口过程
窗口过程是一个Win32程序对消息进行处理反馈的函数
程序中的
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
就是耳熟能详的消息循环(我个人认为这里并不准确的算是一个死循环)
这里就可以发现,Win编程的一个特点:用户会给在屏幕上的程序做一系列的操作,操作系统收到反馈后在对应的程序内将相应消息插入这个程序的消息队列内,然后在消息循环得到了这个消息以后在窗口过程中对这个消息进行处理(switch语句)然后相应的变化反馈到用户。
总而言之就是:Windows给程序发送消息
之后来看看这段代码的运行:(其实在VS中系统是已经会帮你生成一个非常简易但是又五脏俱全的一个窗口程序)
这里有一个窗口类,窗口,的差别,WNDCLASS类这个类实例化的对象就是一个窗口类的数据基础,然后由RegisterClass注册这个窗口类,然后再由CreateWindow来将窗口创建出来,其中,一个窗口类可以创建很多个有着不同差异的窗口,就好比同一类的书,但是书的内容(知识点)却有一些差异一样,这也就是为什么窗口类不直接把一些细节数据(如窗口大小,窗口名称等)的直接包括在内的原因(方便重复利用,简短的代码)
之后是将已分配好内存的窗口(CreateWindow之后)显示出来的ShowWindow这里的第一个参数就是窗口句柄,也就是CreateWindow的返回值,第二个就是之前提到的第四个参数
然后有了窗口,就要让他按照我们规定好的规则显示,此时用UpdateWindow向消息队列中插入一个WM_PAINT消息来重绘客户区
最后就是消息循环,这个里面的msg结构体的前四个实质上就是窗口队列的前四个参数,最后两个time是消息放入消息队列的事件以及pt(POINT)就是消息入队时的鼠标坐标(相对于屏幕)
然后就是
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
这两个第一个是将击键消息进行翻译转换的比如摁住shift加后一个字母或者其他的什么的时候会转换为WM_CHAR消息,并将转换过后的字符放到wParam这个域中去
然后DispatchMessage又将msg回传给Windows,由Windows调用窗口过程
之后是介绍窗口过程中的消息以及对应两个Param的意义了
WM_CREATE是创建窗口时收到的第一个消息(CreateWindow时就有的)
WM_PAINT是刷新客户区的时候系统自动调用(也可以手动调用)
使窗口无效的方式大概有:
调用InvalidateRect(在这里的第三个参数是比较皮的,如果为FALSE就保留之前客户区的内容,直接进行WM_PAINT显示出之后的东西)
拖动窗口边框(有CS_HREDRAW和CS_VREDRAW),改变Size
窗口有一部分被覆盖
滚动条滚动客户区
最后是WM_DESTROY消息,故名思意,单击X或者是在菜单里面点了退出时发生的,这是调用了PostQuitMessage函数这个函数会在消息队列里面插入一个WM_QUIT,当GetMessage收到一个WM_QUIT时,就会返回0从而跳出消息循环
其中WM_SIZE的lParam的高低字节分别记录了客户区的高和宽
补充:
在WinMain里面的消息函数对应关系:
CreateWindow -> WM_CREATE
ShowWindow-> WM_SIZE和WM_SHOWWINDOW
UpdateWindow->WM_PAINT