
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
- Take an MFC project you want to modify so that it supports ATL/WTL
functions and classes.
- Open its precompiled header file (usually
"stdafx.h")
and add the following lines:
#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.
- Open the project main file, where the
CWinApp-derived class
is implemented, and modify it as follows:
CMixedWindowApp theApp;
WTL::CAppModule _Module;
The global _Module variable must be defined somewhere and
this is a good place.
- Modify
InitInstance() as follows:
BOOL CMixedWindowApp::InitInstance()
{
_Module.Init(NULL, AfxGetInstanceHandle());
...
}
- If not present, add the
ExitInstance() virtual function to
the application object, for example using ClassWizard, then modify it as
follows:
int CMixedWindowApp::ExitInstance()
{
_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.
- Make a new class, derived from
CStatic (or any other CWnd-derived
class), for example using ClassWizard.
- Open the class header file and include any WTL header you need:
#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>
- Include the necessary header to turn a
CWnd class into a CWindow.
It contains a template class that defines the missing members:
#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.
- 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>
- 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:
void DoPaint(WTL::CDCHandle dc)
{
OnDraw(CDC::FromHandle(dc));
}
protected:
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:
void DoPaint(WTL::CDCHandle dc)
{
OnDraw(CDC::FromHandle(dc));
}
protected:
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!
| You must Sign In to use this message board. |
|
|
 |
|
 |
Hi! well done, however, it will be even great if you could should how we can use MFC in WTL Based project.
It is better of me!
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hello Paolo,
I would like to use a CSpinListBox in my MFC project on smartphone platform. SO I have downloaded your source file and commented the following functions to compile it on my platform
#ifndef UNDER_CE BOOL GetWindowPlacement(WINDOWPLACEMENT FAR* lpwndpl) const { ATLASSERT(::IsWindow(m_hWnd)); return ::GetWindowPlacement(m_hWnd, lpwndpl); }
BOOL SetWindowPlacement(const WINDOWPLACEMENT FAR* lpwndpl) { ATLASSERT(::IsWindow(m_hWnd)); return ::SetWindowPlacement(m_hWnd, lpwndpl); } #endif
then I have declared a control deriving from CWnd like this : #ifdef USE_WTL #include "Wnd2Window.h" #ifdef WIN32_PLATFORM_WFSP class CSpinBoxEx : public CWnd2Window, public WTL::CSpinListBox {
}; #endif #endif
But I get the following errors : C:\Program Files\Microsoft Visual Studio 8\VC\\wtl\include\Wnd2Window.h(28) : error C2385: ambiguous access of 'm_hWnd' 1> could be the 'm_hWnd' in base 'CWnd' 1> or could be the 'm_hWnd' in base 'ATL::CWindow' 1> C:\Program Files\Microsoft Visual Studio 8\VC\\wtl\include\Wnd2Window.h(26) : while compiling class template member function 'LRESULT CWnd2Window::DefWindowProcW(UINT,WPARAM,LPARAM)' 1> with 1> [ 1> T=CSpinBoxEx, 1> TBase=CWnd 1> ] 1> ..\Inc\DlgSchedule.h(17) : see reference to class template instantiation 'CWnd2Window' being compiled 1> with 1> [ 1> T=CSpinBoxEx, 1> TBase=CWnd 1> ] 1>C:\Program Files\Microsoft Visual Studio 8\VC\\wtl\include\Wnd2Window.h(31) : error C2039: 'ProcessWindowMessage' : is not a member of 'CSpinBoxEx' 1> ..\Inc\DlgSchedule.h(17) : see declaration of 'CSpinBoxEx'
When I try to comment DefWindowProcW I got same kind of errors about ShowWindow: error C2385: ambiguous access of 'ShowWindow' 1> could be the 'ShowWindow' in base 'CWnd2Window' 1> or could be the 'ShowWindow' in base 'ATL::CWindow'
Please help.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hi Vincent,Vincent_RICHOMME wrote: I would like to use a CSpinListBox in my MFC project on smartphone platform.
WTL::CSpinListBox cannot be created, even in WTL projects. Assuming you have included the necessary headers as explained in the article, just insert a 'normal' ListBox control in your dialog. In your MFC InitDialog handler write:WTL::CSpinListBox MySpinLB = GetDlgItem(ID_MYSPINLB); MySpinLB.AddString(L"Test");
cheers, AR
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
First, your example only uses ATL include files (atlbase.h & atlwin.h) except for atlapp.h. That one is needed for WTL::CAppModule, but you don't use that further in the code (at least not that you show in the article, I didn't investigate the code). So I'm not quite sure what it's for and it looks to me that it could just as easily have been left out - which would make your article more about 'Mix up ATL with MFC' than 'Mix up WTL with MFC'.
Secondly, the (IMO) most useful part of WTL lives in atlctrls.h & atlcrtlw.h. But you cannot just insert them after the other atlxxx.h includes that you showed, because LPSTREAM will not be defined, and that's because objidl.h has not been included before commctrl.h has been included (through the mfc headers). Which is basically a chicken-and-egg problem that cannot be solved from anywhere else than from within mfc (as far as I could tell - there may be some preprocessor trickery to fool the compiler but I don't believe there's a clean way to do it). This is also where the people below's problem about not being able to find ImageList_Read() etc. comes from.
Does this mean people should give up all hopen? If they're not afraid of modifying their local WTL install a bit, no. Just comment out the functions in which the offending functions appear (WTL::CImageList::Read(LPSTREAM lpStream), WTL::CImageList::Write(LPSTREAM lpStream) and WTL::CImageList::CreateFromStream(LPSTREAM lpStream) ), make sure you don't use them in your code, and you're good to go. Not a great solution as modifying system libraries has all of the upgrade headaches associated with it etc., but at least you can get on with your work.
For further reading, check the WTL mailing list archives, like here: http://tech.groups.yahoo.com/group/wtl/message/11955.
|
| Sign In·View Thread·PermaLink | 4.00/5 |
|
|
|
 |
