在MFC中使用WTL

http://www.codeproject.com/wtl/mix_wtl_mfc.asp?df=100&forumid=14171&exp=0&select=666091#xx666091xx

What is it?

This article presents a way to use WTL template classes on MFC window classes, that is how to transform the MFC CWnd class to its ATL/WTL counterpart CWindow, while leaving the class usable from MFC code.

Here you can find the required files, a detailed explanation in 10 steps and a working demo.

How to do it, step by step

These are the basic steps to obtain hybrid MFC/WTL windows. They have been used on MFC dialog based projects, but should work with any MFC application.

Add WTL support to an MFC application

  1. Take an MFC project you want to modify so that it supports ATL/WTL functions and classes.
  2. Open its precompiled header file (usually "stdafx.h") and add the following lines:
    // Add support for ATL/WTL
    #define _WTL_NO_AUTOMATIC_NAMESPACE

    This prevents the WTL headers to automatically merge the WTL namespace to the global namespace. This avoids conflicts with MFC classes with the same names, such as CRect, CDC and others.

    #include <atlbase.h>
    #include <atlapp.h>
    
    extern WTL::CAppModule _Module;

    The ATL/WTL code may access the global _Module variable, so it must have external linkage.

    #include <atlwin.h>

    We add the common WTL header here to exploit precompiled headers, but you may want to include it only where you really need it.

  3. Open the project main file, where the CWinApp-derived class is implemented, and modify it as follows:
    /
    // The one and only CMixedWindowApp object
    
    CMixedWindowApp theApp;
    WTL::CAppModule _Module;	// add this line

    The global _Module variable must be defined somewhere and this is a good place.

  4. Modify InitInstance() as follows:
    /
    // CMixedWindowApp initialization
    
    BOOL CMixedWindowApp::InitInstance()
    {
        // Initialize ATL
        _Module.Init(NULL, AfxGetInstanceHandle());
    
        ...
    }
  5. If not present, add the ExitInstance() virtual function to the application object, for example using ClassWizard, then modify it as follows:
    int CMixedWindowApp::ExitInstance() 
    {
        // Terminate ATL
        _Module.Term();
    
        return CWinApp::ExitInstance();
    }

Make an hybrid window class

Assume we want to add scrolling capabilities to a static bitmap control. We may want to use the WTL template class CScrollImpl<...> together with the MFC class CStatic, as there is no such a window class in MFC.

  1. Make a new class, derived from CStatic (or any other CWnd-derived class), for example using ClassWizard.
  2. Open the class header file and include any WTL header you need:
    // Add support for scrolling windows
    #include <atlscrl.h>

    Remember that if you didn't choose to use the precompiled header at point 3 above, you need to add that line here, before any other WTL header:

    #include <atlwin.h>
  3. Include the necessary header to turn a CWnd class into a CWindow. It contains a template class that defines the missing members:
    // Make this class CWindow compatible
    #include "Wnd2Window.h"

    You may want to put the above line into your precompiled header instead, like all the other WTL headers, if you use them many times, to speed up recompilation. That's completely at your choice.

  4. Then modify the class declaration to make the conversion to CWindow and to use the WTL template class you have chosen:
    class CScrollPicture : public CWnd2Window<CScrollPicture, CStatic>,
                           public WTL::CScrollImpl<CScrollPicture>
  5. Add any other function the WTL class may require, paying attention to specify the WTL namespace for its arguments. In this example we need to implement DoPaint() to work with scrolling:
    public:
        // Inline helper
        void DoPaint(WTL::CDCHandle dc)
        {
            OnDraw(CDC::FromHandle(dc));
        }
    
    protected:
        // Implement painting with scroll support
        void OnDraw(CDC* pDC);

Notes on implementation

