窗口开发原理
一:几个概念
- API : 应用程序编程接口(Application Programming Interface),如同C语言的库函数一样,Windows中也有封装好的函数和接口用于操作Windows应用与操作系统即Windows API。头文件为:Windows.h(在VS2019中大概有一万个函数)。我们可以在离线或者在线MSDN中查阅这些API,离线MSDN下载:MSDNLibraryVisualStudio6.0
- SDK : 软件开发包(Software Develoment Kit) 一个资源的集合包含API函数库、帮助手册、工具
- 窗口 : 举个例子:比如我们的记事本这样有标题栏、菜单、系统菜单、滚动条、最大化、最小化窗口这样的就构成一个菜单:
另外菜单还可以分为客户区和非客户区:
二者之间的关系是:客户区覆盖在非客户区之上,非客户区范围比客户区大
- 句柄 :句柄可以简单的理解为一个ID,如同身份证一样,它唯一的标识着一种资源。比如常见的窗口有窗口句柄,图标会有图标句柄,菜单会有菜单句柄,光标会有光标句柄等等。
二:最简单的Win32程序
(一): 包含头文件:#include<Windows.h>
(二): 入口函数:
与C/C++控制台程序的入口函数不同的是此处的入口函数是Winmain函数。
main函数是 CUI (Console Uers Interface) 应用程序入口函数
Winmain函数是GUI(Graphical User Interface)应用程序入口函数
//最简单的Win32程序:
//1.头文件:
#include<Windows.h>
//2.入口函数:
int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPreInstance, LPSTR lpCmdLine, int nCmdShow)
{
//3.函数体:
return 0;
}
调用约定 : 调用约定定义了函数的入栈方式,我们知道函数的参数和局部变量都存储在栈中。其入栈顺序为参数先入栈,然后局部变量按照声明的的先后顺序进行入栈。那么对于参数入栈时由右往左进行入栈还是由左往右进行入栈有着两个约定:__stdcall定义的函数参数入栈方式为由右向左,WindowsAPI函数采用__stdcall的方式,必须显式表示出来也即在WinMain前必须有__stdcall。而C/C++函数默认采用的是__cdecl,不用显式表现出来。
函数的形参,局部变量存储在栈中
通常我们更多的用__stdcall的宏替换:尤其用WINAPI
WINAPI
CALLBACK
APIENTRY
参数列表解析:
- HINSTANCE:应用程序实例句柄类型
- hInstance:当前应用程序 实例句柄(标识着这个exe,管理着exe的所有资源)
- hPreInstance : 代表着前一个应用程序实例句柄。16为exe上面的一个参数,32位和64位已将将其废弃。现在默认为0值。
- LPSTR : char *类型,指向一个C语言字符串。
- lpCmdLine : 命令行参数
- nCmdShow : 窗口的显示方式(最大化,最小化,隐藏等方式)
深究HINSTANCE:
我们由HINSTANCE转到定义:
看出HINSTANCE为宏函数DECLARE_HANDLE的参数:
然后将宏函数DECLARE_HANDLE转到定义:
得到:其中##是连接符号
我们将name替换为HINSTANCE:
然后将连接符##去掉,整理:
由此我们可以看出HINSTANCE是一个结构体指针,并且该结构体只有一个整型数据成员。Windows中会将指针类型进行封装,故Windows中很少见指针类型,通常以句柄来代替。
三:消息提示框:MessageBox函数
#include<Windows.h>
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPreInstance, LPSTR lpCmdLine, int nCmdShow)
{
MessageBox(NULL, L"我的第一个Win32应用程序", L"天津科技大学", MB_OK);
return 0;
}
效果:
MessageBox函数解析:
参数:
- 第一个参数:父窗口句柄,没有父窗口则为NULL。
- 第二个参数:提示框的提示信息。L: 告诉编译器字符串要使用Unicode编码 好处:支持全世界各国语言,各国语言操作系统也可以用TEXT()代替。
- 第三个参数:提示框标题
- 第四个参数:按键和图标的结合。用 | 进行结合。 注意:按钮和图标进行组合,而不能按钮和按钮进行组合,图标和图标进行组合
MSDN:
按钮:
图标:
- 返回值:返回点击的按钮的值 IDOK(“确定”按钮),IDYES(“是”按钮),IDCANCEL(“取消”按钮), IDNO(“否”按钮)
//示例:
#include<Windows.h>
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPreInstance, LPSTR lpCmdLine, int nCmdShow)
{
//消息提示框
int nResult = MessageBox(NULL, TEXT("我的第一个win32应用程序"), TEXT("天津科技大学"), MB_ICONINFORMATION | MB_YESNOCANCEL);
if (IDYES == nResult)
{
MessageBox(NULL, L"点击的是“是(Y)”按钮", NULL, MB_OK);
}
else if (IDNO == nResult)
{
MessageBox(NULL, L"点击的是“否(N)”按钮", NULL, MB_OK);
}
else if (IDCANCEL == nResult)
{
MessageBox(NULL, L"点击的是取消按钮", NULL, MB_OK);
}
return 0;
}
四:第一个窗口程序
(一):设计窗口类
- 首先定义一个窗口:
//定义窗口wc:
WNDCLASS wc;
我们转到定义:
看到WNDCLASS 是一个结构体类型,其中包含了一个窗口的属性。
- 为窗口wc的属性进行赋值
wc.style = CS_HREDRAW | CS_VREDRAW //窗口类的风格,水平重绘,竖直重绘。
wc.lpfnwndProc = WindowProc; //窗口处理函数(窗口回调函数)
wc.cbClsExtra = 0; //窗口类的附加内存
wc.cbWndExtra = 0; //窗口的附加内存
wc.hInstance = hInstance; //当前应用程序的实例句柄
wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_LOGO)); //没有图标就带NULL,可以用系统图标LoadIcon,第一个参数为NULL,图标句柄
wc.hCursor = LoadCursor(hInstance, MAKEINTRESOURCE(IDC_CURSOR)); //加载光标,光标句柄
wc.hbrBackground = CreateSolidBrush(RGB(0, 255, 0)); //红绿蓝 0-255 //背景画刷颜色
wc.lpszMenuName = NULL; //菜单名
wc.lpszClassName = szAppClassName;
(二):注册窗口类
//注册窗口类:
if (0 == RegisterClass(&wc))
{
MessageBox(NULL, L"此程序不能运行在Windows NT上", L"温馨提示", MB_OK | MB_ICONERROR);
return 0;
}
(三):创建窗口
//创建窗口:
HWND hwnd = CreateWindow(
szAppClassName, //窗口类型名
L"天津科技大学", //窗口的标题
WS_BORDER | WS_CAPTION |WS_SYSMENU| WS_MAXIMIZEBOX | WS_MINIMIZEBOX, //WS:Windows Style 窗口的风格
300, 200, //窗口左上角坐标
800, 600, //窗口的宽和高
NULL, //父窗口句柄、
NULL, //菜单句柄
hInstance, //应用程序实例句柄
NULL //附加参数,WM_CREATE传递的信息
);
(四):显示以及更新窗口
//4.显示窗口
ShowWindow(hwnd, SW_SHOW);
//SW_SHOW:显示
//SW_SHOWMAXIMIZEO:最大化显示
//SW_SHOWMINIMIZEO:最小化显示
(五):消息循环
UpdateWindow(hwnd);
MSG msg;
while (GetMessage(&msg,NULL, 0,0))//WM_QUIT:退出消息,GetMessage返回0,循环截止
{
//将虚拟键消息转化为字符消息
TranslateMessage(&msg);
//将消息分发给窗口处理函数
DispatchMessage(&msg);
}
窗口处理函数:
//窗口处理函数
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CLOSE://窗口关闭消息
DestroyWindow(hwnd);//窗口销毁函数
break;
case WM_DESTROY://窗口销毁消息
PostQuitMessage(0);//发送退出消息,直接退出程序:WM_QUIT
break;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);//操作系统处理
}
完整代码:
#include<Windows.h>
#include"resource.h"
//第一个窗口程序
//1.设计窗口类
//2.注册窗口类
//3.创建窗口
//4.显示和更新窗口
//5.消息循环
//新类型的认识:
//LRESULT long 结果
//HWND 窗口句柄
//UINT usigned int 消息类型(编号)
//WParam usigned int 附加信息
//LPSTR long 附加信息
//ATOM WORD unsigned short
//WORD unsigned short
//LPCTSTR const wchar_t*
//LPCTSTR =>CONST WCHAR *
//CONST =>const
//WCHAR =>wchar_t
//窗口处理函数
LRESULT CALLBACK WindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPreInstance, LPSTR lpCmdLine, int cCmdShow)
{
//1.设计窗口类
//char szAppClassName[]="tust"; 不带L的字符串叫做窄字符串 窄字符:一个字符是一个字节,宽字符:一个字符占两个字节
//宽字符:wchar_t ch = L'A'; 宽字符串: wchar_t ch = L"A"; wchar_t ch = L"ABC";
wchar_t szAppClassName[] = L"tust"; //窗口类型名,任何一个窗口都有一个类型名,自己取
WNDCLASS wc;
wc.style = CS_HREDRAW | CS_VREDRAW; //窗口类的风格
wc.lpfnWndProc = WindowProc; //窗口处理函数(窗口回调函数)
wc.cbClsExtra = 0; //窗口类的附加内存
wc.cbWndExtra = 0; //窗口的附加内存
wc.hInstance = hInstance; //当前应用程序的实例句柄
wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_LOGO)); //没有图标就带NULL,可以用系统图标LoadIcon,第一个参数为NULL,图标句柄
wc.hCursor = LoadCursor(hInstance, MAKEINTRESOURCE(IDC_CURSOR)); //加载光标,光标句柄
wc.hbrBackground = CreateSolidBrush(RGB(0, 255, 0)); //红绿蓝 0-255 //背景画刷颜色
wc.lpszMenuName = NULL; //菜单名
wc.lpszClassName = szAppClassName;
//2.注册窗口类
if (0 == RegisterClass(&wc))
{
MessageBox(NULL, L"此程序不能运行在Windows NT上", L"温馨提示", MB_OK | MB_ICONERROR);
return 0;
}
//3.创建窗口
HWND hwnd = CreateWindow(
szAppClassName, //窗口类型名
L"天津科技大学", //窗口的标题
WS_BORDER | WS_CAPTION |WS_SYSMENU| WS_MAXIMIZEBOX | WS_MINIMIZEBOX, //WS:Windows Style 窗口的风格
300, 200, //窗口左上角坐标
800, 600, //窗口的宽和高
NULL, //父窗口句柄、
NULL, //菜单句柄
hInstance, //应用程序实例句柄
NULL //附加参数,WM_CREATE传递的信息
);
//4.显示窗口
ShowWindow(hwnd, SW_SHOW);
//SW_SHOW:显示
//SW_SHOWMAXIMIZEO:最大化显示
//SW_SHOWMINIMIZEO:最小化显示
//消息循环,更新窗口,重新画一下
UpdateWindow(hwnd);
MSG msg;
while (GetMessage(&msg,NULL, 0,0))//WM_QUIT:退出消息,GetMessage返回0,循环截止
{
//将虚拟键消息转化为字符消息
TranslateMessage(&msg);
//将消息分发给窗口处理函数
DispatchMessage(&msg);
}
return 0;
}
//窗口处理函数
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CLOSE://窗口关闭消息
DestroyWindow(hwnd);//窗口销毁函数
break;
case WM_DESTROY://窗口销毁消息
PostQuitMessage(0);//发送退出消息,直接退出程序:WM_QUIT
break;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);//操作系统处理
}