|
 |
roel_ wrote: Just comment out the functions in which the offending functions appear
Why not simply guard the WTL functions with conditional compilation, like the original functions they are wrapping, then? This could be included in the official WTL without negative side-effects I guess...
#ifdef __IStream_INTERFACE_DEFINED__
... offending WTL::CImageList members ...
#endif
Paolo
------ Why spend 2 minutes doing it by hand when you can spend all night plus most of the following day writing a system to do it for you? - (Chris Maunder)
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Yeah that's right, that would prevent people from having to patch their Wtl installation over and over. I'll prepare a patch for the WTL guys.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Posted on https://sourceforge.net/tracker/index.php?func=detail&aid=1674732&group_id=109071&atid=652374. Let's hope someone quickly takes a look, my last patch took 2.5 years to get included :/
|
| Sign In·View Thread·PermaLink | 5.00/5 |
|
|
|
 |
|
 |
Ok, just to keep everybody updated: Nenad has checked in the necessary changes today (12th March 2007). So make sure to get a CVS version or release from after that date if you want to use WTL controls in MFC code.
|
| Sign In·View Thread·PermaLink | 1.00/5 |
|
|
|
 |
|
 |
I worked out a correct solution to this problem.
First, add #undef __IStream_INTERFACE_DEFINED__ before #include "atlctrls.h" in stdafx.h
then add WTL:: before every WTL class name you used.
|
| Sign In·View Thread·PermaLink | 5.00/5 |
|
|
|
 |
|
 |
hi! nice acticle. I use the demo but has error when rebuild all and wtl7.5 also has same error ,
--------------------Configuration: MixedWindow - Win32 Debug-------------------- Compiling... StdAfx.cpp d:\my\cp\wtl\wtl80_6137\include\atlgdi.h(3385) : error C2664: 'CreateCompatibleDC' : cannot convert parameter 1 from 'struct HDC__ *' to 'class CDC *' Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast d:\my\cp\wtl\wtl80_6137\include\atlgdi.h(3389) : error C2065: 'SelectBitmap' : undeclared identifier d:\my\cp\wtl\wtl80_6137\include\atlgdi.h(3389) : error C2440: '=' : cannot convert from 'int' to 'struct HBITMAP__ *' Conversion from integral type to pointer type requires reinterpret_cast, C-style cast or function-style cast Error executing cl.exe.
MixedWindow.exe - 3 error(s), 0 warning(s)
|
| Sign In·View Thread·PermaLink | 1.13/5 |
|
|
|
 |
|
 |
