使用Win32/ATL建立窗口的过程
acmilan2015/07/02软件综合 IP:四川
有时候想写个几十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 项目
捕获.png

点击确定,再点击下一步,选上ATL支持(注意此时MFC是灰色的)点击完成以新建工程
捕获2.png

二、打开MyAtlWindowTest.cpp,删减示例代码

原因是我们不需要采用传统方法来新建窗口

捕获3.png

剩下的代码如下:
<code class="lang-cpp">// 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;
}</code>

三、在stdafx.h添加头文件atlwin.h

捕获4.png

向导只给我们添加了基本的atlbase.h和atlstr.h支持,并没有给我们添加窗口支持,因此要手动添加:
<code class="lang-cpp">#include <atlwin.h></atlwin.h></code>

四、添加CMainWindow实现

ATL窗口最基本的形式如下:
class 自己的窗口类 : public CWindowImpl<自己的窗口类, 基类=CWindow, 特性类=CControlWinTraits> {
public:
    BEGIN_MSG_MAP(自己的窗口类) // 利用宏实现ProcessWindowMessage消息分发函数
    END_MSG_MAP()
};


因此最简单的代码如下:
<code class="lang-cpp">// TODO: 实现窗口类CMainWindow
class CMainWindow : public CWindowImpl<cmainwindow> { // 主窗口,基于CWindowImpl模板
public:
    BEGIN_MSG_MAP(CMainWindow) // 利用宏实现ProcessWindowMessage函数,用以分发消息
    END_MSG_MAP()
};</cmainwindow></code>

在这里我们实现了如下的代码(当然你也可以使用上边的代码):
<code class="lang-cpp">// 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;
    }
};</idd_aboutbox></cmainwindow></code>

五、在WinMain中加载窗口

加载一个Win32窗口很麻烦,但是加载一个ATL窗口是很简单的事情
——根本不用操心窗口类的注册,因为Create函数会自动为我们注册一个。

在WinMain中加载CMainWindow窗口:
<code class="lang-cpp">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;
}</code>

六、运行

捕获5.png

七、发布

将默认目标改为Release,右击项目->属性->C/C++->代码生成,运行库设置为『多线程 (/MT)』,以便可以免运行库:
捕获7.png

按F7生成,然后打开项目父目录,找到Release文件夹(不是项目子目录下的Release),可以找到我们可以发布的程序:
捕获6.png

八、总结

通过ATL,我们使用很短的代码就实现了一个标准的Windows窗口,比用传统的Win32方法不知道高到哪里去了,然而程序的体积并没有大幅度的增长,相对于MFC,还算是轻量级的。[s::lol]

[修改于 8年11个月前 - 2015/07/02 21:22:37]

来自:计算机科学 / 软件综合
1
已屏蔽 原因:{{ notice.reason }}已屏蔽
{{notice.noticeContent}}
~~空空如也
acmilan 作者
8年11个月前 修改于 8年11个月前 IP:四川
776552
在Win32中,控件其实就是WS_CHILD样式的子窗口,公共控件则是使用预定义lpszClassName创建的WS_CHILD子窗口。

在消息处理模式上,对话框的处理比较不同(DlgProc和WndProc写法不同),而其它窗口都差不多。

如果对Win32编程比较熟练,那么使用ATL编写窗口就足够了,如果想更方便地写程序可以考虑用WTL来增强ATL的功能。当然,如果对Win32编程不是很熟练,那还是用MFC吧。[s::lol]
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论

想参与大家的讨论?现在就 登录 或者 注册

所属专业
所属分类
上级专业
同级专业
acmilan
进士 学者 笔友
文章
461
回复
2934
学术分
4
2009/05/30注册,5年3个月前活动
暂无简介
主体类型:个人
所属领域:无
认证方式:邮箱
IP归属地:未同步
文件下载
加载中...
{{errorInfo}}
{{downloadWarning}}
你在 {{downloadTime}} 下载过当前文件。
文件名称:{{resource.defaultFile.name}}
下载次数:{{resource.hits}}
上传用户:{{uploader.username}}
所需积分:{{costScores}},{{holdScores}}下载当前附件免费{{description}}
积分不足,去充值
文件已丢失

当前账号的附件下载数量限制如下:
时段 个数
{{f.startingTime}}点 - {{f.endTime}}点 {{f.fileCount}}
视频暂不能访问,请登录试试
仅供内部学术交流或培训使用,请先保存到本地。本内容不代表科创观点,未经原作者同意,请勿转载。
音频暂不能访问,请登录试试
支持的图片格式:jpg, jpeg, png
插入公式
评论控制
加载中...
文号:{{pid}}
投诉或举报
加载中...
{{tip}}
请选择违规类型:
{{reason.type}}

空空如也

加载中...
详情
详情
推送到专栏从专栏移除
设为匿名取消匿名
查看作者
回复
只看作者
加入收藏取消收藏
收藏
取消收藏
折叠回复
置顶取消置顶
评学术分
鼓励
设为精选取消精选
管理提醒
编辑
通过审核
评论控制
退修或删除
历史版本
违规记录
投诉或举报
加入黑名单移除黑名单
查看IP
{{format('YYYY/MM/DD HH:mm:ss', toc)}}