第一节 创建应用程序主窗体
1 创建消息处理函数
LRESULT CALLBACK fWinProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg){
case WM_CLOSE: //处理关闭按钮消息
DestroyWindow(hWnd); //销毁窗口
break;
case WM_DESTROY:
PostQuitMessage(0); //发送退出消息
break;
}
//将不处理的函数交给系统默认处理
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
这个函数不用自己调用,赋值给窗体后会自动调用这个函数
关键是第二个参数,这个参数是获取到的消息标识
本例只处理两个消息:
(1) 当按下窗体右上角的关闭按钮时的消息: WM_CLOSE
(2) 当窗体进行真正销毁时:WM_DESTROY
消息队列只有在收到:WM_QUIT GetMessage()函数才会返回false值.因此必须要有一个行为能够发送这个消息.
此例中,我们在接到受到销毁窗口时发送这个消息:PostQuitMessage(0)
本例中消息处理函数:
LRESULT CALLBACK fWinProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg){
case WM_CLOSE: //处理关闭按钮消息
DestroyWindow(hWnd); //销毁窗口
break;
case WM_DESTROY:
PostQuitMessage(0); //发送退出消息
break;
}
//将不处理的函数交给系统默认处理
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
返回值类型为: LRESULT
限定词CALLBACK作用同WINAPI 用来设定参数的入栈方式(从右往左,以及参数的管理者:调用者)
第一个参数: 窗体句柄
第二个参数: 消息事件标识ID
第三个参数: 附加消息
第四个参数: 附加消息
返回值: 系统消息处理函数(对于不处理的消息,交回给系统处理,并返回调用结果的返回值
2 创建窗体结构体实例并设置相关属性
2.1 窗体结构体: WNDCLASS
WNDCLASS wc;
2.2 设置窗体实例属性
? 所有属性都要设置
typedef struct tagWNDCLASS {
UINT style;
WNDPROC lpfnWndProc;
int cbClsExtra;
int cbWndExtra;
HINSTANCE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCTSTR lpszMenuName;
LPCTSTR lpszClassName;
} WNDCLASS, *PWNDCLASS;
2.2.1 窗体显示风格
UINT style
style的值为下列值的组合
值 | 描述 |
---|
CS_DBLCLKS | 接受双击事件的窗体 | CS_HREDRAW | 调整大小后自动重绘的窗体 | CS_NOCLOSE | 禁用窗口菜单上的关闭按钮 | CS_PARENTDC | | CS_VREDRAW | 客户区移动或大小调整后重绘 | CS_SAVEBITS | 保存被窗体挡住部分的图像,以便恢复 |
? 可以上以上风格的任意组合,用(|) 连接即可
本例中设置为:
wc.style = CS_VREDRAW | CS_HREDRAW;
2.2.2 消息处理函数
WNDPROC lpfnWndProc;
就是第一步我们定义的消息处理函数,传入函数名即可
本例我们设置为:
wc.lpfnWndProc = fWinProc; //消息处理函数
2.2.3 窗体扩展额外空间
int cbClsExtra;
本例赋值为0
wc.cbClsExtra = 0;
2.2.4 窗体额外空间
int cbWndExtra;
本例赋值为0
wc.cbWndExtra = 0; //窗口的窗外扩展空间
2.2.5 应用程序句柄
HINSTANCE hInstance;
本例赋值为当前程序实例句柄
wc.hInstance = hInstrance; //当前应用程序句柄
这个变量为WinMain 函数的第一个参数
2.2.6 窗体左上角图标资源句柄
HICON hIcon
如果用系统默认的图标,设置为NULL
wc.hIcon = NULL;
如果用自己的资源
wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON));
函数: LoadIcon用来加Icon资源
第一个参数: 应用程序句柄
第二个参数: 一个字符串, MAKEINTRESOURCE将资源的ID转换为资源对应的字符串
2.2.7 窗体鼠标形状
? 如果用系统默认的鼠标图标,设置为NULL
wc.hCursor = NULL;
? 如果用自定义的资源
wc.hCursor = LoadCursor(hInstance, MAKEINTRESOURCE(IDC_CURSOR));
函数LoadCursor用来加载鼠标形状资源
第一个参数: 应用程序句柄
第二个参数: 资源字符串,一般用MAKEINTRESOURCE将资源对应的资源标识(IDC_CURSOR)转化为对应的字符串
2.2.8 客户区背景
HBRUSH hbrBackground;
? 设置客户区背景为红色
wc.hbrBackground = CreateSolidBrush(RGB(255, 0, 0 ));
2.2.9 菜单名
菜单 名: LPCTSTR lpszMenuName;
wc.lpszMenuName = NULL; //不设置没有菜单
2.2.10 类名
窗体类名: LPCTSTR lpszClassName;
wc.lpszClassName = L"MyWindowClass"; //随便写,暂时不知道什么用,用中文也是可以的
3 注册窗体
函数: RegisterClass
ATOM WINAPI RegisterClass(
_In_ const WNDCLASS *lpWndClass
);
参数: 窗体类地址
RegisterClass(&wc);
在实际应用中要对返回值做一个判断,以确认是否正确注册
if (0 == RegisterClass(&wc)) {
MessageBox(NULL, L"注册失败", L"温馨提示", MB_OK);
return 0; //注册失败结束程序
}
4 创建窗体句柄
HWND hWnd = CreateWindow(
LPCTSTR lpClassName, //类名 字符串
LPCTSTR lpWindowName, //窗体标题 字符串
DWORD dwStyle, //显示风格
int x, //左上角x坐标
int y, //左上角y坐标
int nWidth, //窗体宽度
int nHeight, //窗体高度
HWND hWndParent, //父窗体句柄,没有设置为NULL
HMENU hMenu, //菜单栏句柄,没有设置为NULL
HANDLE hInstance, //所属应用程序句柄
PVOID lpParam //附加参数,没有设置为NULL
);
本例设置如下:
HWND hWnd = CreateWindow(
L"MyWindowClass", //窗口类型名
L"第一个窗体", //窗口s标题
//带标题栏 系统菜单(关闭按钮) 最小化按钮 最大按钮
WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,//带标题栏的窗体
0, //左上角x坐标
0, //左上角y坐标
600, //窗体宽度
600, //窗体高度
NULL, //没有父窗体
NULL, //没有菜栏
hInstance, //所属应用程序句柄
NULL, //附加参数
);
第一个参数要求必须与窗体类定义时的类名一致
第三个参数: 窗体风格,可以是如下值的组合
值 | 描述 |
---|
WS_BORDER | 带边框的窗口 | WS_CAPTION | 带标题栏的窗口(包含WS_BORDER) | WS_CHILD | 子窗口(必须含父窗口), 不能与WS_POPUP 共用 | WS_CLIPCHILDREN | 父窗口使用,在绘制含本属性的窗口时会排除子窗口占用的区域 | WS_CLIPSIBLINGS | | WS_DISABLED | 最初禁用的窗体(禁用指不能接受用户输入,包括按钮点击等事件) | WS_DLGFRAME | 不能标题栏但有边框的窗体(一般用于对话框) | WS_GROUP | 指定一组控件的第一个控件,后续不带WS_GROUP的控件为后续控件,直到下一个使用WS_GROUP的控件出现 | WS_HSCROLL | 带水平滚动条 | WS_OVERLAPPED | 带标题栏与边框的重叠窗口 | WS_OVERLAPPEDWINDOW | 创建重叠窗口 | WS_POPUP | 弹出窗口,不能与WS_CHILD同时使用 | WS_SIZEBOX | 可调节大小同, WS_THICKFRAME | WS_SYSMENU | 在非客户区带关闭(X)按钮的窗体 | WS_TABSTOP | 可通过TAB切换控件焦点 | WS_THICKFRAME | 可调节大小同, WS_SIZEBOX | WS_VISIBLE | 最初可见 | WS_VSCROLL | 带垂直滚动条 |
第四个参数: 窗品显示的左上角x坐标(相对于屏幕左上角)
第五个参数: 窗品显示的左上角y坐标(相对于屏幕左上角)
第六个参数: 窗口的宽度
第七个参数: 窗口的高度
第八个参数: 父窗口,如果是顶级窗口没有父窗口,值为NULL
第九个参数: 菜单句柄,如果没有菜单栏,值为NULL
第十个参数: 应用程序句柄
第十一个参数: 附加参数 ,没有设置为NULL
注意: 在使用的时候,在后续的操作中要判断句柄是否创建成功,以免空指针成生的不良后果
if (hWnd == NULL){
MessageBox(NULL, L"窗体创建失败", L"警告", MB_ICONWARNING);
return 0;
}
5 显示窗体
函数原型
BOOL ShowWindow(HWND hWnd, nCmdShow)
第一个参数: 要显示的窗体句柄
第二个参数: 显示方式
显示方式列表:
值 | 描述 |
---|
SW_HIDE | 隐藏本窗体并激活另一个 | SW_SHOW | 正常显示 | SW_SHOWNA | 显示窗体带不激活(焦点仍在上一个窗体) | SW_SHOWNORMAL | 如果窗体最小化了或者最大化了,会被还原成正常的大小 |
本例代码 :
ShowWindow(hWnd, SW_SHOW);
5 更新窗体
函数原型:
BOOL UpdateWindow(HWND hWnd);
参数: 要更新的窗体句柄
本例代码:
UpdateWindow(hWnd);
6 消息循环
6.1 消息类型MSG
定义一个消息对象(结构体)
MSG msg;
6.2 获取消息: GetMessage
函数原型:
BOOL WINAPI GetMessage(
_Out_ LPMSG lpMsg,
_In_opt_ HWND hWnd,
_In_ UINT wMsgFilterMin,
_In_ UINT wMsgFilterMax
);
第一个参数: 消息对象指针
第二个参数: 消息传入窗体句柄
第三个参数: 消息过滤器, 消息标识的最小值
第四个参数: 消息过滤器,消息标识的最大值
注意:
(1)最后两个参数如果不想过滤,统一设置成0. 如果设置了,不在范围内的消息将不被接收
(2) 本函数只在获取消息WM_QUIT时才会返回false,否则都为true
因此,要想中止消息队列循环,必须在适当的时机发送WM_QUIT消息
发送方式为调用函数:
PostQuitMessage(0);
6.3 消息循环
while(GetMessage(&msg, NULL, 0, 0)){
TranslateMessage(&msg); //将虚拟键消息转换为字符消息
DispatchMessage(&msg); //将虚拟键消息分发给窗口处理函数
}
循环体里的两个函数:
(1) 函数原型
BOOL TranslateMessage(
const MSG* lpMsg
);
函数功能: 将虚拟按键消息转换为字符消息, 即将按键翻译字符,即处理GetMessage函数接收到的消息(msg)对象
使之能够在后续的处理中以字符的消息来处理
(2)函数原型
LONG DispatchMessage(
const MSG* lpmsg
);
函数功能: 调用消息处理函数来处理消息
完整示例代码
(1)main.cpp
#include<Windows.h>
#include "resource.h"
//窗口应用程序入口函数: WinMain
//命令行程序入入口函数: main
LRESULT CALLBACK fWinProc(HWND, UINT,WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPreinstance, LPSTR lpCmdline, int nCmdShow){
WNDCLASS wc;
LPCTSTR lpClassName = L"MyWindowClass";
wc.style = CS_VREDRAW | CS_HREDRAW; //设置显示风格
wc.lpfnWndProc = fWinProc; //消息处理函数
wc.cbClsExtra = 0; //窗口类的额外扩展空间
wc.cbWndExtra = 0; //窗口的窗外扩展空间
wc.hInstance = hInstance; //当前应用程序句柄
//wc.hIcon = NULL; //不设置窗口图标
wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON));
//wc.hCursor = NULL; //不设置光标
wc.hCursor = LoadCursor(hInstance, MAKEINTRESOURCE(IDC_CURSOR));
wc.hbrBackground = CreateSolidBrush(RGB(255, 0, 0));
wc.lpszMenuName = NULL;
wc.lpszClassName = lpClassName;
//(3)注册窗体类
if (0 == RegisterClass(&wc)){
MessageBox(NULL, L"注册失败", L"警告", MB_OK || MB_ICONERROR);
return 0;
}
// (4) 创建窗口句柄
HWND hWnd = CreateWindow(
//lpClassName, //窗口类型名
lpClassName,
L"第一个窗体", //窗口s标题
//带标题栏 系统菜单(关闭按钮) 最小化按钮 最大按钮
WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,//带标题栏的窗体
0, //左上角x坐标
0, //左上角y坐标
600, //窗体宽度
600, //窗体高度
NULL, //没有父窗体
NULL, //没有菜栏
hInstance, //所属应用程序句柄
NULL, //附加参数
);
if (hWnd == NULL){
MessageBox(NULL, L"窗体创建失败", L"警告", MB_ICONWARNING);
return 0;
}
//(5)显示窗体
ShowWindow(hWnd, SW_SHOW);
//(4)更新窗体
UpdateWindow(hWnd);
//(5) 消息队列
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)){
TranslateMessage(&msg); //将虚拟键消息转换为字符消息
DispatchMessage(&msg); //将虚拟键消息分发给窗口处理函数
}
return 0;
}
LRESULT CALLBACK fWinProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg){
case WM_CLOSE: //处理关闭按钮消息
DestroyWindow(hWnd); //销毁窗口
break;
case WM_DESTROY:
PostQuitMessage(0); //发送退出消息
break;
}
//将不处理的函数交给系统默认处理
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
资源文件: resource.h
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ 生成的包含文件。
// 供 Demo01创建窗口.rc 使用
//
#define IDI_ICON 101 //这两行是关键
#define IDC_CURSOR 102 //这两行是关键
// Next default values for new objects
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 103
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
|