In this article you show how to add a wtl control to a MFC dialog,is it possible to change for example:the dlg or mainframe and view base class and use the WTL features such as docking windows?
Regards, Maryam.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
It might be possible, you just have to try. You can use the technique explained in the article and the demo project: transform the appripriate CWnd-based classes to have a CWindow interface (with the CWnd2Window class), and then try to add WTL docking support.
I don't know how WTL docking works, so I don't know if there are some other requirements that can't be satisfied with MFC frame/view windows. Especially with MDI, you might need to "transform" also the MDI-client window.
I guess I can't be of much help there...
Paolo
------ Why spend 2 minutes doing it by hand when you can spend all night plus most of the following day writing a system to do it for you? - (Chris Maunder)
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
This article is about adding WTL into an MFC project. I need the opposite. Exactly I need to know how can I add MFC support into WTL project? It looks downgrading, but, cause I have the rest project written in MFC, and it takes time converting them in to WTL,now I am looking for a way to solve this problem,do you have any idea?
Regards, Maryam.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
This article introduces a way to add WTL implementations to existing MFC windows.
If you want to create WTL-only windows in MFC projects just add the required header files from ATL/WTL and you're done. Then you don't need this article at all!
Paolo
------ Why spend 2 minutes doing it by hand when you can spend all night plus most of the following day writing a system to do it for you? - (Chris Maunder)
|
| Sign In·View Thread·PermaLink | 2.00/5 |
|
|
|
 |
|
 |
