有时候想写个几十kb的小程序,MFC实在是太大了,Win32有时又太麻烦,怎么办呢?用ATL写会更方便和轻量级一些 ATL和MFC不同的是,ATL并没有强制性封装WinMain函数,仅封装了WndProc,所以使用ATL写Windows程序有很高的自由度
ATL的窗口架构是这样的——
【两个底层封装类】 CWindow 窗口句柄和API封装类 只封装了hWnd窗口句柄和与之有关的WinAPI,CWindow和hWnd可以方便地进行转换。 CMessageMap 消息映射接口 该基类有一个待实现的函数ProcessWindowMessage,用以分发消息,可使用宏实现: BEGIN_MSG_MAP(CMyClass) END_MSG_MAP()
【两个窗口类实现模板】(最终多继承自CWindow和CMessageMap) CWindowImpl<T> 自定义窗口模板(实现了WNDCLASS和WndProc) 可选参数:<T, TBase = CWindow, TWinTraits = CControlWinTraits> 通过继承CWindowImpl<CMyWindow>,并实现消息映射,可以实现一个自定义窗口CMyWindow。 CDialogImpl<T> 自定义对话框模板(实现了DlgProc) 可选参数:<T, TBase = CWindow> 通过继承CDialogImpl<CMyDialog>,并实现消息映射、资源绑定,可以实现一个自定义对话框CMyDialog。 资源绑定的实现:enum { IDD = IDD_DIALOG };
【两个即刻可用的窗口类】 CSimpleDialog<IDD_DIALOG> 简单对话框 可选参数:<IDD_DIALOG, bCenter = TRUE> 用来创建只有确定和取消的简单对话框,使用这个类就不需要每次都从CDialogImpl<T>派生了。 CContainedWindow 被容纳的窗口 可选参数:CContainedWindowT<TBase = CWindow, TWinTraits = CControlWinTraits> 可以用来创建子窗口(控件),也可以SubclassWindow来绑定它们,这样就不用每次从CWindowImpl<T>派生了。 这个类将消息路由到父窗口的ALT_MSG_MAP(n),方便接收子窗口消息,自己并不进行消息分发。
以及一些附加的类和模板,如CWinTraits<>、CWinTraitsOR<>、CWndClassInfo等。 一、新建一个支持ATL的Win32项目
新建一个项目,选择Visual C++ -> Win32 -> Win32 项目
点击确定,再点击下一步,选上ATL支持(注意此时MFC是灰色的)点击完成以新建工程
二、打开MyAtlWindowTest.cpp,删减示例代码
原因是我们不需要采用传统方法来新建窗口
图片:捕获3.PNG
剩下的代码如下:
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
// MyAtlWindowTest.cpp : 定义应用程序的入口点。
//
#include "stdafx.h"
#include "MyAtlWindowTest.h"
// 全局变量:
HINSTANCE
hInst;
// 当前实例
// TODO: 实现窗口类CMainWindow
int
APIENTRY _tWinMain(
HINSTANCE
hInstance,
HINSTANCE
hPrevInstance,
LPTSTR
lpCmdLine,
int
nCmdShow)
{
MSG msg;
hInst = hInstance;
// 将实例句柄存储在全局变量中
// TODO: 初始化窗口
// 主消息循环:
while
(GetMessage(&msg, NULL, 0, 0))
// 消息循环 - 等待消息
{
TranslateMessage(&msg);
// 消息循环 - 翻译键盘消息
DispatchMessage(&msg);
// 消息循环 - 分发消息
}
return
(
int
) msg.wParam;
}
|
三、在stdafx.h添加头文件atlwin.h
图片:捕获4.PNG
向导只给我们添加了基本的atlbase.h和atlstr.h支持,并没有给我们添加窗口支持,因此要手动添加:
复制代码
1
|
#include <atlwin.h>
|
四、添加CMainWindow实现
ATL窗口最基本的形式如下: class 自己的窗口类 : public CWindowImpl<自己的窗口类, 基类=CWindow, 特性类=CControlWinTraits> { public: BEGIN_MSG_MAP(自己的窗口类) // 利用宏实现ProcessWindowMessage消息分发函数 END_MSG_MAP() };
因此最简单的代码如下:
复制代码
1
2
3
4
5
6
|
// TODO: 实现窗口类CMainWindow
class
CMainWindow :
public
CWindowImpl<CMainWindow> {
// 主窗口,基于CWindowImpl模板
public
:
BEGIN_MSG_MAP(CMainWindow)
// 利用宏实现ProcessWindowMessage函数,用以分发消息
END_MSG_MAP()
};
|
在这里我们实现了如下的代码(当然你也可以使用上边的代码):
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
// TODO: 实现窗口类CMainWindow
class
CMainWindow :
public
CWindowImpl<CMainWindow> {
// 主窗口,基于CWindowImpl模板
public
:
BEGIN_MSG_MAP(CMainWindow)
// 利用宏实现ProcessWindowMessage函数,用以分发消息
COMMAND_ID_HANDLER(IDM_ABOUT, OnAbout)
// if命令分发分支
COMMAND_ID_HANDLER(IDM_EXIT, OnExit)
// if命令分发分支
MESSAGE_HANDLER(WM_PAINT, OnPaint)
// if消息分发分支
MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
// if消息分发分支
END_MSG_MAP()
LRESULT
OnPaint(
UINT
uMsg,
WPARAM
wParam,
LPARAM
lParam,
BOOL
& bHandled) {
// ATL消息处理函数的标准形式
PAINTSTRUCT ps;
this
->BeginPaint(&ps);
// 开始绘图
// 在这里进行绘图操作
this
->EndPaint(&ps);
// 结束绘图
// bHandled如果不手动赋值FALSE的话,默认为TRUE
return
0;
}
LRESULT
OnDestroy(
UINT
uMsg,
WPARAM
wParam,
LPARAM
lParam,
BOOL
& bHandled) {
PostQuitMessage(0);
// 退出消息循环
return
0;
}
LRESULT
OnAbout(
WORD
wNotifyCode,
WORD
wID,
HWND
hWndCtl,
BOOL
& bHandled) {
// ATL命令处理函数的标准形式
CSimpleDialog<IDD_ABOUTBOX> dlg;
dlg.DoModal();
// 显示『关于』对话框
return
0;
}
LRESULT
OnExit(
WORD
wNotifyCode,
WORD
wID,
HWND
hWndCtl,
BOOL
& bHandled) {
this
->DestroyWindow();
// 点击文件->关闭时,销毁窗口
return
0;
}
};
|
五、在WinMain中加载窗口
加载一个Win32窗口很麻烦,但是加载一个ATL窗口是很简单的事情 ——根本不用操心窗口类的注册,因为Create函数会自动为我们注册一个。
在WinMain中加载CMainWindow窗口:
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
int
APIENTRY _tWinMain(
HINSTANCE
hInstance,
HINSTANCE
hPrevInstance,
LPTSTR
lpCmdLine,
int
nCmdShow)
{
MSG msg;
hInst = hInstance;
// 将实例句柄存储在全局变量中
// TODO: 初始化窗口
// 加载菜单资源
HMENU
hMenu = LoadMenu(hInstance, MAKEINTRESOURCE(IDC_MYATLWINDOWTEST));
// 创建窗口
CMainWindow wnd;
wnd.Create(NULL, CWindow::rcDefault, _T(
"My Window"
), WS_OVERLAPPEDWINDOW, WS_EX_CLIENTEDGE, hMenu);
// 显示并更新窗口
wnd.ShowWindow(nCmdShow);
wnd.UpdateWindow();
// 主消息循环:
while
(GetMessage(&msg, NULL, 0, 0))
// 消息循环 - 等待消息
{
TranslateMessage(&msg);
// 消息循环 - 翻译键盘消息
DispatchMessage(&msg);
// 消息循环 - 分发消息
}
return
(
int
) msg.wParam;
}
|
六、运行
七、发布
将默认目标改为Release,右击项目->属性->C/C++->代码生成,运行库设置为『多线程 (/MT)』,以便可以免运行库:
按F7生成,然后打开项目父目录,找到Release文件夹(不是项目子目录下的Release),可以找到我们可以发布的程序:
八、总结
通过ATL,我们使用很短的代码就实现了一个标准的Windows窗口,比用传统的Win32方法不知道高到哪里去了,然而程序的体积并没有大幅度的增长,相对于MFC,还算是轻量级的。