win32
Windows消息机制
1.SDK和API
SDK:
软件开发工具包 (Software Development Kit) ,一般都是一些被软件工程师用于为特定的软件包、软件框架、硬件平台、操作系统等建立应用软件的开发工具的集合。
API函数:
Windows 操作系统提供给应用程序编程的接口(Application Programming Interface)。Windows应用程序API函数是通过C语言实现的,所有主要的Wi ndows函数都在Windows. h头文件中进行了声明。Windows 操作系统提供了1000多种API函数。
2.窗口和句柄
窗口是Windows应用程序中一个非常重要的元素,一个Windows 应用程序至少要有一个窗口,称为主窗口。
窗口是屏幕上的一块矩形区域,是Windows应用程序与用户进行交互的接口。利用窗口可以接收用户的输入、以及显示输出。
一个应用程序窗口通常都包含标题栏、菜单栏、系统菜单、最小化框、最大化框、可调边框,有的还有滚动条。如下图:
窗口可以分为客户区和非客户区,如上图。客户区是窗口的一部分,应用程序通常在客户区中显示文字或者绘制图形。
标题栏、菜单栏、系统菜单、 最小化框和最大化框、可调边框统称为窗口的非客户区,它们由Windows系统来管理,而应用程序则主要管理客户区的外观及操作。
窗口可以有一个父窗口,有父窗口的窗口称为子窗口。除了上图所示类型的窗口外,对话框和消息框也是一-种窗口。在对话框上通常还包含许多子窗口,这些子窗口的形式有按钮、单选按钮、复选框、组框、文本编辑框等。
在Windows应用程序中,窗口是通过窗口句柄( HIND)来标识的。我们要对某个窗口进行操作,首先就要 得到这个窗口的句柄。
句柄( HANDLE)是Windows程序中一个重要的概念,使用也非常频繁。在Windows程序中,有各种各样的资源 (窗口、图标、光标,画刷等),系统在创建这些资源时 会为它们分配内存,并返回标识这些资源的标识号,即句柄。 在后面的内容中我们还会看到图标句柄( HICON)、 光标句柄( HCURSOR)和画刷句柄( HBRUSH) 。
3.消息与消息队列
Windows程序设计是一种完全 不同于传统的DOS方式的程序设计方法。它是一种事件驱动方式的程序设计模式,主要是基于消息的。
每一个Windows应用程序开始执行后,系统都会为该程序创建一个消息队列,这个消息队列用来存放该程序创建的窗口的消息。
例如,当用户在窗口中画图的时候,按下鼠标左键,此时,操作系统会感知到这一事件, 于是将这个事件包装成一一个消息,投递到应用程序的消息队列中,等待应用程序的处理。
然后应用程序通过一个消息循环不断地从消息队列中取出消息,并进行响应。
在这个处理过程中,操作系统也会给应用程序“发送消息”。所谓“发送消息”,实际上是操作系统调用程序中一个专门负责处理消
息的函数,这个函数称为窗口过程。
4.WinMain函数
当Windows 操作系统启动一个程序时,它调用的就是该程序的WinMain函数(实际是由插入到可执行文件中的启动代码调用的)。WinMain是Windows 程序的入口点函数,与DOS程序的入口点函数main的作用相同,当WinMain函数结束或返回时,Windows 应用程序结束。
Windows 编程模型
一个完整的Win32程序(#include <windows.h>),该程序实现的功能是创建-一个窗口,并在该窗口中响应键盘及鼠标消息,程序的实现步骤为:
- winMain函数的定义
- 创建一个窗口
- 进行消息循环
- 编写窗口过程函数
- 通过循环取消息
- 处理消息(窗口过程)
代码如下:
#include <windows.h>//底层实现窗口的头文件
//6.处理消息(窗口过程)这是最后一步,但是把它写到了上面,起始是从程序入口函数开始
//CALLBACK 代表__stdcall:参数的传递顺序:从右至左依次入栈,并且在函数返回前清空堆栈
LRESULT CALLBACK WindowProc(
HWND hwnd, //消息所属的窗口句柄
UINT uMsg, //具体的消息名称 WM_xxxx消息名
WPARAM wParam,//键盘附加消息
LPARAM lParam//鼠标附加消息
)
{
//switch根据不同的消息做不同的处理
switch (uMsg)
{
case WM_CLOSE:
//所有xxxxWindow为结尾的方法,都不会进入消息队列中,而是直接执行
DestroyWindow(hwnd); //DestroyWindow发送另一个消息 WM_DESTROY
break;
case WM_DESTROY:
PostQuitMessage(0); //发送正常的退出
break;
case WM_LBUTTONDOWN://鼠标左键按下
{
int xPos = LOWORD(lParam);//Xposition,x位置,打印鼠标位置
int yPos = HIWORD(lParam);
char buf[1024];//定义一个字符数组
wsprintf(buf,TEXT("X=%d,Y=%d"),xPos,yPos);//拼接字符串1.buf你要把字符串拼接到哪里,2.你要拼接的字符串具体信息
MessageBox(hwnd,buf,TEXT("鼠标左键按下"),MB_OK);
break;
}
case WM_KEYDOWN://键盘按下
MessageBox(hwnd,TEXT("键盘按下"),TEXT("键盘按下"),MB_OK);
break;
case WM_PAINT://绘图
PAINTSTRUCT ps;//绘图结构体
HDC hdc = BeginPaint(hwnd,&ps);
TextOut(hdc,100,100,TEXT("hello"),strlen("hello"));
EndPaint(hwnd,&ps);
break;
}
//返回值用默认处理方式
return DefWindowProc(hwnd,uMsg,wParam,lParam);
}
//程序入口函数
//WINAPI宏 代表__stdcall:参数的传递顺序:从右至左依次入栈,并且在函数返回前清空堆栈
int WINAPI WinMain(
HINSTANCE hInstance, //应用程序实例句柄
HINSTANCE hPrevInstance,//上一个应用程序句柄,在win32环境下参数一般为null,不起作用了
LPSTR lpCmdLine, //char*argv[]
int nShowCmd )//显示命令 最大化、最小化、正常
{
//1.设计窗口
//2.注册窗口
//3.创建窗口
//4.显示和更新
//5.通过循环取消息
//6.处理消息(窗口过程)
//1.设计窗口
WNDCLASS wc; //WNDCLASS窗口的类
wc.cbClsExtra = 0; //类的额外的内存
wc.cbWndExtra = 0; //窗口的额外的内存
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);//设置背景GetStockObject()是个API
wc.hCursor = LoadCursor(NULL,IDC_HAND);//设置光标 第一个是null代表使用系统提供的光标,
wc.hIcon = LoadIcon(NULL,IDI_ERROR);//图标
wc.hInstance = hInstance; //应用程序的实例句柄,传入winmain中的形参即可
wc.lpfnWndProc = WindowProc; //窗口过程函数,即回调函数,回调函数名可随意更改
wc.lpszClassName = TEXT("WIN");//指定窗口类名称
wc.lpszMenuName = NULL;//菜单名称
wc.style = 0;//显示风格,0代表默认风格
//2.注册窗口类
RegisterClass(&wc);//带地址wc
//3.创建窗口
/*
lpClassName, //类名
lpWindowName, 标题名
dwStyle, 风格
x, 坐标
y,
nWidth,
nHeight,
hWndParent,//父窗口
hMenu,//菜单
hInstance, //实例句柄
lpParam //附加值 鼠标的附加值
*/
HWND hwnd = CreateWindow(
wc.lpszClassName,
TEXT("WINDOWS"),
WS_OVERLAPPEDWINDOW, //混合风格
CW_USEDEFAULT, //坐标默认值
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL
);
//4.显示和更新
ShowWindow(hwnd,SW_SHOWNORMAL);//SW_SHOWNORMAL 正常方式展示窗口
UpdateWindow(hwnd);
//5.通过循环取消息
/*
HWND hwnd;主窗口句柄,代表哪一个窗口传过来的消息
UINT message;具体的消息名称
WPARAM wParam;附加消息 键盘消息
LPARAM lParam;附加消息 鼠标消息 左右键
DWORD time;消息产生的时间
POINT pt;附加消息 鼠标消息 点击返回该点坐标点信息
*/
MSG msg;//每个消息都是一个结构体
while (1)
{
/*
__out LPMSG lpMsg,封装好的消息,带lp表示指针,所以要在前面加&
__in_opt HWND hWnd, 捕获窗口 填NULL代表捕获所有的窗口
__in UINT wMsgFilterMin, //最小和最大的过滤的消息 一般填0,代表捕获所有消息
__in UINT wMsgFilterMax);
*/
if (GetMessage(&msg,NULL,0,0)==FALSE)
{
break;//退出消息
}
//翻译消息(比如组合键,就需要获取到消息,翻译消息,再排队,再获取,再分发)
TranslateMessage(&msg);
//不为false,分发消息
DispatchMessage(&msg);
}
//6.处理消息(窗口过程)
return 0;
}