Hi,
i used the guide for Mixing up WTL with MFC, to integrate the WTL Oscilloscope classes (from http://www.codeproject.com/wtl/WTL_Oscilloscope.asp) into a MFC Project.
But i get those two error msg´s:
[...] EEG_VIS_2Dlg.cpp(52): error C2385: ambiguous access of 'delete' in 'OscilloscopeCtrl'
and
[...]Wnd2Window.h(28): error C2385: ambiguous access of 'm_hWnd' in 'OscilloscopeChannelImpl' with [ T=OscilloscopeChannel, TBase=CStatic, TWinTraits=ATL::CControlWinTraits ]
any idea?
i did exactly what the guide suggests and changed the class definitions of: OscilloscopeChannelImpl, OscilloscopeAxisImpl, OscilloscopeCtrlImpl to e.g.:
class ATL_NO_VTABLE OscilloscopeChannelImpl : public CWnd2Window < OscilloscopeChannelImpl, CStatic >, public ATL::CWindowImpl< T, TBase, TWinTraits >
but i get the error msg´s as described above. maybe someone got a hint for me.
regards simon
|
| Sign In·View Thread·PermaLink | 1.00/5 |
|
|
|
 |
 | Help  peipeishu | 9:04 24 Feb '05 |
|
 |
I added #include in the stdafx.h file in your "Mixwindows" project, since I may want to use some controls in WTL and it fail to compile. The error message is "error C3861: 'ImageList_Read': identifier not found, even with argument-dependent lookup". How do I fix this?
|
| Sign In·View Thread·PermaLink | 1.00/5 |
|
|
|
 |
|
 |
peipeishu wrote: I added #include <atlctrls.h>in the stdafx.h file in your "Mixwindows" project, since I may want to use some controls in WTL and it fail to compile.
I just tried and the demo project just compiles fine. I have WTL 7.5, check your version at the top of WTL header files.
My stdafx.h is like:
#define VC_EXTRALEAN #include <afxwin.h> // MFC core and standard components
#include <afxext.h> // MFC extensions
#define _WTL_NO_AUTOMATIC_NAMESPACE #include <atlbase.h>
#include <atlapp.h>
extern WTL::CAppModule _Module;
#include <atlwin.h>
#include <atlctrls.h>
Paolo
------ Why spend 2 minutes doing it by hand when you can spend all night plus most of the following day writing a system to do it for you? - (Chris Maunder)
|
| Sign In·View Thread·PermaLink | 2.00/5 |
|
|
|
 |
|
 |
I'm currently using WTL7.1, I also tried WTL7.5, still has the same problem. The onlt thing I did is add the #include in stdafx.h in the demo projects. But since I'm using .NET 2003 as a development environment, so I have to convert the project so I can build it using .NET 2003. I 'm wondering if that could be the problem. But I don't have any problem if I build a WTL projects using the WTL wizard, which mean it does not mix with MFC.
|
| Sign In·View Thread·PermaLink | 2.00/5 |
|
|
|
 |
|
 |
Mr. Messina, I Read your article and Sourc code,it's very interesting , but if I wand to Translate CWindow to CWnd , and How Can I do ? Just need Translate the message Functions parameters to CWnd from CWindow, Right or Wrong? Of course , I should pay attention to m_hWnd that must Asserted it is validated.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Mmm... I'm not sure if I got what you mean.
However, if all you want to do is an MFC subclass of an existing window created with ATL/WTL all you need to do is call CWnd::SubclassWindow on it, and implement message handlers.
Using MFC classes in WTL is not possible without including the whole MFC, which is probably not what you want anyway since you are using WTL.
The trick I present in this article works because ATL/WTL uses templated classes, but MFC does not and an "inverse" of this trick would not be possible.
Does this answer your question?
Paolo
------ Why spend 2 minutes doing it by hand when you can spend all night plus most of the following day writing a system to do it for you? - (Chris Maunder)
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Thanks a lot for your replying! I am using a ATL/WTL Framework, and for the coming deadline of my project,I wand to use my old MFC codes that tested right in many project. fortunately ,I am suceess, next time ,when i am not busy , I will open my idea and demo codes for people who meet the same question of mine . Of Course, I didn't tranlate CWindow pointer to CWnd . Thanks again!!
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I have read some of the other messages regarding splitters, but they do not seem to apply to the error I am receiving. Using the following line:
class CSplitterATL : public CWnd2Window, public WTL::CHorSplitterWindow
I get the following compile error in CWnd2Window:
n:\TestApp\Wnd2Window.h(28) : error C2385: ambiguous access of 'm_hWnd' in 'CSplitterATL' could be the 'm_hWnd' in base 'CWnd::m_hWnd' or the 'm_hWnd' in base 'ATL::CWindow::m_hWnd' n:\blocwilt_view3\CASE\WINLIB\LogPanes\Wnd2Window.h(26) : while compiling class-template member function 'LRESULT CWnd2Window::DefWindowProcA(UINT,WPARAM,LPARAM)' with [ T=CSplitterATL, TBase=CWnd ] n:\blocwilt_view3\CASE\WINLIB\LogPanes\SplitterATL.h(9) : see reference to class template instantiation 'CWnd2Window' being compiled with [ T=CSplitterATL, TBase=CWnd ]
Has anyone been able to make splitters work ?
"Learn from the mistakes of others. You can't live long enough to make them all yourself." Unknown
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
In my first message, the message box garbled the source code syntax between the angle brackets, I am trying again:
class CSplitterATL : public CWnd2Window<CSplitterATL, CWnd>, public WTL::CHorSplitterWindow
Error message generated by above:
n:\blocwilt_view3\CASE\WINLIB\LogPanes\Wnd2Window.h(28) : error C2385: ambiguous access of 'm_hWnd' in 'CSplitterATL' could be the 'm_hWnd' in base 'CWnd::m_hWnd' or the 'm_hWnd' in base 'ATL::CWindow::m_hWnd' n:\blocwilt_view3\CASE\WINLIB\LogPanes\Wnd2Window.h(26) : while compiling class-template member function 'LRESULT CWnd2Window::DefWindowProcA(UINT,WPARAM,LPARAM)' with [ T=CSplitterATL, TBase=CWnd ] n:\blocwilt_view3\CASE\WINLIB\LogPanes\SplitterATL.h(9) : see reference to class template instantiation 'CWnd2Window' being compiled with [ T=CSplitterATL, TBase=CWnd ]
"Learn from the mistakes of others. You can't live long enough to make them all yourself." Unknown
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hi William,
The problem, I guess, is in the WTL class you are trying to use. In order for the trick to work, you should not use classes that already inherit from CWindow, as this class is replaced by CWnd2Window.
So, your class definition should start as:
class CSplitterATL : public CWnd2Window<CSplitterATL, CWnd>>, public WTL::CSplitterImpl<CSplitterATL, false>
Paolo
------ Why spend 2 minutes doing it by hand when you can spend all night plus most of the following day writing a system to do it for you? - (Chris Maunder)
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Thanks for responding Paolo,
The change you suggest does allow the code to compile, but does not actually draw anything.
In order to get my immediate job done, I decided to fudge a little bit, and created an MFC based splitter class based on the ATL splitter code in the atlsplit.h file. This turned out to be easy with very few modifications to adjust for the minor differences between some CDC elements.
In general though, I would really like to know how to get around these issues because I seem to be working in a nether world of ActiveX controls using MFC, where:
1. MFC's CSplitterWnd class does not work because it assumes it is running in an application instead of a control. 2. ATL's CSplitterWindow is incompatible with the MFC.
Catch-22...so I punted (American football expression ).
"Learn from the mistakes of others. You can't live long enough to make them all yourself." Unknown
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|