Introduction

I wrote this article a few for joke and few as a challenge to myself: I'd been a Win32 C programmer, but, with C++, I always relied on MFC, WTL, .NET, WxWin or some "framework" or wrapper.

One of the advantages is that they "simplify" certain operations by hiding certain details. In contrast, when you have to do something different than usual, the entanglement that exist between the framework classes can make the work quite hard.

Do you ever have the feeling - when overriding certain MFC classes or function - to hack-in somebody else's defined behavior? How often did you find with strange "side effects" due to the fact that Document, Frames and Views often destroy each other in a not always obvious order?

What follows is a way to introduce a different approach. Of course, I don't have the man power of the entire MS AFX team, so don't expect a full replacement of WTL or ATL. I just want to introduce a different possible point of view.

And because I'd like to make the proposed classes reusable, I tried a design able to reduce the classes entanglement to the minimum. In particular, if class A needs class B, I wanted to avoid that class B also needed class A. Thus, you can use this classes also outside my framework, may be even in a MFC application as well.

However, I only relied to STL containers and algorithms and to some CP authors' ideas: in particular Paul Bartlett and Alex Farber. (I didn't reuse their code, but - in fact, I reinterpreted their ideas.)

Conventions

I deployed all the classes in agreement with this article. I also used namespaces, an put all the namespaces in a root namespace named "GE_". If this names gives trouble to some other libraries, just replace all the occurrence in files with something else.

Shared Ownership Wrappers

This is the key for all this implementation: Win32 API is full of handles of various kinds, you must "obtain", "use" and "release".

In my intention, a wrapper should not rewrite C API: I don't feel any particular need to replace ShowWindow(hWnd, SW_SHOW) with pWnd->ShowWindow(SW_SHOW): the "actors" are exactly the same, and rewriting thousands of prototypes just to put the first parameter outside the parenthesis ... it's not a good investment, for me.

More interesting is to wrap handles to give them protection from improper destruction politics, or forgetting to destroy them, or offering more effective constructors for functions having often many parameters that takes often same values.

In this implementation, a Wrapper is something similar to a "reference counting smart pointer" (in fact, it is a generalization): it may be an "owner" of the handle it wraps (thus, destroying it when no more "owners" are holding it) or an "observer" that behaves as "invalid" when all "owners" have left.

But, because we may have a number of different handles with different management politics, I decided to split the implementation in a number of template classes inheriting from each other.

The Architecture

The heart of all the mechanism is a cooperation between a WrpBase and a XRefCount.

WrpBase provides the "protocol" for a wrapper: refers to a "reference counter" and can be "Attached" and "Detached" to the object it wraps. It also can be set as "owner" or "observer", be "Valid" or not, and return STL iterators to walk onto a wrapper collection.

XRefCount, in turn, maintains information about how many (and eventually "who") are the wrappers wrapping a given value or handle, and provides some helper functions to the WrpBase to retrieve and update those information.

WrpBase is designed to be derived (one of its template parameters, W, is the class you derive from it) and calls all it functions after static_cast-ing its "this" pointer to W*. This allows you to override its functions.

Also, the behavior of WrpBase depends on a number of "helpers" that are provided by a traits class WrpBase inherits from. The purpose of this "traits class" is also to provide a convenient number of typedef-s that defines the way the wrapped object is stored in the wrapper, is received as a parameter from member functions, is given as a return value, or is de-referenced.

Traits classes are implemented in various versions and inherits the type definition from "types classes".

To close everything into something that can be assigned, copied etc., a Wrp class is then designed to inherit from a WrpBase or derived class, applying assignment operator, copy constructors, equality (==) and ordering (<) operators, access (->) and dereference (*) and so on, using the WrpBase defined protocol (after your customization, if any).

The following picture gives a sketch on all this stuff.

Inheritance goes from top to bottom. XRefCount knows (because of a template parameter) about "YourClass", Wrp knows because of inheritance, and calls WrpBase prototyped functions in the way you can eventually override them. WrpBase, in turn, uses what inherited from "traits" and from "types".

The implementation

Types

"types" is implemented in two different ways:

