1.事件和消息
Windows中的事件是一个“动作”,这个动作可能是用户操作应用程序产生的,也可能是Windows自己产生的.
例如:鼠标右键点击就是一个事件
当我们做了这个动作后,windows要能正确响应必须记录如下信息:点的是谁、在哪里点的、动作是什么类型等等;
否则windows就无法知道我们到底要它做什么事情;
消息,就是用来描述这些“动作”的,比如:
这个动作是什么时候产生的?
哪个应用程序产生的?
在什么位置产生的?
等等。。。
Windows为了能够准确的描述这些信息,提供了一个结构体:MSG,该结构体里面记录的事件的详细信息.
typedef struct tagMSG { HWND hwnd; UINT message; WPARAM wParam; LPARAM lParam; DWORD time; POINT pt; } MSG, *PMSG;
说明:
1】hwnd:
句柄 ,每一个窗口都有一个唯一的句柄
表示消息所属的窗口
一个消息一般都是与某个窗口相关联的
在Windows中 HWND类型的变量通常用来标识窗口。
2】message
消息类型,用来表示到底是什么动作;例如鼠标右键单击;
在Windows中,消息是由一个数值来表示的
但是由于数值不便于记忆,所以Windows将消息对应的数值定义为WM_XXX宏(WM == Window Message)
鼠标左键按下 WM_LBUTTONDOWN 键盘按下 WM_KEYDOWN
3】wParam 和 lParam
对消息的进一步详细说明,具体存什么可以查消息类型的文档;
32位消息的特定附加信息,具体表示什么处决于message
4】time
消息创建时的时间 ;也就是什么时候出发的这个动作;
5】消息创建时的鼠标位置
用一个POINT结构来表示;
POINT结构记录了横纵坐标;
typedef struct tagPOINT { LONG x; LONG y; } POINT, *PPOINT, NEAR *NPPOINT, FAR *LPPOINT;
2.消息流程
1】系统/用户触发的某个动作 //事件
2】系统将这些信息存储到MSG结构体中 //消息
3】系统将该消息存储到相关应用程序的消息队列中 //消息队列
MSG Msg;
4】while(GetMessage(&Msg,NULL,0,0)) //循环从队列中获取消息,放到定义的空Msg结构中
{
TranslateMessage(&Msg); //翻译消息,例如把按键盘某个键翻译成该键对应的字符
DispatchMessage(&Msg); //分派消息,不是直接调用我们写的函数,而是将翻译完的消息发回给操作系统
}
5】DispatchMessage将加工过的消息传递给操作系统
6】系统调用窗口过程函数
7】LRESULT CALLBACK WindowProc( //窗口过程函数
IN HWND hwnd, //窗口句柄
IN UINT uMsg, //消息类型
IN WPARAM wParam, //和第四个参数都是消息附加信息
IN LPARAM lParam
);
如图,系统消息队列与应用程序消息队列:
对上图的解析:
用户的输入使一个事件被触发,windows将该事件封装在MSG消息结构中;
然后将这个消息结构放在windos系统队列里面;系统队列是一个先进先出的数据结构;
每一个程序都有一个应用程序队列,windows根据句柄将系统队列中的消息分发到各个应用程序的队列中;
程序从应用程序队列中取出消息,然后判断消息的类型;
因为不知道什么时候队列中有消息,就需要有个消息循环,一直循环从队列中取消息;
如果是关心的消息就自己处理,否则就交给windows来处理;
3.创建窗口
#include<stdio.h> #include<windows.h> #include "windebug.h" LRESULT CALLBACK WindowProc( IN HWND hwnd, IN UINT uMsg, IN WPARAM wParam, IN LPARAM lParam ); int CALLBACK WinMain( //CALLBACK是一个宏,表示__stdcall,也就是内平栈,win32所有api函数都是该调用约定 HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ){ //1.告诉window要画一个什么样的窗口 TCHAR className[] = "My First Window"; //窗口的类名 // 创建窗口类的对象 WNDCLASS wndclass = {0}; //一定要先将所有值赋值,否则RegisterClass函数无法起作用; wndclass.hbrBackground = (HBRUSH)COLOR_MENU; //窗口的背景色 wndclass.lpfnWndProc = WindowProc; //窗口过程函数 wndclass.lpszClassName = className; //窗口类的名字 wndclass.hInstance = hInstance; //定义窗口类的应用程序的实例句柄 //2.注册窗口类 RegisterClass(&wndclass); //3.创建窗口类 HWND hwnd = CreateWindow( className, //类名,可以用自己定义的窗口My First Window,也可用系统定义好的窗口例如按钮button TEXT("我的第一个窗口"), //窗口标题 WS_OVERLAPPEDWINDOW, //窗口外观样式 10, //相对于父窗口的X坐标 10, //相对于父窗口的Y坐标 600, //窗口的宽度 300, //窗口的高度 NULL, //父窗口句柄,为NULL NULL, //菜单句柄,为NULL hInstance, //当前应用程序的句柄 NULL); //附加数据一般为NULL if(hwnd == NULL) //是否创建成功 return 0; //4.显示窗口 ShowWindow(hwnd, SW_SHOW); //5.消息循环 MSG msg; while(GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return 0; } //6.回调函数,由操作系统来调用 /* 窗口消息处理程序 窗口回调函数: 1、窗口回调函数处理过的消息,必须传回0. 2、窗口回调不处理的消息,由DefWindowProc来处理. */ LRESULT CALLBACK WindowProc( IN HWND hwnd, IN UINT uMsg, IN WPARAM wParam, IN LPARAM lParam ) { switch(uMsg) { //窗口消息 case WM_CREATE: { DbgPrintf("WM_CREATE %d %d\n",wParam,lParam); CREATESTRUCT* createst = (CREATESTRUCT*)lParam; DbgPrintf("CREATESTRUCT %s\n",createst->lpszClass); return 0; } case WM_MOVE: { DbgPrintf("WM_MOVE %d %d\n",wParam,lParam); POINTS points = MAKEPOINTS(lParam); DbgPrintf("X Y %d %d\n",points.x,points.y); return 0; } case WM_SIZE: { DbgPrintf("WM_SIZE %d %d\n",wParam,lParam); int newWidth = (int)(short) LOWORD(lParam); int newHeight = (int)(short) HIWORD(lParam); DbgPrintf("WM_SIZE %d %d\n",newWidth,newHeight); return 0; } case WM_DESTROY: { DbgPrintf("WM_DESTROY %d %d\n",wParam,lParam); PostQuitMessage(0); return 0; } //键盘消息 case WM_KEYUP: { DbgPrintf("WM_KEYUP %d %d\n",wParam,lParam); return 0; } case WM_KEYDOWN: { DbgPrintf("WM_KEYDOWN %d %d\n",wParam,lParam); return 0; } //鼠标消息 case WM_LBUTTONDOWN: { DbgPrintf("WM_LBUTTONDOWN %d %d\n",wParam,lParam); POINTS points = MAKEPOINTS(lParam); DbgPrintf("WM_LBUTTONDOWN %d %d\n",points.x,points.y); return 0; } } return DefWindowProc(hwnd,uMsg,wParam,lParam); }