It may be a good idea not to mix the code too much with MFC and WTL classes used anywhere, as it could become quite confusing. An efficient way could be defining inline helper functions that translates WTL arguments to their MFC counterpart, unless you want to write the required functions using only WTL. Note that you can safely mix WTL objects with MFC code, but you always need to specify the WTL namespace when appropriate.

Using helper functions could also make writing "bridge" classes easier. For example, you could define a CScrollWnd class to reuse (through inheritance) whenever you need scrolling capabilities and custom drawing, declaring a virtual OnDraw() function like in CView and CScrollView:

public:
    // Inline helper
    void DoPaint(WTL::CDCHandle dc)
    {
        OnDraw(CDC::FromHandle(dc));
    }

protected:
    // Must be implemented in derived class
    virtual void OnDraw(CDC* pDC) = 0;

I used a pure virtual function, but CView defines a base implementation that just fires an assertion. These are just some ideas, but feel free to suggest new.

Sounds good... but how does it work?

Well, nothing special or mysterious. The ATL class CWindow, which is used by WTL, has only one member variable, m_hWnd, that is exactly the same as the one you can find in the CWnd class. So all the CWindow member functions need just that member variable, that can also be found in a CWnd-derived class.

What I did was to copy all the CWindow members, except m_hWnd, to a include file and define a new template class.

template <class T, class TBase> class CWnd2Window : public TBase

To use the template you need to pass both the derived class and the base class, as you can see in the example above about a scrolling control.

Then I added the necessary code to enable WTL message maps. I implemented the MFC function DefWindowProc(), that is called after WindowProc(), when no entry is found in the MFC message map.

protected:
    virtual LRESULT DefWindowProc(UINT nMsg, WPARAM wParam, LPARAM lParam)
    {
        T* pT = static_cast<T*>(this);
        ATLASSERT(::IsWindow(pT->m_hWnd));

        LRESULT lResult;
        if (pT->ProcessWindowMessage(m_hWnd, nMsg, wParam, lParam, lResult))
                return lResult;

        return TBase::DefWindowProc(nMsg, wParam, lParam);
    }

Remember that WindowProc() is called first. If it is not overridden or if you pass the message to the base implementation in CWnd, the MFC message handler is called. If the corresponding entry is not found in the MFC message map or if you call the base implementation in your message handler or if you explicitly call Default(), the message finally arrives to DefWindowProc() that calls the ATL/WTL message map implementation.

Note that you don't have to change the MFC message map macros, that skip to the MFC base class ignoring the intermediate template CWnd2Window. I don't define an MFC message map there, so this is perfectly legal.

I also commented out "dangerous" member functions already defined by MFC, such as Attach(), Detach(), CreateWindow() and DestroyWindow(), but there may be others to comment out I'm not aware of. As far as I know Attach/Detach deal with CWnd maps, while CreateWindow/DestroyWindow set up Windows hooks or call some virtual functions. So if you still want to use your class as a CWnd, you need to call the original functions, not those defined by CWindow.

There may be also some functions identical to the MFC ones, that could be removed. Feel free to suggest improvements.

So far, so good... but what changes?

Well, you have to pay attention to a few things:

  • You need to explicitly specify namespaces when accessing WTL objects, which is not a bad practice anyway.
  • And you may find that the compiler complains about some member functions of your hybrid classes. That's because many CWnd and CWindow members have the same name and you must resolve the ambiguity to compile.

That's all. I just used this method a couple of times, so I'm not aware of any other problems. But I expect comments...

License

I suppose you can freely use the code as long as you have the right to use ATL source code. There is no original code here, except for the CScrollPicture class, only a nice idea. All the source code by me is released to the public domain, you may do what you want with it. As for the CWnd2Window class, almost all the code is copyright of Microsoft Corporation.

The demo project

In the attached demo project you can find a very simple implementation of a scrolling picture control. Since I subclass a CStatic control, I need to override the default behaviour sometimes. A much cleaner implementation would have used a CWnd, but that way I can show that message routing works as expected, even mixing MFC message handlers with WindowProc().