types_wrp_hnd<H>: suppose H is a handle or a "small object" or a pointer or ... something that can be casted to LPVOID without information loss and that is stored "as is".

types_wrp_ptr<H>: suppose H is an object on the heap, we have to refer to it with a smart pointer. Thus an H* is stored by the wrapper and the wrapper will have a "pointer semantics".

Both the classes define the following types:

type

description

types_wrp_hnd

types_wrp_ptr

SH

What the wrapper stores to represent H

H

H*

IH

What the wrapper will receive as input parameter in functions

const H&

H*

OH

What the wrapper will return from its functions

const H&

H*

RH

What the wrapper will return as "dereference"

const H&

H&

In specific implementations, may be SH, IH, OH and RH can be different (for example: you can return a proxy object to store a reference ...).

In any case, it is required that implicit conversion can work when converting IH into SH, SH into OH, and OH into IH.

Traits

"traits" is implemented in three different ways:

traits_wrp_hnd<H, Types>: suppose H is a Handle. Most of its functions need an override once H is known and its management policy is defined.

traits_wrp_ptr<H,Types>: suppose H is a "pointed object" (the wrapper operates as H*) and implements Create as new and Destroy as delete. Conversions between pointers are operated with static_cast.

traits_wrp_dynptr<H,Types>: like before, but using dynamic_cast.

Normally, the "Types" parameter defaults to types_wrp_hnd<H> in the first case and to types_wrp_ptr<H> in the other two cases. But - for some particular cases - other solutions can be afforded.

The purpose of the "traits" classes is to provide some low-level helper functions to standardize to the upper "layer" classes an interface to operate on the various "types".

It provides storage for a SH member, a pointer to a t_refCount class (supposed to have the same prototyping as XRefCount_base), and a bool defining if this wrapper is an "owner" or an "observer". It also implements the following:

void Attach(TT::IH h) attaches the wrapper to a passed "dumb value". Existing reference counters for the value are searched and, if not found, created.

void AttachWrp(const WrpBase& w) attaches the wrapper to the value carried by another wrapper.

template<class A> void AttachOther(const A& a) attaches the wrapper to the value that can be detected by conversions from the A type.

void Detach() detaches the wrapper. Calls Destroy (inherited from TTraits) if we are the last owner wrapper associated to the wrapped value.

bool IsValid() const returns if the wrapper must be considered valid and so the wrapped object.

bool IsOwner() constreturns if the wrapper is an "owner" wrapper or not.

void SetOwnership(bool bOwn) makes the wrapper an "owner" (true) or an "observer" (false).

typedef TRefCount::iterator iterator; staticvoid get_range(iterator& first, iterator& last, TT::IH h) gets the iterator range from an eventual wrapper collection associated to the wrapped value. If first==last, the collection is empty or not existent.

They are implemented in terms of other functions (always through the pThis() function, that converts this into W*) and in terms of TRefCount functions. It also provides to the TRefCOunt a set of "do nothing" call back functions:

void OnFirstAttach(XRefCount_base* pRC) called when the reference count is counting the very first reference for the wrapped object.

void OnAddRef(XRefCount_base* pRC) called every time a new reference is added.

void OnRelease(XRefCount_base* pRC) called every time a reference is released.

void OnLastRelease(XRefCount_base* pRC) called when the last release happens.

XRefCount<W>: W is supposed to be WrpBase derived. Implements owners and observers reference counting. (reference() is the sum of the two). It auto-destroys on Release() when no more reference remains, returning true (else, false). Doesn't provide iterators (return "first" and "last" to random equal values).

XRefChain<W>: keeps a list of W* pushing in front on AddRef and removing on Release. Auto-deletes when the list becomes empty.

The first can be used with types that requires only a reference counting, the last with types you need to keep track of "who" are the wrappers.

Reverse Mapping

Wrappers know what they wrap, and know who is the associated reference counter. But if we're assigning a value to a wrapper, we must have a way to find out (in the case that "value" is already wrapped by someone else) if there is (and who is) the associated XRefCount.

One of the function that comes with the traits classes is Key(IH). It converts the wrapped type into an LPVOID.

