1、了解Windows操作系统应用程序基础原理概念;
2、掌握WinMain函数定义,创建窗口,消息循环及窗口过程函数;与动手做一个简单的Windows窗口程序
1、Visual Studio 2017
1、WinMain函数的定义
2、创建一个窗口
3、进行消息循环
4、编写窗口过程函数
最简单的Windows程序就是如此显示一个消息窗口,代码如下:
#include
int WINAPI WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nShowCmd)
{
MessageBox(NULL, "Visual C++", "窗口消息", 0); //显示消息对话框,MessageBox(句柄,显示内容,标题,MB_OK[此参数类型多种,如:MB_YESNO,MB_OKCANCEL等])
return 0;
}
1、设计一个窗口类;
2、注册窗口类;
3、创建窗口;
4、显示及更新窗口。
1️⃣、设计一个窗口类
WNDCLASS wn1; //定义一个窗口类
wn1.style = CS_HREDRAW | CS_VREDRAW; //窗口类的类型
wn1.lpfnWndProc = Win1; //窗口的过程函数(回调函数)
wn1.cbClsExtra = 0; //窗口类的附加内存,一般设置为0
wn1.cbWndExtra = 0; //窗口附加内存空间,通常设置为0
wn1.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);//画刷句柄通俗的说就是窗口的背景
wn1.hCursor = LoadCursor(NULL, IDC_APPSTARTING); //光标句柄;使用系统预定义光标时,第一个参数为NULL,第二参数有很多,仅限于系统预订光标
wn1.hIcon = LoadIcon(NULL, IDI_ERROR); //图标句柄
wn1.hInstance = hInstance; //定义一个程序实例句柄,后面会用到
wn1.lpszClassName = "FIRST"; //设置类的名称
wn1.lpszMenuName = NULL; //设置菜单的名字
Windows对窗口类定义原型如下:
typedef struct WNDCLASS{
UINT style;//窗口类的类型
WNDPROC lpfnWndProc;//窗口的过程函数(回调函数)
int cbClsExtra;//窗口类的附加内存,一般设置为0
int cbWndEXtra;//窗口附加内存空间,通常设置为0
HISNSTANCE hInstance;//实例号
HICON hIcon;//图标句柄
HCURSOR hCursor;//光标句柄
HBRUSH hbrBackground;//画刷句柄
LPCTSTR lpszMenuName;//设置菜单的名字
LPCTSTR lpszClassName;//设置类的名称
}WNDCLASS;
2️⃣、注册窗口类
RegisterClass(&wn1); //注册窗口
Windows对注册窗口类函数原型如下:
WINUSERAPI
2 ATOM
3 WINAPI
4 RegisterClassA(
5 __in CONST WNDCLASSA *lpWndClass);
6 WINUSERAPI
3️⃣、创建窗口
HWND hwnd;
hwnd = CreateWindow("FIRST", "一个窗口", WS_OVERLAPPEDWINDOW, 500, 500, 600, 400, NULL, NULL, hInstance, NULL
//(窗口名称,窗口标题,窗口风格,初始X坐标,初始Y坐标,X方向尺寸,Y方向尺寸,父窗口句柄,窗口菜单句柄,程序实例句柄,创建参数)
);
CreateWindows函数的原型声明如下:
HWND CreateWindow(
LPCTSTR lpClassName, //窗口类名称
LPCTSTR lpWindowName, //窗口的名称
DWORD dwstyle, //创建窗口的样式
int x, //窗口左上角x坐标
int y, //窗口左上角y坐标
int nWidth, //窗口宽度
int nHeight, //窗口高度
HWND hWndParent, //创建窗口的父窗口句柄
HMENU hMenu, //窗口菜单的句柄
HANDLE hInstance, //窗口所属的应用程序的实例句柄
LPVOID lpParam //数据指针
);
4️⃣、显示窗口及更新
ShowWindow(hwnd, SW_SHOWNORMAL); //显示窗口函数
UpdateWindow(hwnd); //更新窗口函数
显示及更新窗口函数ShowWindow原型声明如下:
//显示窗口
BOOL ShowWindow(
HWND hWnd,//窗口句柄
int nCmdShow//窗口显示状态
);
//更新窗口
BOOL UpdateWindow(
HWND hWnd//创建成功后的窗口句柄
)
到此为止,一个简单的窗口就创建完毕了。
MSG结构体定义如下
typedef struct tagMSG{
HWND hWnd;//窗口表示,代表消息所属窗口
UINT message;//消息标识
WPARAM wParam;//消息附加信息
LPARAM lParam;//消息附加信息
DWORD time;//消息投递到消息队列的时间
POINT pt;//鼠标当前位置
}
通常编写消息循环用如下方式
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg); //将虚拟键消息转换为字符消息
DispatchMessage(&msg); //将消息回传给操作系统
}
Windows的GetMessage()函数如下
BOOL GetMessage(
LPMSG lpMsg,//指向消息结构体
HWND hWnd,//接收属于哪个窗口的消息
UINT wMsgFilteMin,//获取消息的最小值
UINT wMsgFilteMax//获取消息的最大值
);
消息将进入消息队列中,当下一次调用GetMessage函数时被取出。取出后的消息被TranslateMessage函数转换为新消息投递到线程的消息队列中。而后DispatchMessage函数对消息进行处理(响应)
下面是Windows应用程序的消息处理机制图以便更好的理解(采自孙鑫先生的《VC++深入详解》)
LRESULT CALLBACK Win1(
HWND hwnd,//窗口句柄
UINT uMsg,//消息代码
WPARAM wParam,//消息附加参数
LPARAM lParam//消息附加参数
)
{
switch (uMsg)
{
case WM_CHAR:
char show[20];
scanf_s(show, "char is %d", wParam);
MessageBox(hwnd, show, "char", 0);
break;
case WM_LBUTTONDOWN:
MessageBox(hwnd, "Windows!", "消息窗口", MB_OKCANCEL);
HDC hdc;
hdc = GetDC(hwnd);
TextOut(hdc, 50, 100, "我做的", strlen("我做的"));
break;
case WM_PAINT:
HDC hDC;
PAINTSTRUCT ps;
hDC = BeginPaint(hwnd, &ps);
TextOut(hDC, 100, 100, "第一个Windows!", strlen("第一个Windows!"));
EndPaint(hwnd, &ps);
break;
case WM_CLOSE:
if (IDYES == MessageBox(hwnd, "是否真的结束?", "message", MB_YESNO))
{
DestroyWindow(hwnd);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
以下看看我们做的第一个窗口是什么样子的
点击鼠标后
点击确定按钮
按下键盘键
选择关闭窗口
由于我用的是VS2017的WIN32环境,与书中所讲的VC++6.0编译环境有所不同,在最初接触VS时遇到了些许困难,但都一一解决,在此总结下来,并分享给大家
①在VC++6.0中使用spritf是可行的在VS中需要加入_s,即sprintf_s,否则就会出现烫烫烫…的乱码如图(其实在VS2017中,哪怕是用sprintf也是会报错的)
②在配置VS2017环境时,需要将字符集改为多字节字符集。如图
否则则会与LRESULT的类型不兼容。
③在用VS2017建立Windows桌面应用程序时一定要点击正确,在项目中的子系统与预编译器中要有以下的东西,预处理器定义中有WINDOWS;子系统是窗口而非控制台,否则会一直出现“无法解释外部符号_WinMain@16…”(当然每个电脑可能配置不同,需要用到的同学可以看看)附图
问题基本上就这些。但是这样做出来的窗口是比较丑陋的,特别是右上角的那个红色的×。
在网络上制作一个ico文件,添加到resource文件夹中,在上面提到的光标句柄中进行修改
wn1.hCursor = LoadCursor(NULL, IDC_APPSTARTING);
如此就一点点的对窗口进行内容丰富和美化