To test the control use the mouse on the scroll bars or move the focus on it and use the keyboard arrows, holding down the CTRL key to scroll by pages. There is no focus indicator, so you have to guess by exclusion. There is also a button to load external BMP files.

Please note that this control is not meant to be fully featured, but only an example of use for WTL templates in MFC projects. In particular, I expect bugs if the control is used in a resizable dialog, as to my experience the WTL classes that implement scrolling are not "perfect" from that point of view. I'm working on a replacement class, but no time to release it yet.

Any idea to improve this article, please let me know!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
WTL 具有两面性,确实是这样的。它没有MFC的界面(GUI)类库那样功能强大,但是能够生成很小的可执行文件。如果你象我一样使用MFC进行界面编程,你会觉得MFC提供的界面控件封装使用起来非常舒服,更不用说MFC内置的消息处理机制。当然,如果你也象我一样不希望自己的程序仅仅因为使用MFC的框架就增加几百K的大小的话,WTL就是你的选择。当然,我们还要克服一些障碍: ATL样式的模板类初看起来有点怪异 没有类向导的支持,所以要手工处理所有的消息映射。 MSDN没有正式的文档支持,你需要到处去收集有关的文档,甚至是查看WTL的源代码。 买不到参考书籍 没有微软的官方支持 ATL/WTL的窗口与MFC的窗口有很大的不同,你所了解的有关MFC的知识并不全部适用与WTL。 从另一方面讲,WTL也有它自身的优势: 不需要学习或掌握复杂的文档/视图框架。 具有MFC的基本的界面特色,比如DDX/DDV和命令状态的自动更新功能(译者加:比如菜单的Check标记和Enable标记)。 增强了一些MFC的特性(比如更加易用的分隔窗口)。 可生成比静态链接的MFC程序更小的可执行文件(译者加:WTL的所有源代码都是静态链接到你的程序的)。 你可以修正自己使用WTL的错误(BUG)而不会影响其他的应用程序(相比之下,如果你修正了有BUG的MFC/CRT动态库就可能会引起其它应用程序的崩溃。 如果你仍然需要使用MFCMFC的窗口和ATL/WTL的窗口可以“和平共处”。(例如我工作的一个原型就使用了了MFC的CFrameWnd,并在其内包含了WTL的CSplitterWindow,在CSplitterWindow使用MFC的CDialogs -- 我并不是为了炫耀什么,只是修改了MFC的代码使之能够使用WTL的分割窗口,它比MFC的分割窗口好的多)。 在这一系列文章,我将首先介绍ATL的窗口类,毕竟WTL是构建与ATL之上的一系列附加类,所以需要很好的了解ATL的窗口类。介绍完ATL之后我将介绍WTL的特性以并展示它是如何使界面编程变得轻而易举。
目录 WTL for MFC Programmers, Chinese Version Prologue WTL for MFC Programmers, Chinese Version PrologueWTL for MFC Programmers, Chinese Version Prologue WTL for MFC Programmers, Chinese Version Prologue WTL for MFC Programmers, Chinese Version Prologue WTL for MFC Programmers, Chinese Version PrologueWTL for MFC Programmers, Chinese Version PrologueWTL for MFC Programmers, Chinese Version Prologue WTL for MFC Programmers, Chinese Version PrologueWTL for MFC Programmers, Chinese Version Prologue WTL for MFC Programmers, Chinese Version Prologue WTL for MFC Programmers, Chinese Version PrologueWTL for MFC Programmers, Chinese Version PrologueWTL for MFC Programmers, Chinese Version PrologueWTL for MFC Programmers, Chinese Version Prologue WTL for MFC Programmers, Chinese Version PrologueWTL for MFC Programmers, Chinese Version PrologueWTL for MFC Programmers, Chinese Version PrologueWTL for MFC Programmers, Chinese Version Prologue WTL for MFC Programmers, Chinese Version PrologueWTL for MFC Programmers, Chinese Version PrologueWTL for MFC Programmers, Chinese Version PrologueWTL for MFC Programmers, Chinese Version PrologueWTL for MFC Programmers, Chinese Version Prologue WTL for MFC Programmers, Chinese Version Prologue WTL for MFC Programmers, Chinese Version Prologue WTL for MFC Programmers, Chinese Version PrologueWTL for MFC Programmers, Chinese Version Prologue WTL for MFC Programmers, Chinese Version Prologue WTL for MFC Programmers, Chinese Version Prologue ................................ ........................... 6 WTL for MFC Programmers, Part I - ATL GUI Classes ................................ ........................... 8 README.TXTREADME.TXT README.TXTREADME.TXT README.TXTREADME.TXT ................................ ................................ ................................ ......................... 8 对本系列文章的总体介绍 对本系列文章的总体介绍 对本系列文章的总体介绍 ................................ ................................ ................................ ....... 9 对第一章的简单介绍 对第一章的简单介绍 ................................ ................................ ................................ ............. 11 ATL ATL ATL 背景知识 ................................ ................................ ................................ ....................... 11 ATL 和 WTL 的发展历史 ................................ ................................ ......................... 12 ATL-style 模板 ................................ ................................ ................................ ............. 12 ATL ATL ATL 窗口类 ................................ ................................ ................................ ........................... 15 定义一个窗口的实现 定义一个窗口的实现 ................................ ................................ ................................ ............. 17 填写消息映射链 ................................ ................................ ................................ ............. 19 高级消息映射链和嵌 高级消息映射链和嵌 入类 ................................ ................................ ................................ ..... 21 ATLATL 程序的结构 ................................ ................................ ................................ .................... 24 ATLATL 的对话框 ................................ ................................ ................................ .................... 27 WTL for MFC Programmers, Part II - WTL GUI Base Classes ................................ ............. 32 对第二部分的介绍 对第二部分的介绍 ................................ ................................ ................................ ................. 32 WTL WTL WTL WTL 总体印象 ................................ ................................ ................................ ...................... 32 开始写 WTLWTLWTL程序 ................................ ................................ ................................ .................. 33 WTL WTL WTL WTL 对消息映射的增强 ................................ ................................ ................................ ...... 36 从 WTLWTLWTL的应用程序生成向导能得到什么 的应用程序生成向导能得到什么 的应用程序生成向导能得到什么 ................................ ................................ .......... 42 使用向导的整个过程 ................................ ................................ ................................ ..... 42 查看生成的代码 ................................ ................................ ................................ ............. 44 CMessageLoop CMessageLoop CMessageLoop CMessageLoop CMessageLoop CMessageLoop CMessageLoop CMessageLoop 的内部实现 ................................ ................................ ................................ .. 47 CFrameWindowImpl CFrameWindowImpl CFrameWindowImpl CFrameWindowImpl CFrameWindowImpl CFrameWindowImpl CFrameWindowImpl CFrameWindowImpl CFrameWindowImpl CFrameWindowImpl CFrameWindowImpl CFrameWindowImpl CFrameWindowImpl 的内部实现 ................................ ................................ ......................... 48 回到前面的时钟程序 回到前面的时钟程序 ................................ ................................ ................................ ............. 50 界面元素的自动更新 界面元素的自动更新 (UI Updating)(UI Updating)(UI Updating)(UI Updating) (UI Updating) (UI Updating)(UI Updating)(UI Updating)(UI Updating) (UI Updating) ................................ ................................ ...................... 51 添加控制时钟的新菜单项 ................................ ................................ ............................. 52 调用 UIEnable()................................ ................................ ................................ ............ 53 消息映射链最后需要注意的地方 消息映射链最后需要注意的地方 消息映射链最后需要注意的地方 消息映射链最后需要注意的地方 ................................ ................................

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值