This can be done with C-syle cast (as with traits_wrp_hnd), static_cast (as with non polymorphic pointers, in traits_wrp_ptr) and dynamic_cast (for polymorphic pointers, as in traits_wrp_dynptr).

Particularly interesting is the last one: given a complex object (with many bases) different pointers to different components can be different (in terms of absolute value) but a dynamic_cast to LPVOID always return the same base address (the one of the entire object). This allows to share the ownership of a complex object between different smart pointers referring to the different components: they all can find out the same reference counting object.

Operators ( Wrp<B> )

Conversion constructors, copy constructors, assignment operators etc. cannot be inherited as ordinary functions. Their override requires particular attention, so I preferred to concentrate all this stuff into a specific template class. Wrp<B> is the "upper layer class". B is the class Wrp is made to derive from. It can be every WrpBase derived class. It implements assignment, copy, conversion and destruction in terms of Detach/Attach.

Some specific cases: smart pointers

To specialize a wrapper, a method could be the following:

declare a struct derived from WrpBase specifying the required parameters, then

typedef a Wrp<yourstruct>, to give assign and copy functionality.

A very simple case when this happens is the definition of smart pointers:

Smart Handle

Note that, while Ptrxxxstructs close the wrapper inheritance (they give themselves to WrpBase as W parameter) HndBase doesn't: there is still a W parameter.

This is because, while for pointer it is clear that new and delete are the creation and destruction policy (Create and Destroy comes from the traits classes this way), for handles, it's all still to be defined. Different handles have different creation and destruction API. HDC, then, have many of them! So we must complete later.

Of course, for certain particular objects, you may also want to do something else than "new" or "delete": in this case, you must do yourself the definition of a struct that derives from WrpBase (and closes it inheritance) and then wrap it into a Wrp.

Chained actions

Using XRefChain as WrpBase parameter, we can chain "wrappers to the same entity" together and walk on them.

An interesting thing that can be done is to associate actions to this "walking". The idea is to provide - for example- a way to dispatch Windows messages to HWND wrappers. But not only.

EChainedAction<W, I, A> is a class designed do be derived (W is its derived class) that associates an action having A as parameter to the walk on the iterator I, that must be an STL compliant forward iterator, that dereferences into W* (whose I::operator*() returns a W*). Such an iterator can be obtained, for example, by WrpBase::get_range(...).

The function Act(const A& a, iterator first, iterator last), iterates from first to last calling the member function OnAction(const A& a) that is supposedly overridden in W.

