Windows应用程序是基于消息驱动的,任何线程只要注册窗口类都会有一个消息队列用于接收用户输入的消息和系统消息。
一、 窗口的创建流程
创建一个完整的窗口需要经过下面四个操作步骤:
-
设计一个窗口类(填写Win32程序必要信息)
-
注册窗口类
-
创建窗口
-
显示及更新窗口
二、相关函数及术语
1、WinMain函数
// Windows程序的入口函数,C/C++是main函数
int WINAPI WinMain(
HINSTANCE hInstance, // 实例的句柄
HINSTANCE hPrevInstance, // 没有意义。它在16位窗口中使用,但现在总是0
LPSTR lpCmdLine, // 包含命令行参数作为一个Unicode字符串
int nCmdShow // 显示标志
);
2、窗口类
// 创建窗口所要信息
typedef struct {
UINT style; //窗口类型
WNDPROC lpfnWndProc; //回调函数,由系统调用
int cbClsExtra; //类的额外附加字节数
int cbWndExtra; //窗口的额外附加字节数
HINSTANCE hInstance; //实例句柄
HICON hIcon; //图标样式
HCURSOR hCursor; //光标样式
HBRUSH hbrBackground; //背景颜色
LPCTSTR lpszMenuName; //菜单名称
LPCTSTR lpszClassName; //窗口类名称
} WNDCLASS, *PWNDCLASS;
3、关于句柄
- 句柄(HANDLE),资源的标识。
- 操作系统要管理和操作这些资源,都是通过句柄来找到对应的资源。按资源的类型,又可将句柄细分成图标句柄(HICON),光标句柄(HCURSOR),窗口句柄(HWND),应用程序实例句柄(HINSTANCE)等等各种类型的句柄。操作系统给每一个窗口指定的一个唯一的标识号即窗口句柄。
4、MSG结构定义
typedef struct {
HWND hwnd; //消息发向窗口的句柄
UINT message; //消息标识符。这是一个数值,用以标识消息。
WPARAM wParam; //个32位的消息参数,其含义和数值根据消息的不同而不同。
LPARAM lParam; //一个32位的消息参数,其值与消息有关。
DWORD time; //消息放入消息队列的时间
POINT pt; //消息放入消息队列时鼠标的坐标
} MSG, *PMSG;
5、回调函数
- 回调函数是由系统调用的,只需要在设计窗口类当中,指定回调函数名就可以了。
- 一般在回调函数中处理各种消息,如对键盘输入的处理,对鼠标点击的处理等。
// 回调函数名可以改,只要在窗口类那里填写好对应的名称就可以
// 参数个数和各类型不能随便更改
LRESULT CALLBACK WindowProc(
HWND hwnd, //窗口句柄
UINT uMsg, //指定消息类型
WPARAM wParam, //指定其余的、消息特定的信息。该参数的内容与UMsg参数值有关。
LPARAM lParam //指定其余的、消息特定的信息。该参数的内容与UMsg参数值有关。
);
三、实例代码
#include <stdio.h>
#include <windows.h>
//回调函数声明
LRESULT CALLBACK WindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);
//Win32应用程序入口
int WINAPI WinMain(
HINSTANCE hInstance, // 当前应用程序实例
HINSTANCE hPrevInstance, // 32位程序没用,总为0
LPSTR lpCmdLine, // 命令行参数
int nCmdShow // 显示状态
)
{
char szTitle[] = "任逍遊 --- [专用空间]";
char szClassName[] = "简单问题复杂化";
// 1.设计窗口类
WNDCLASS wndcls; //窗口类
//填写窗口类相应的信息(必要)
wndcls.cbClsExtra = 0; //类的额外附加字节数
wndcls.cbWndExtra = 0; //窗口额外的附加字节数
wndcls.hbrBackground = (HBRUSH)GetStockObject(DKGRAY_BRUSH); //背景颜色
wndcls.hCursor = LoadCursor(NULL,IDC_ARROW); //光标样式
wndcls.hIcon = LoadIcon(NULL,IDI_WINLOGO); //图标样式
wndcls.hInstance = hInstance; //实例句柄
wndcls.lpfnWndProc = WindowProc; //回调函数,由系统调用
wndcls.lpszClassName = szClassName; //窗口类名称
wndcls.lpszMenuName = NULL; //菜单名字
wndcls.style = CS_HREDRAW | CS_VREDRAW; //窗口类型,这里是水平重画跟垂直重画
// 2.注册窗口类
RegisterClass(&wndcls);
// 3.创建窗口
HWND hwnd;
hwnd=CreateWindow(
szClassName, //窗口类名称,必须跟上面填写的一样,否则显示不了窗口
szTitle, //窗口标题
WS_OVERLAPPEDWINDOW, //指定要创建的窗口的样式
0,0, //窗口位置
600,400, //窗口大小
NULL, //父窗口
NULL, //菜单
hInstance, //实例句柄,WinMain函数参数
NULL //窗口创建数据
);
// 4.显示窗口
ShowWindow(hwnd,SW_SHOWNORMAL);
UpdateWindow(hwnd); //刷新窗口
// 循环获取消息
MSG msg;
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg); //转换消息(如:WM_CHAR),只会产生新的消息,不会销毁原由消息
// WM_CHAR消息由WM_KEYDOWN消息和WM_KEYUP消息组成
DispatchMessage(&msg); //将消息传给窗口回调函数
}
return 0;
}
//回调函数,处理各种消息,由系统调用
LRESULT CALLBACK WindowProc(
HWND hwnd, // 窗口句柄
UINT uMsg, // 指定消息类型
WPARAM wParam, // 第一个消息参数
LPARAM lParam // 第二条消息参数
)
{
char szMotto[] = "天若有情天亦老,人间正道是沧桑。";
char szOut[] = "简单问题复杂化";
switch(uMsg)
{
case WM_PAINT: /* 窗口重绘消息 */
HDC hDC;
PAINTSTRUCT ps;
hDC = BeginPaint(hwnd,&ps); //指定窗口进行绘图工作的准备
TextOut(hDC,0,0,szMotto,strlen(szMotto)); //窗口上输出文字
EndPaint(hwnd,&ps); //绘图结束,释放绘图区
/*
BeginPaint和EndPaint只能在WM_PAINT消息中使用,不能在其它消息在使用
BeginPaint和EndPaint是一对的,BeginPaint函数创建的DC不能用ReleaseDC去释放
GetDC获取到的DC也不能使用EndPaint来释放
*/
break;
case WM_CHAR: /* 键盘输入事件 */
char szChar[20];
sprintf(szChar,"键盘输入: %c(%d)",wParam,wParam);
MessageBox(hwnd,szChar,"任逍遊",0); //弹出消息对话框
break;
case WM_LBUTTONDOWN: /* 鼠标单击事件 */
MessageBox(hwnd,"鼠标单击事件发生了","任逍遊",0);
HDC hdc;
hdc = GetDC(hwnd); //获取窗口DC句柄
TextOut(hdc,0,50,szOut,strlen(szOut));
ReleaseDC(hwnd,hdc); //释放DC
/*GetDC和ReleaseDC是一对的*/
break;
case WM_CLOSE: /* 关闭窗口消息 */
if(IDYES==MessageBox(hwnd,"是否真的结束?","任逍遊",MB_YESNO))
{
// 会发送WM_DESTROY 和 WM_NCDESTROY 消息
DestroyWindow(hwnd); //销毁窗口,进程并没有结束
}
break;
case WM_DESTROY: /* 销毁消息 */
PostQuitMessage(0); //请求进程退出
break;
default:
return DefWindowProc(hwnd,uMsg,wParam,lParam); //缺省消息处理,必不可少!
}
return 0;
}
程序运行截图