微软基础类库(英语:Microsoft Foundation Classes,简称MFC)是微软公司提供的一个类库(class libraries),以C++类的形式封装了Windows API,并且包含一个应用程序框架,以减少应用程序开发人员的工作量。其中包含大量Windows句柄封装类和很多Windows的内建控件和组件的封装类。
MFC(微软基础类库)是对于Windows平台下做GUI开发的一个很好的选择。
要想熟练掌握 Windows 应用程序的开发, 首先需要理解 Windows 平台下程序运行的内部机制。如果想要更好的学习掌握 MFC,必须要先了解Windows 程序的内部运行机制。
在Windows平台下,也有类似标准库下的函数可供调用:不同的是,这些函数是由Windows操作系统本身提供的(windows API)。
SDK 和 API
窗口和句柄
消息与消息队列
Windows 程序设计是一种完全不同于传统的 DOS 方式的程序设计方法。它是一种事件驱动方式的程序设计模式,主要是基于消息的。
每一个 Windows 应用程序开始执行后, 系统都会为该程序创建一个消息队列, 这个消息队列用来存放该程序创建的窗口的消息。
例如,当用户在窗口中画图的时候,按下鼠标左键,此时,操作系统会感知到这一事件,于是将这个事件包装成一个消息,投递到应用程序的消息队列中,等待应用程序的处理。然后应用程序通过一个消息循环不断地从消息队列中取出消息,并进行响应。
在这个处理过程中,操作系统也会给应用程序“ 发送消息”。所谓“ 发送消息”,实际上是操作系统调用程序中一个专门负责处理消息的函数,这个函数称为窗口过程。
WinMain函数
当Windows操作系统启动一个程序时,它调用的就是该程序的WinMain函数( 实际是由插入到可执行文件中的启动代码调用的)。 WinMain是Windows程序的入口点函数,与DOS程序的入口点函数main的作用相同,当WinMain 函数结束或返回时,Windows应用程序结束。
一个完整的Win32程序(#include
创建项目
在visual studio下创建一个空白的win32应用程序项目。
int WINAPI WinMain(
HINSTANCE hInstance, //应用程序实例
HINSTANCE hPrevInstance, //上一个应用程序实例
LPSTR lpCmdLine, //命令行参数
int nShowCmd); //窗口显示的样式
创建一个窗口
创建一个完整的窗口,需要经过下面几个步骤:
typedef struct _WNDCLASS{
UINT style;//窗口样式
WNDPROC lpfnWndProc;//窗口回调函数
int cbClsExtra;
int cbWndExtra;
HINSTANCE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCWSTR lpszMenuName;
LPCWSTR lpszClassName;
} WNDCLASS;
ATOM RegisterClass(CONST WNDCLASS *lpWndClass);
//使用示例:RegisterClass(&wc);
//返回值说明:如果窗口创建成功,CreateWindow函数将返回系统为该窗口分配的句柄,否则,返回NULL。
HWND CreateWindow(
LPCTSTR lpClassName,//指定窗口类的名称,此名字必须和WNDCLASS的lpszClassName成员指定的名称一样。
LPCTSTR lpWindowName,//指定窗口的名字,即窗口的标题。
DWORD dwStyle,//指定创建的窗口的样式,常指定为指WS_OVERLAPPEDWINDOW类型,这是一种多种窗口类型的组合类型。
int x,int y,//指定窗口左上角的x,y坐标。如果参数x被设为CW_USEDEFAULT,那么系统为窗口选择默认的左上角坐标并忽略y参数。
int nWidth,int nHeight,//指定窗口窗口的宽度,高度。如果参数nWidth被设为 CW_USEDEFAULT,那么系统为窗口选择默认的宽度和高度,参数nHeight被忽略。
HWND hWndParent,//指定被创建窗口的父窗口句柄,没有父窗口,则设置NULL。
HMENU hMenu,//指定窗口菜单的句柄,没有,则设置为NULL。
HINSTANCE hInstance,//窗口所属的应用程序实例的句柄,用WinMain中的形参hInstance为其赋值。
LPVOID lpParam);//作为WM_CREATE消息的附加参数lParam传入的数据指针。通常设置为NULL。
BOOL ShowWindow(HWND hWnd, int nCmdShow);
更新窗口函数原型:
BOOL UpdateWindow(HWND hWnd);
//示例
ShowWindow(hWnd, SW_SHOWNORMAL); //SW_SHOWNORMAL为普通模式
UpdateWindow(hWnd);
typedef struct _WNDCLASS{
UINT style;//窗口样式
WNDPROC lpfnWndProc;//窗口回调函数
int cbClsExtra;
int cbWndExtra;
HINSTANCE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCWSTR lpszMenuName;
LPCWSTR lpszClassName;
} WNDCLASS;
类型 | 含义 |
---|---|
CS_HREDRAW | 当窗口水平方向上的宽度发生变化时, 将重新绘制整个窗口。 当窗口发生重绘时, 窗口中的文字和图形将被擦除。如果没有指定这一样式,那么在水平方向上调整窗口宽度时,将不会重绘窗口。 |
CS_VREDRAW | 当窗口垂直方向上的高度发生变化时,将重新绘制整个窗口。如果没有指定这一样式,那么在垂直方向上调整窗口高度时,将不会重绘窗口。 |
CS_NOCLOSE | 禁用系统菜单的 Close 命令,这将导致窗口没有关闭按钮。 |
CS_DBLCLKS | 当用户在窗口中双击鼠标时,向窗口过程发送鼠标双击消息。 |
LRESULT CALLBACK WindowProc(
HWND hWnd, //信息所属的窗口句柄
UINT uMsg, //消息类型
WPARAM wParam, //附加信息(如键盘哪个键按下)
LPARAM lParam //附加信息(如鼠标点击坐标)
);
HICON LoadIcon(HINSTANCE hInstance, LPCTSTR lpIconName);
如:LoadIcon(NULL, IDI_WARNING); //第一个参数为NULL,加载系统默认图标
HCURSOR LoadCursor(HINSTANCE hInstance, LPCTSTR lpCursorName);
如:LoadCursor(NULL, IDC_HELP); //第一个参数为NULL,加载系统默认光标
HGDIOBJ GetStockObject(int fnObject);
如:GetStockObject(WHITE_BRUSH);
示例代码
WNDCLASS wc; //窗口类变量
wc.cbClsExtra = 0; //类附加内存
wc.cbWndExtra = 0; //窗口附加内存
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); //背景色为白色
wc.hCursor = (HCURSOR)LoadCursor(NULL, IDC_HELP); //帮助光标
wc.hIcon = (HICON)LoadIcon(NULL, IDI_WARNING); //警告图标
wc.hInstance = hInstance; //应用程序实例,为WinMain第1个形参
wc.lpfnWndProc = WinProc; //窗口过程函数名字
wc.lpszClassName = TEXT("MyWin"); //类的名字
wc.lpszMenuName = NULL; //没有菜单
wc.style = 0; //类的风格,填0,使用默认风格
//注册窗口类
RegisterClass(&wc);
//创建窗口
HWND hWnd = CreateWindow(TEXT("MyWin"), TEXT("测试"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
//显示及更新窗口
ShowWindow(hWnd, SW_SHOWNORMAL);
UpdateWindow(hWnd);
在创建窗口、显示窗口、更新窗口后,我们需要编写一个消息循环,不断地从消息队列中取出消息,并进行响应。
消息结构体
在Windows程序中,消息是由MSG结构体来表示的。MSG结构体的定义如下:
typedef struct tagMSG {
HWND hWnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
} MSG;
取消息
要从消息队列中取出消息,我们需要调用GetMessage()函数,该函数的原型声明如下:
BOOL GetMessage(
LPMSG lpMsg,
HWND hWnd,
UINT wMsgFilterMin,
UINT wMsgFilterMax);
参数说明:
建立消息循环
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
窗口过程函数(消息处理函数)
在完成上述步骤后,剩下的工作就是编写一个窗口过程函数,用于处理发送给窗口的消息。
窗口过程函数的名字可以随便取, 如WinProc, 但函数定义的形式必须和下面声明的形式相同:
LRESULT CALLBACK WinProc( //CALLBACK 和WINAPI 作用一样
HWND hWnd, //信息所属的窗口句柄
UINT uMsg, //消息类型
WPARAM wParam, //附加信息(如键盘哪个键按下)
LPARAM lParam //附加信息(如鼠标点击坐标)
);
#include
//处理消息
LRESULT CALLBACK Windowproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_LBUTTONDOWN://左键按下
{
int xPos, yPos;
xPos = LOWORD(lParam);
yPos = HIWORD(lParam);
//打印操作
TCHAR buf[1024];
wsprintf(buf, TEXT("x = %d, y = %d "), xPos, yPos);
MessageBox(hwnd, buf, TEXT("鼠标按下"), MB_OK);
}
break;
case WM_KEYDOWN://按下键盘
MessageBox(hwnd, TEXT("键盘按下"), TEXT("键盘消息"), MB_OK);
break;
case WM_PAINT://绘图消息
{
PAINTSTRUCT ps;//绘图结构体
HDC hdc = BeginPaint(hwnd, &ps);
//绘制文字
TextOut(hdc, 100, 100, TEXT("hello world"), strlen("hello world"));
EndPaint(hwnd, &ps);
}
break;
default:
break;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
//实现底层窗口步骤
//1、设计窗口类
//2、注册窗口类
//3、创建窗口类
//4、通过循环获取消息
//5、处理消息(窗口过程)
//设计窗口
WNDCLASS wc;
wc.cbClsExtra = 0;//额外内存为0
wc.cbWndExtra = 0;//窗口额外内存
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);//设置背景
wc.hCursor = LoadCursor(NULL, IDC_HAND);//设置光标
wc.hIcon = LoadIcon(NULL, IDI_WARNING);//设置鼠标
wc.hInstance = hInstance;//当前实例句柄.
wc.lpfnWndProc = Windowproc;//窗口过程回调函数.自定义
wc.lpszClassName = TEXT("WINDOW");//指定窗口类名
wc.lpszMenuName = NULL;//菜单名
wc.style = 0;//默认风格
//注册窗口类
RegisterClass(&wc);
//创建窗口类
/*
lpClassName, 类名
lpWindowName, 窗口名
dwStyle, 显示风格
x, y, 窗口起始坐标
nWidth,窗口宽度
nHeight,高度
hWndParent,父窗口, NULL
hMenu, 菜单,NULL
hInstance,实例句柄
lpParam,其他参数
*/
HWND hwnd = CreateWindow(wc.lpszClassName, TEXT("TEXT WINDOW"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
//显示和更新
ShowWindow(hwnd, SW_SHOWNORMAL);
UpdateWindow(hwnd);
//通过循环取消息
MSG msg;
while (1)
{
if (GetMessage(&msg, NULL, 0, 0) == FALSE)
{
break;
}
//翻译消息
TranslateMessage(&msg);
//分发消息
DispatchMessage(&msg);
}
return 0;
}