The way the iteration is done, however, is not a pure flat loop: it is instead a recursion over stored parameters: Act fills in a data structure on stack and saves its address into a per-thread saved static variable, keeping the previous address (a per-thread variable is a variable that's accessed through a map whose key is the current thread ID) than call Default(). Default retrieves the data structure and iterates calling OnAction. If OnAction returns true, Default returns immediately true, otherwise iterates to the next element until "last" is reached. At that point returns false.

Thus, in your OnAction override, you can:

Simply return false. The iteration will continue to the next chained element.

Simply return true. The iteration is halted.

Call OnAction where appropriate. The iteration to the next elements is anticipated and the control returned to you.

More or less, it's like subclassing a window procedure with another, and if needed, call the previous. An arbitrary number of times.

Events

A combination between smart pointers and chained actions are events.

Here, they are implemented as "functors" to be declared as member variables of a class (the event source) that are attached by internal objects (the "sinks") that dispatch an action to registered "receivers".

Event<A> is a struct that's few more than an operator()(const A&) member, that supposes that the event has to be wrapped by chainable wrappers. The operator gets the chain, and calls Act.

EventRcv<D> is a base for a generic derived class (D), providing RegisterEvent<A>(Event<A>&, bool(D::*pmemfun)(consty A&) ) and UnregisterEvent<A>(Event<A>&). The first function creates an XTypedSync<A,R> that associates the event to the given member function. The second clears the association.

XTypedSync<A,R> derives from XSync<A>, that is a chainable wrapper for Events having parameter A. XSync implements OnAction delegating to a virtual DoAction function, that is implemented in the derived XTypedSync, calling the given member function (via a pointer to a member saved during association). The use of virtual function is required because different XTypedSync (referring to different receivers, so having different R parameters) must chain together all as XSync<A>.

Thus, calling an event, will call the registered member function for all the associated receivers.

Registering a WNDCLASS

If you think that filling in the field of certain data structure like WNDCLASSEX is a tremendously annoying task, this wrapper is for you:

SWndClassExReg registers a window class, un-registering it when no more references are available.

It derives from WrpBase, customizing Destroy.

Constructors attempt to fill a WNDCLASSEX with the supplied parameters:

A version loads from resources with the passed ID (icon and cursor), the other uses the passed raw values.

Both the constructors register the window class and save the returned ATOM.

The wrapper Destroy function (it is called when the last owner wrapper is detached) calls UnregisterWndClass, passing the saved ATOM.

The message loop (SMsgLoop)

The message loop is implemented by SMsgLoop. You can create as many instances you need. Typically one will be in WinMain, while others may be where "modal loops" are required (for example: when asking mouse coordinates to detect where to place an object).

The "loop" itself is implemented by a LoopBody function (that is not itself a loop) implementing message retrieving and dispatching, with also a bit of message filtering / translation and idle processing. Such a function is called by LoopWhile that loops until a passed bool reference becomes false or until WM_QUIT is received. LoopWhile is called by Loop, that provides an "always true" value to LoopWhile.

This "breaking the loop" in three components allow to use SMessageLoop also for modal loop to be run inside an application (using LoopWhile) or to dispatch message during a loop calculation (calling LoopBody from inside the hosting loop).

SMessageLoop provides also a member to store an HWND (that can be the application main window) whose destruction will cause WM_QUIT to be posted.

If SMsgLoop is created with the bAutoQuit parameter set to true, the first window that is created through that message loop becomes the message loop main window.

Idle processing and Message filtering

Idle processing and message filtering are implemented through global events.

This solution avoids the risk to create entanglement between the message loop implementation and - for example - a window implementation. You can use my windows also in other frameworks where my message loop is not used. The only requirement is to properly generate the events.

SIdleEvent and SMsgFilterEvent are both EGlobals and Events.

The SIdleEvent parameter is the SMsgLoop that calls the event. If you are originating yourself the event, you can pass NULL.

SMsgFilterEvent parameter is the Win32 MSG structure. In your event hander, return true to indicate that the passed message was filtered.

Wrapping HWND: subclassing windows

HWND wrappers must have the capability to receive window messages. This implies them to be both chaining WrpBase and EChainedAction, but that's not enough.

There's also the need to get the messages from the system. This requires a mechanism of subclassing/reclassing. And also, there is the need to dispatch messages to possibly different member functions. There are different approaches to do this:

MFC-like: member functions are mapped as addresses with the correspondent message IDs. When a subclass window procedure receives a message, it scans the map searching a match and then calls the corresponding function. A set of macros can be provided to assist a static-map filling.

ATL-like: a set of macros provides the fragments of code necessary to identify messages and dispatch them to the indicated functions. The macros also override a well-known virtual function that is called by a subclassing window procedure.

.NET-like: a subclassing window procedure cracks the messages, dispatching them to a set of well-known members, each of which raise a different event.

None of those approaches are perfect: the first two strongly deal on macros, and the third requires a heavy data structure and is not "symmetrical": there is an object that "owns" the HWND and others attaching to it.

This "imperfections" have been compensated by a number of wizards that help in compiling message maps or chaining event handlers. Considering the big number of messages we have to deal with, they are probably the best compromise between programming techniques and tool usability. In my experience, I found the ATL/WTL approach more flexible, so I did the following:

Wrappers for the same HWND chain together. The first attachment and the last release are used to subclass / reclass the wrapped HWND.

OnFirstAttach is overridden to do window subclassing.

A subclassing window procedure "Act"s on the chained action relative to HWND.

OnAction is overridden to call a well-known virtual function with the same prototyping of the ATL PocessWindowMessage.

Default is overridden to call the previous window procedure if the action returns unhandled.

To store the previous window procedure, we need a place that is shared among all the wrappers of the same window. Thus the XRefChain must be derived to host this value.

Create a window based on DefWndProc (passed as default parameter to SWndClassExReg) that does ... really absolutely nothing ... in 76KB executable.

Not the shortest exe I've ever seen in my old life, but consider all traits functions, std::maps, lists etc. They improve flexibility but have a cost!

Hello World: GDI wrappers

AS a first step, we need to derive EWnd into SWnd and implement a message map dispatching WM_PANT.

Then we need message map macros,...

Then we need "featured" GDI object wrappers ... or include the GDI+ library.

Message map macros

Inspired by ATL macros, they've the same names to allow the IDE wizard to do their dirty work in the same way, but .. they're not the same of ATL (they'd been designed to work into an EWnd wrapper, not CWnd or CWindow!). They are included in MessageMap.h. ATL basic macros work also well, but not WTL macros.

Message cracker macros, again have same names and parameters of WTL ones, but different implementation.

Note: I obtained them with a heavy use of "find and replace" from the original sources. They are hundredths, so I cannot test all them singularly, so, because the process had been the same for all, and because the ones I tested worked, ... I suppose all of them work.

But it is very easy to test as they are used. Simply ... don't forget!

Isn't it nice? Not yet: what are those BeginPaint - EndPaint pairs? Why don't we wrap HDC into a wrapper that manages properly the Begin-End or Get-Release pairs? And what about saving and restoring the state before quit ?

Wrapping HDC

The idea to save the state of a DC when wrapping, it seems good, but ... where should be the state stored ?

There are two possible answers:

In the wrapper itself: every wrapper of the same DC keeps its own state, saving it on Attach and restoring it on Detach. This is useful in "short life wrapper": you attach one at the beginning of a function and you are sure that its destruction (at the end) will restore the DC and close it. Nested function calls create a series of wrappers that will be destroyed in reverse order. So it's OK.

In the reference counter object: the saved state is shared among all the wrappers of the same HDC, and is restored on the last owner detachment. This can be good in "long life wrappers" that can be passed as values between functions or that keep the wrapped object alive until other objects refer to it. This is the case for the "SuperWndProc" in EWnd, but it is not the case for an HDC: its normal life doesn't survive the process of a command.

Given the above consideration, I provided a set of HDC wrappers that can interoperate (they use the same XRefCount) but with different Attach/Detach policy.

All classes are based on SHdcSave_base<W>, that override OnAddRef and OnRelease to save and restore the DC state. Then:

SHdcSave(HDC): it's nothing more than the closure of SHdcSave_base, with a constructor that takes an HDC as an argument.

SHdcPaint(HWND): Overrides Create calling BeginPaint and Destroy calling EndPaint. The constructor keeps an HWND. The PAINSTRUCT is accessible as read_only.

HPEN, HBRUSH, HFONT

GDI objects can be created in a variety of ways, but always destroyed with the same API: DeleteObject.

Thus, SGdiObj<H> wraps any HGDIOBJECT with an HndBase, overriding the Destroy callback by calling DeleteObject.

It is important that GDI object destruction takes place when objects are not selected into some HDC. This can be obtained by initializing the GDI wrappers before to initialize HDC wrapper. This will let HDC wrapper to go out of scope before restoring its state and leave free the GDI object that can be destroyed by their own wrappers.

Note that font is created before dc, hence dc will be destroyed first. When this happens, "RestoreDC, and then, EndPaint" are called. RestoreDC will deselect the font, EndPaint will close the painting. At that point, font destruction will delete the GDI object.

Further issues

OK: now the big picture is done. I'm going to further refinements and to some more specific and detailed classes. But is is probably a better subject for subsequent articles.

Share

About the Author

Born and living in Milan (Italy), I'm an engineer in electronics actually working in the ICT department of an important oil/gas & energy company as responsible for planning and engineering of ICT infrastructures.
Interested in programming since the '70s, today I still define architectures for the ICT, deploying dedicated specific client application for engineering purposes, working with C++, MFC, STL, and recently also C# and D.

Any way, the major source of incompatibility between C7.0 and C8 is the keyword typename, that became mandatory since C7.1, to be used in front of identifuers of dependent type names inside template classes.
For example

template<class A>
class C
{
A::ret_type function();
}

(ret_type is supposed to be a type defined inside A scope)
should be

template<class A>
class C
{
typename A::ret_type function();
}

and so on.

Another problem is the requirement to repeat the template parameter list inside the inheritance list or inside friend declarations.
For example:

template<class Derived>
class C:
public abase<C>
{ ... };

should be

template<class Derived>
class C:
public abase<C<Derived> >
{ ... };

and similarly

template<class A>
class C
{
friendclass C;
};

should be

template<class A>
class C
{
friendclass C<A>
};

etc. etc.

May be lot of this are around the code, I never migrate towards C8.
I hope this may help anyone in similar situation.

I received an e-mail telling me about some compiler errors.
I wasn’t able to reproduce the problem (to me, everything worked) since the sender comes to the solution:

I worked with the 2001 release, while he worked with 2003.
The 2003 release of the compiler impose the use of the typename keyword before the use of an unknown type inside a template. This was not required previously. I back-tested his modified code with no problem, so I updated.

... why reinventing the wheel??? This kind of approaches can led to untested code, hidden bugs, whereas by using any framework available (MFC, WTL, etc.) you know from the start that it's (hopefuly) a well tested codebase you can use.

There are always good reasons to reinvent wheels:
1. You need a larger or smaller one.
2. You need a different grip.
3. You want to understand why a given wheel doesn’t go on stairs.

Although wheels exist from thousands years, car factories still reinvent them. It happens almost every time a new model is designed.

If you are confortable with MFC, no problem: use it. I also use it, when I have to do something that “fits” into MFC philosophy. But if you have to go ahead that philosophy … you stop programming and start hacking the sources. I think that’s not the right way to go. It ‘s putting truck wheels on cars. They are tested … but results may be disastrous.

How many bugged MFC apps exist … because a not good knowledge of MFC code from who used it ?

Well at least for me what is wrong with WTL (or any other framework than MFC) is that I have to port all my MFC controls and code to WTL before I can use the framework. So even though WTL is much better designed than MFC I would be wasting my time to move from MFC to WTL considering that I have over 300K lines of MFC code to port. And that is only my current code. Look at the articles here what percentage of them is WTL?

Porting existing code to WTL from MFC isn't as hard as you might think. I have switched to WTL and have ported lots of MFC controls - GDI code is virtually identical between the frameworks. Yes, it takes some time but it isn't necessarily THAT daunting - both MFC and WTL are relatively thin wrappers over Win32 anyway.

>Look at the articles here what percentage of them is WTL?

But look how many more WTL articles there are now compared to a year ago - this speaks volumes. Even without "offical" MS support/docs, people are still taking the plunge, which I find heartening. Not everyone wants to move to a .NET world and WTL is THE framework for writing Windows apps IMHO. I find it a joy to use.

So I agree with the original poster - the code here is excellent (it got my 5) but other than an interesting hobbyist-style "how to" project, I can't see any need for it in the real world when a framework like WTL is already available.

Very interesting post: but I would like to go more deep in some assertions.

1) “Not everyone wants to move to a .NET world”: although this is a real matter of fact, it would be interesting to investigate WHY this happens: it seems people are moving away from MFC, not necessarily to go the “.NET”, that Microsoft would like to be the replacement for the Windows API. (But we’re still far from that …). In this scenario, WTL is enlarging its domain. It should be interesting to understand what could happen in a Windows2003 and subsequent, when Win32 or Win64 APIs will be eventually no more available.

2) What I proposed is not a “framework”. My wrappers can work in WTL and MFC apps as well. What I tried to do is to define something that both frameworks (and even .Net also) miss: shared ownership. You can attach any number of EWnd derived classes to an exixsting HWND (even CWindow, CWindowImpl, CWnd and whatever) and define additional reusable behaviours. They combine Shared ownership with Subclassing. And use the same ATL / WTL scheme. I used alone because – that’s the point – By only adding a message loop and a WNDCLASS registration, no other stuff is required.

