窗口创建过程
注册窗口类
- win32中通过调用RegisterClassEx API来注册窗口样式,相当于指定一个窗口模板.
- 通过WNDCLASSEX结构体来指定窗口类的样式,WNDCLASSEX 是一个窗口风格样式的设置结构体.
具体代码如下
//由入口函数WinMain调用此函数
ATOM MyRegisterClass(HINSTANCE hInstance)
{
//指定窗口类的样式的结构体
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WIN32DEMO));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_WIN32DEMO);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
//RegisterClassExW函数的功能就是根据给定窗口类的样式的结构体wcex注册窗口类
return RegisterClassExW(&wcex);
}
cbSize
存储表示该结构大小的字节数,通常以sizeof(WNDCLASSEX)的形式对该域进行设置 一定要确保在调用
一定要在 GetClassInfoEx 前设置该值
style
存储表示窗口类风格的整数,它决定了该窗口的外观和内在特征。
可以使用’|‘进行多个选项绑定
具体绑定原理参见:枚举值的组合使用
lpfnWndProc
- lp :ong point 指针。
- fn :Function 函数。
- wnd :window 窗口
proc :process 过程,步骤
所以lpfnWndProc就是指向窗口处理过程的函数指针
必须使用 CallWindowProc 函数去调用窗口过程
cbClsExtra
为窗口类的额外信息做记录,初始化为0。
一般用得很少
cbWndExtra
记录窗口实例的额外信息,系统初始为0
如果应用程序使用WNDCLASSEX注册在资源文件中使用CLASS指令创建的对话框, 则必须将此成员设置为DLGWINDOWEXTRA
hInstance
该窗口类的窗口过程所属的应用实例
hIcon
存储该类窗口的图标的句柄
如果设置为NULL, 系统将提供默认图标
hCursor
存储该窗口光标的句柄
- 该域必须是一个光标资源的句柄。
- 如果为NULL, 则只要鼠标移动到应用程序的窗口中, 应用程序就必须显式设置光标形状
hbrBackground
窗口类的背景刷.
为背景刷句柄,也可以为系统颜色值
lpszMenuName
存储以空结尾的指定类菜单资源名的字符串指针
- 类菜单资源名已经在资源文件中进行了定义。
- 如果使用整数来标识菜单, 请使用MAKEINTRESOURCE宏
- 如果此成员为NULL, 属于此类的Windows没有默认菜单
lpszClassName
当前窗口类的名称
使用当前窗口类的唯一标识符
hIconSm
存储该类窗口小图标的句柄。
创建窗口
注册窗口类后,就可以创建窗口了,创建窗口的有关语句如下:
由入口函数WinMain调用此函数
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // 将实例句柄存储在全局变量中
// 通过调用CreateWindowW来创建窗口
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
参数说明
参数1:登记的窗口类名,这个类名刚才咱们在注册窗口时已经定义过了。
参数2:用来表明窗口的标题。
参数3: 用来表明窗口的风格,如有无最大化,最小化按纽啊什么的。
参数4,5: 用来表明程序运行后窗口在屏幕中的坐标值。
参数6,7: 用来表明窗口初始化时(即程序初运行时)窗口的大小,即宽度与高度。
参数8: 在创建窗口时可以指定其父窗口,这里没有父窗口则参数值为0。
参数9: 用以指明窗口的菜单,菜单以后会讲,这里暂时为0。
参数10:该窗口类的窗口过程所属的应用实例
最后一个参数是附加数据,会被传递到窗口过程中。一般都是0。
显示和更新窗口
API函数CreateWindow创建完窗口后,要想把它显示出现,还必须调用另一个API函数ShowWindows.形式为:
ShowWindow (hWnd, nCmdShow);
- hwnd: 窗口句柄,告诉ShowWindow()显示哪一个窗口
- nCmdShow:告诉它如何显示这个窗口.
- 最小化(SW_MINIMIZE)
- 普通(SW_SHOWNORMAL)
- 最大化(SW_SHOWMAXIMIZED)
WinMain在创建完窗口后就调用ShowWindow函数,并把iCmdShow参数传送给这个窗口。
WinMain()调用完ShowWindow后,还需要调用函数UpdateWindow使窗口得到更新.
创建消息循环
Windows为每个正在运行的应用程序都保持一个消息队列。
息响应机制是在Win32子系统中实现的.
- 为什么不在内核中实现?
- 因为内核越简单越好
- 如果在内核中实现, 则会使内核占用大量资源
- 可能会引起不稳定
过程
- 当按下鼠标或者键盘时,Windows并不是把这个输入事件直接送给应用程序,
- 而是将输入的事件先翻译成一个消息,
- 然后把这个消息放入到这个应用程序的消息队列中去。
- 应用程序的WinMain函数通过执行一段代码(主消息循环)从她的队列中来检索Windows送往她的消息。
- 然后WinMain就把这些消息分配给相应的窗口函数以便处理它们,
这段代码是一段循环代码,故称为”消息循环”
具体代码如下
// 主消息循环:
//通过GetMessage函数获取当前应用程序的消息队列的消息
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
//消息翻译
TranslateMessage(&msg);
//消息分发
DispatchMessage(&msg);
}
}
//MSG的定义 在WinUser.h中
typedef struct tagMSG {
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
#ifdef _MAC
DWORD lPrivate;
#endif
} MSG, *PMSG, NEAR *NPMSG, FAR *LPMSG;
MSG结构体参数解析
- hwnd
- 消息要发送到的那个窗口的句柄,
- 这个窗口就是用CreateWindows函数创建的那一个。
- 如果是在一个有多个窗口的应用程序中,用这个参数就可决定让哪个窗口接收消息。
- message
- 消息值, UINT类型,它唯一标识了一种消息类型。
- 每种消息类型都在Windows文件中定义了,这些常量都以WM_开始后面带一些描述了消息特性的名称。
- wParam,lParam
- 一个32位的附加信息,UINT类型,这个值的意义由message决定
- wParam,lParam和message一起决定要进行怎样的操作
- 通常作为指针来使用
- time
- 消息创建的时间
- 在这个域中写入的并不是日期,而是从Windows启动后所测量的时间值
- Windows用这个域来使用消息保持正确的顺序。
- pt
- 消息创建时鼠标所在屏幕当中的位置
GetMessage
GetMessage(
_Out_ LPMSG lpMsg,
_In_opt_ HWND hWnd,
_In_ UINT wMsgFilterMin,
_In_ UINT wMsgFilterMax);
- 第一个参数是要接收消息的MSG结构的地址,
- 第二个参数表示窗口句柄,NULL则表示要获取该应用程序创建的所有窗口的消息;
- 第三,四参数指定消息范围。
在接收到除WM_QUIT之外的任何一个消息后,GetMessage()都返回TRUE。
如果GetMessage收到一个WM_QUIT消息,则返回FALSE,结束消息循环,从而终止应用程序。
TranslateMessage
将用GetMessage读入的消息翻译成一些键盘消息
对大多数消息来说它并不起什么作用
DispatchMessage
在注册窗口类时曾提到过,注册窗口类时,指定Windows把函数WindosProc作为窗口处理过程。就是说,Windows会调用函数WindowsProc()来处理这个消息。在WindowProc()处理完消息后,代码又循环到开始去接收另一个消息,这样就完成了一个消息循环。