This is probably my biggest problem. If I moved to WTL it would take me many months to port my code and many more to port other essential tools I use im my applications like Chris Maunder's grid control... Porting things like this is more difficult because I am not the original author of the code and it will require me to learn specific parts of the code to perform the port and then it breaks the code upgrade path. If Chris implemented an update I would have to figure out what he changed and try to make the changes myself... This is the reason why I still use MFC when I know WTL would be much better for my applications.

I am currently doing some of this porting work in my application development by making as much of the non GUI MFC code I have use STL instead of MFC. This is mostly with new code but when I need to update an old class and I see that there is very little MFC dependence I usually will remove the MFC dependence and move the class to one of my win32 dlls instead.

>making as much of the non GUI MFC code I have use STL instead of MFC.

Very wise indeed John - and I have made similar moves with my code, albeit to using more ATL7 code instead of MFC (e.g. ATL list/array classes - especially CAutoPtr lists, the new shared ATL/MFC CString, etc.). I am lucky to be using VS.2003 and ATL7 is a real goldmine.

My biggest headache has probably been the woefully short-sighted MFC serialization code that is endemic to my apps - porting a MFC specific binary file format to WTL is a royal pain in the arse. Now I use XML format files everywhere - much, much better and far easier to port to other platforms/frameworks in the future.

If I had the time I would take the top 10 CodeProject MFC control articles and port them all to WTL to show people how easy it is. One day I will make time for this as I know my WTL-evangelism has one over some people in the past and some more MFC->WTL articles will only guarantee more converts!

Isn't WTL the worst of both worlds? It is proprietary, but it isn't supported. So, MS owns it, and is free to stop developing it and making it available anytime they want.

These are not just theoretical, philosophical issues: it really is a drag sometimes that I can't copy/change parts of MFC.

There are some good alternatives that don't have these problems, such as wxWindows. I've also heard that the VCF is really good, especially for people that want to use modern C++ features (wxWindows is more in the style of MFC).

In other words, please don't assume that the WTL and dotNet are the only successors to MFC. I would like to see some of these other alternatives get a higher profile here at CodeProject, but I'm too lazy to do anything about it myself.

TomM wrote:In other words, please don't assume that the WTL and dotNet are the only successors to MFC.

Ohh ... nice to here this ! but there is a problem:
To me, WTL,MFC and what I presented here are diffeent approch to do a same thing: wrap Win32 functions.

.Net ..... its all another completly different story !
Win32 is not "wrapped": is embedded and definitely hidden.

I cannot think to ".Net" ( ".niet" ?!?) as a true "successor" to MFC.
And a don't like WTL insisting in using private collections, far away from STL. why ?!? And why always rely to ATL and loading the COM library ... even in the cases that the only problem is process windows messsages ?

However I don't agree with your definition of "proprietary": it's a tautology: everithing is proprietary, staying with your words.

Coming SOON

Back soon with some enancements with part 2 (no licence: just pure text code without any copiright !). Until Win32 will be available.

"I believe in an America where the separation of church and state is absolute--where no Catholic prelate would tell the President (should he be Catholic) how to act, and no Protestant minister would tell his parishoners for whom to vote ... and where no man is denied public office merely because his religion differs from the President who might appoint him or the people who might elect him.
- John F. Kennedy

If you like it, you migbht consider working with the project! I'm always looking for people to contribute.

Some examples of what would be nice to get help on:
- more controls/components - it'd ne nice to take some of the cooler controls here on CP and port them to VCF.
- help port to GTK/linux - if you have GTK experience I definitely need help with this.
- help writing examples - maybe you have some small utility program that you wrote in MFC or raw Win32 - you could "port" it to the VCF and write up a bit of documentation on it and contribute it as an example.

Feel free to email me at ddiego at users dot sourceforge dot net for more info on this.

Cheers !

¡El diablo está en mis pantalones! ¡Mire, mire!

Real Mentats use only 100% pure, unfooled around with Sapho Juice(tm)!