Introduction
This article is the consequence of
this other. Here, I'll go adding other features to the NLib.
The Approach
I'll follow a "modular" approach: avoiding to "frameworkize" the library, and
using a "component model" (not necessarily COM or .NET: I'll stay with native
language features) allowing us to use that component as needed, without any
need to include them or use them when what we do doesn't require them.
As in the previous article, I'll do nothing new that has not yet been done
before: you can find as many articles you like to do the same with MFC, or WTL.
I just want to test a different coding approach. Am I reinventing wheels ?!
YES: I AM !
But if MFC is best for rain, and WTL for sand ... may be these ones feel better
on snow!
The starting point
It is the W2 project of the previous
article: a "Hello world" unfeatured window, but already with
message maps and command delivery. I created a W3 Win32 empty project, put a
copy of W2 sources, and did some settings (adding "NLib" to the solution, set
RTTI on, set "use precompiled headers" and "create precomp. header" for stdafx.cpp.
Also set the dependency of W3 from NLib).
Some NLIB Modification
During the deployment of this second article, I found it convenient to modify
certain parts of the NLIB, for both bug fixing and also improvement.
Message crackers
The message cracker map macros have been modified to carry functions having as
last parameter a bool& bHandled. This value is set to true by
the macro before calling the passed function, but this arrangement allows you
to set it back to false. By setting to false, the chained action is not broken
by the handler call, and can continue with other EWnd eventually
attached to the same HWND. To avoid confusion, I renamed those
macros ONMSG_xxx rather than GETMSG_xxx.
This is particularly comfortable when you want to "spy" a message, without
interfering with its dispatching.
Wrappers redesign
While extending the wrappers to create more features, I discovered some "flaws"
that - if not better redesigned - put some limitations in usage. And since the
cost of this "redesign" is very few, I found convenient to adequate the
existing code:
In particular:
-
New types have been introduced:
CRH and COH: normally
they are as CH and RH, but are intended to be
returned by "const" functions. The old type are instead
intended to be returned by non-const functions.
-
New functions had been introduced in the
traits_wrp_xxx classes:
CAccess and CDereference: they return COH and
CRH from a IH<CODE>.
-
In
WrpBase, operator TT::OH() is no more const,
and calls Access. Instead an operator TT::COH() const
had been introduced, calling CAccess.
-
In
Wrp, operator->() and operator*() had
been rewritten in both the const and non-const versions, calling the
corresponding traits functions.
Although the supplied implementation of the traits does the same things, this
different interface gives us the capability, where is the case, to
differentiate between the use of a wrapper to access a data for reading, and
the use to modify the hold data.
Strings
Many Windows APIs are defined in terms of TCHAR. Although it is
relatively easy to have a string class holding TCHARs by defining
a std::basic_string<TCHAR>, the implementation of std::strings
provided by PJ Plauger (essentially a specialization of vectors) is not very
efficient: strings are considered "values" and are always copied.
In contrast, MFC and ATL CString makes use of shared vectors that
are "cloned" only when a string value needs to be modified. To implement a
similar scheme, I tested a "WrpBase and traits"
override that allows to maintain shared data between wrappers, and do the clone
when non-const access is attempted.
Although it worked, there is an overhead due to the internal calls between Wrp,
WrpBase and traits. So I preferred a dedicated
implementation, more efficient for this specific purpose (also if less
flexible).
A new wrapper type (NWrp::SShare<T, nullval>) has been
introduced to auto generate, auto clone and share buffers of Ts.
NWin::SString is then derived by NWrp::SShare<TCHAR,
_T('\0')>, adding Left, Mid, Right,
constructors and operators.
Note that SString is not itself a full OOP class (none of the
classes defined here is): it is only a "memory manager". To manipulate strings,
I still refer to the Windows API or to the string function taken from tchar.h.
SString converts implicitly to and from LPCTSTR. To
modify the buffer, use - instead GetBuffer(int wantedsize),
passing the required size, or -1 to let the actual size unchanged. GetBuffer
performs a buffer "clone" if the actual buffer is shared, then resizes it
accordingly and returns its address.
Working with Menu, commands and IDs
To add an icon or a menu to a window, we don't need a wrapper: the icon ID can
be specified during window class registration, and a menu can be loaded while
creating the window. Until the HMENU remains associated to a
window, it's Windows that provides its destruction.
Thus, here is the modified winmain.
int APIENTRY _tWinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine, int nCmdShow)
{
NWin::SMsgLoop loop(true); NUtil::SResID resWapp(IDR_WApp);
LPCTSTR wcname = _T("W1MainWnd");
NWin::SWndClassExReg wc(true, hInstance, wcname, resWapp);
CMainWnd wnd;
wnd.CreateWnd(hInstance, wcname, NULL, WS_VISIBLE|WS_OVERLAPPEDWINDOW,
NWin::SRect(NWin::SPoint(CW_USEDEFAULT,CW_USEDEFAULT),
NWin::SSize(600,400)), NULL,
LoadMenu(hInstance, resWapp), NULL);
loop.Loop();
return 0;
}
Things become a bit more complicated when we want to manage command user
interface updates (for example: to disable unused commands).
Two common approaches are the following:
-
WTL approach: a command has an associated "state" that the application
manages. GUI elements have the functionality to represent that state when
displayed. Menus typically do during
WM_INITMENUPOPUP,
toolbars during the idle time processing.
-
MFC approach: a GUI element (menus during
WM_INITENUPOPUP,
toolbars during idle processing) queries the application about its state. The
application responds by setting its interface status.
I think the two approaches are "dual": no "best" exists, but one can be favorite
than the other depending on cases: if the command state depends on a variety of
factors dispersed in many places, probably the MFC approach is simpler to code,
while - if the state is a well defined condition, WTL approach is probably
simpler.
I find MFC approach more general, so I implemented a similar one.
Command state
We need an object that abstracts the behavior of a GUI element (a menu item or a
toolbar button or whatever) that can receive a number of states:
Enabled/Disabled, Grayed, Checked, Radiochecked, Indeterminate, Having Text,
Having Image.
This is done through an interface: ICmdState, that provides a set
of abstract functions.
Those functions can be implemented in various classes managing a specific UI
component. For Windows standard menu, this is done by SCmdMnu.
To manage command update, I used two private messages GE_QUERYCOMMANDHANLER
and GE_UPDATECOMMANDUI.
In particular, to update menus:
-
During
WM_INITMENUPOPUP, a GE_QUERYCOMMANDHANLER message
is sent. The LPARAM is a SCmdMnu*, and WPARAM
is a command ID.
-
GE_QUERYCOMMANDHANLER is handled by the COMMAND_xxxx_U
macros (should be used in place of COMMAND_xxxx, the same that
handles WM_COMMANDs) by calling SetHandled.
When a GUI require it is appropriate, (WM_INITMEMUPOPUP, idle
handler, or whatever), it must post a GE_UPDATECOMMANDUI message
(same parameter as the previous). The application can handle this message by
calling ICmdState member functions. Their implementation operates
setting the state of the GUI.
For menus, this stuff can easily be handled by a EWnd derived class
that can be attached, for example, to the main window. This class is EMenuUpdator:
it handles WM_INITMENUPOPUP as indicated.
To use it, we simply create it and attach it to the main window.
int APIENTRY _tWinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine, int nCmdShow)
{
NWin::SMsgLoop loop(true); NUtil::SResID resWapp(IDR_WApp);
LPCTSTR wcname = _T("W1MainWnd");
NWin::SWndClassExReg wc(true, hInstance, wcname, resWapp);
CMainWnd wnd;
wnd.CreateWnd(hInstance, wcname, NULL, WS_VISIBLE|WS_OVERLAPPEDWINDOW,
NWin::SRect(NWin::SPoint(CW_USEDEFAULT,CW_USEDEFAULT),
NWin::SSize(600,400)), NULL,
LoadMenu(hInstance, resWapp), NULL);
NWin::EMenuUpdator mnuupdt;
mnuupdt.Attach(wnd);
loop.Loop();
return 0;
}
That's it.
To process GE_UPDATECOMMANDUI, I provided the macros ONMSG_GE_UPDATECOMMANDUI,
ONMSG_GE_UPDATECOMMANDUI_RANGE, and ONMSG_GE_UPDATECOMMANDUI_RANGE_CODE
(they are in CmdUpdt_macros.h) that can be used in message maps.
They all call a function prototype as:
LRESULT function(
UINT nID, WORD nCode, ICmdState* pCmdState, bool& bHandled );
Pseudo-Bugs fixed
Some pseudo-bugs have been fixed from the previous article. I say "pseudo"
because they are not themselves "bugs" (programming mistakes) but "design
flaws": they reveal lack of functionalities when needed.
Wrapper types
The function WrpBase::Value, originally returns OH,
but -in fact- it should be the return type of the function Access,
inherited from the traits, making the Value function a not useful
clone. In fact, Value should return the "value" as it is stored,
for read-only access, without any conversion. Thus, its return type has been
changed into const SH&.
This has no influence on normal wrappers (SH is H and
OH is const HANDLE&, so that's the same) or
pointers (SH is H*, and OH is H*
again, but this means "copy on return", so const SH& becomes
const H*&, that assigns to an H* in the same way),
but can be substantial in more complex cases, where the returned types are not
the same as the wrapper stored values.
And here is the real case.
Reading custom resources
To read a custom resource, the windows API require the following steps:
-
Call
FindResource passing a HMODLULE and an ID,
to get a HRSRC.
-
Call
LoadResource passing the HRSRC to get a pseudo
HGLOBAL.
-
Call
LockResource to get a LPVOID
to cast appropriately.
-
use the data, then ...
-
Call
UnlockResource passing the HGLOBAL
and then ...
-
Call
FreeResource again with the HGLOBAL.
While step 1 does not involve memory allocation, steps 5 & 6 are the inverse
of 3 & 2 respectively.
Thus, a comfortable wrapper can attach to HRSRC loading the "data",
let them available using dereference, and free the data on detach. If more
wrappers are around the same HRSRC, loading must be done by the
first and freeing must be done by the last. The "data" ... should be a template
parameter.
So, a resource wrapper for a toolbar resource can be (namespaces apart) a Wrp<SResourceData<XToolBarData>
>. If wrpTbrRes is a variable of that type, operator->
will return an XToolbarData*.
SResourcedata, then, derives from WrpBase, closing the
inheritance (passing itself as the "W" parameter) and having
specific traits and reference counters holder.
Because to load a resource we need not only a HRSRC, but also a HMODULE,
we have to store the HMODULE as "initial parameter" into SResourceData,
providing an initialization function (void SetHModule(HMODULE hMod)).
Then, we need to store the HGLOBAL and the Data pointer.
Because these values must survive until there are wrappers around a given
resource, the more comfortable place to store is the shared reference counters
holder.
Thus:
-
an
XRefCountRes<Data> has been defined, derived from XRefCount<SResourceData<Data>
>, holding also two additional members: a HGLOBAL and
a Data*.
-
types have been definded to receive a
const HRSRC& (that's IH),
store an HRSRC(that's SH), access a Data*
(OH) and dereference to a Data& (as RH)
-
Access and Dereference are redefined to access _pRC
(the reference count holder) and returning the Data* or Data&
accordingly. These functions are defined as "static" in the traits, and
inherited in the WrpBase. We cannot rewrite the traits because, at
that level, no reference counter object pointer is defined. Thus I override
them in SResourceData, where they are no more static, but become
const member functions. Because they're always called via pThis()
(that converts into a W*, where W is the wrapper: SResourceData,
in this case) the overrides will be called.
-
OnFirstAttach and OnLastDetach are overridden to do
the "dirty work": Load and Lock the first, Unlock
and Free, the second.
For example, to read a RT_TOOLBAR resource into a XToolBarData,
we can use the following code:
struct XToolBarData
{
WORD wVersion;
WORD wWidth;
WORD wHeight;
WORD wItemCount;
WORD* items()
{ return (WORD*)(this+1); }
};
typedef NWrp::Wrp<NWrp::SResourceData<XToolBarData> > TTbrData;
TTbrData TbrData;
TbrData.SetHModule((HMODULE)hInst);
TbrData.Attach(FindResource(hInst, lpszResourceName, RT_TOOLBAR));
XToolBarData* pData = &*TbrData;
ASSERT(pData && pData->wVersion == 1);
Adding images to menu
A simple way to bind commands to images is to use the Visual Studio toolbar
editor, to create a bitmap and a "toolbar" resource.
During menu initialization, we can convert all the items into "owner draw" (a
task that can be accomplished by an EWnd derived, called EMenuImager),
but ... where do we store the data ?
For commands, we can think to a global map, indexed by command IDs: after all
... command IDs are global. The "value" of the map will be a SCmdDraw::XCmdChara
holding the resource ID the image is from, and the relative image index.
Then, we can convert to owner-draw all the menu items during WM_INITMENUPOPUP
and convert them back to normal during WM_UNINITMENUPOPUP. May be
not so efficient, but it is safe in terms of memory management (we don't keep
all strings having no ID, for example in submenus, from a menu).
It is important that those message are processed after eventual menu
state modification induced by other wrappers. To be sure of this, I let the
message handler to mark the messages as "handled" (no further processing), but
I called Default() at the beginning of the handler.
So:
-
If you wrap first (for example) with
EMenuUpdator and then with EMenuImager,
the subclass window procedure will call EMenuImager that calls Default,
causing EMenuUpdator
to process first.
-
If you wrap first with
EMenuImager and then with EMenuUpdator,
the subclass procedure will call EMenuUpdator first and (because
it left the messages as "unhandled") then EMenuImager.
In both cases, the sequence is first update the items, then, adapt them to be
drawn.
Items drawing
Because there are various ways to draw menu items (in terms of effects and state
rendering: think of Win2K or WinXP or VS-IDE), we can think of different
implementation strategies to handle WM_DRAWITEM.
-
Templetize
EMenuImager and provide a parametric "traits" class
that provides the OnDrawItem
function: it has the drawback that the code is "static": the user cannot switch
between different "traits": we have to provide different template
instantiation, but this also means a multiple static or global data structure
instantiation.
-
Declare
OnDrawItem
as abstract, and implement it in different derived classes.
-
Don't respond to
WM_DRAWITEM, and do it in a separate EWnd
derived, letting to the Windows kernel to do the polymorphism.
-
Derive
EMenuManager, give it a new message map that chains with
the base's one (WTL style).
While A is not suitable for implementations like this, B introduces some
constraints about the use of the functions.
C means declare a EMenuImager_xxx, to attach to the same HWND
wrapped by EMenuImager, where xxx is the aspect we
want to provide. It is probably the most flexible solution, but it's prone to
generate strongly entangled classes: you define a Exxx that seems
a HWND wrapper, but it is not functional by itself and relies on
data generated (and defined) by another independent class.
D is the most traditional, but in this case seems optimum. The implementation
provided is NGDI::EMenuImagerIDE.
Displaying Accelerators
A common practice in programs using accelerators, is to display the shortcut
keys for commands on the right of the menu text.
Writing the shortcut names in the MENU resource it is not - however
- a good practice: Message translation is based on HACCEL that is
loaded from an "accelerator table" that is not itself the menu. You change the
shortcuts by editing the accelerator table, but you must also change the menu
text. And if a same command appear in many different menus ... may be it is not
so easy to track.
An alternative can be to avoid to place the text in the MENU resource, but
provide a way to alter the menu text before displaying it. This can be easily
done in EMenuUpdator. By making this class aware of an accelerator
table and by providing it the textual descriptions for all the keyboard keys,
we can - just before displaying a command - browse the given accelerator table,
find the accelerator for a given command, retrieve its text and then add it to
the right of the menu.
And because this means "to associate an accelerator table to a window", we can
hook to the SMsgFiltrerEvent singleton when becoming active (and
unhook when becoming inactive), and call the TranslateAccelerator API.
Because this event is generated by the message loop just before dispatching a
message, this will - in fact - make the accelerators associated to the window,
working.
All this is implemented in EMenuUpdator.
In particular, to create the text to be used to describe the shortcuts, I used a
RC_DATA resource that is a sequence of a pair, composed by a WORD
followed by a C-string. The WORD is a VK_xxx constant
representing a keyboard functional key, and the C-string is its description.
The last three record of the resource must have 0 as ID, and report "Shift+",
"Ctrl+" and "Alt+" (or whatever description they have in a given localization)
respectively.
In EMenuUpdator, the XKeyNames structure is a helper
to read this resource: I used it wrapped into a SResourceData<>,
attached to the HRSRC given by FindResource. The XKeyTexts
structure is instead where the description are stored, indexed by the VK_xxx
ID. (See its Load function implementation.)
To stay within the functionality of the IDE, I provided to the NLIB project the NLib.rc
resource file, and the Accels.rc2 file. And also a NLib_res.h file
containing the manifest constant definitions.
NLib.rc is configured to include NLib_res.h as its own header and
the Accels.rc2 as an included resource file. Because Nlib is a library, NLib.rc
must not be itself compiled. It must instead be included in the "exe" project
resource file: in our case, the "W3" project has a resource file (W3.rc)
that includes the traditional resouce.h and also the Nlib_res.h,
and also includes in its body the NLib.rc file.

The test project
I generated some projects to test and as samples of usage.
The executable is a pure Win32 project making use of NLib as static library.
The stdafx.h file includes the NLib/StdAfx.h, and - for non debug
versions - defines the GE_FORCEINLINE symbol: this results in the
NLib CPP files to be included at the end of their respective .h files,
with all functions declared as "inline".
Because the solution contains two projects and because W3 depends on NLib, there
is the need, even when the all code is inlined, to still have a Nlib.lib
to link. This is obtained by providing a emptylib.cpp source just
defining a local hidden symbol (no meaning, just to have something to compile,
or no library is generated), and by differentiating the Debug configuration
(where all files but emptylib.cpp are included in the generation
process) and the release version (where the generation rule is inverted). emptylib.cpp
is also configured to not use the precompiled headers.
The WinMain function is very simple:
int APIENTRY _tWinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
NUtil::STrace::_Filter() = 2;
NUtil::SWinMain winmainparams(hInstance);
NWin::SMsgLoop loop(true);
CMainWnd wnd;
wnd.Create(hInstance);
loop.Loop();
return 0;
}
Essentially a CMainWnd is created and a message loop executed.
CMainWnd is declared as follows:
class CMainWnd: public NWin::EWnd
{
protected:
NWin::EMenuUpdator _mnuupdt;
NWin::EMenuImagerIDE _mnuimg;
public:
CMainWnd() {}
bool Create(HINSTANCE hInstance);
protected:
LRESULT OnFileExit(WORD wNotifyCode,
WORD wID, HWND hWndCtl, bool& bHandled);
LRESULT OnExecSomeCommand(WORD wNotifyCode,
WORD wID, HWND hWndCtl, bool& bHandled);
LRESULT OnCreate(LPCREATESTRUCT lpcs, bool& bHandled);
LRESULT CMainWnd::OnPaint(bool& bHandled);
BEGIN_MSG_MAP(CMainWnd)
ONMSG_WM_CREATE(OnCreate)
ONMSG_WM_PAINT(OnPaint)
COMMAND_ID_HANDLER_U(ID_FILE_EXIT, OnFileExit)
COMMAND_ID_HANDLER_U(ID_HELP_ABOUT, OnExecSomeCommand)
COMMAND_ID_HANDLER_U(ID_FILE_NEW, OnExecSomeCommand)
COMMAND_ID_HANDLER_U(ID_FILE_OPEN, OnExecSomeCommand)
COMMAND_ID_HANDLER_U(ID_FILE_SAVE, OnExecSomeCommand)
END_MSG_MAP()
};
Note the COMMAND_ID_HANDLER_U macros in the message map, and note
that some commands had been implemented through the same "placeholder" function
OnExecSomeCommand. The Create function is a shortcut to
handle WNDCLASSEX registration and the real window creation, via
EWnd::CreateWnd(...).
The OnCreate function (handler for WM_CREATE)
initializes the other member wrappers and attach them to the same CWnd.
Of course, there's no need for those wrappers to be members, but enclosing them
into a same class makes the data structure more ordered.
Here are the two bodies:
bool CMainWnd::Create(HINSTANCE hInstance)
{
LPCTSTR wcname = _T("W3MainWnd");
NUtil::SResID resWapp(IDR_WApp);
static NWin::SWndClassExReg clsrg(true, hInstance, wcname, resWapp);
//instantiate a window class
if(!CreateWnd(hInstance, wcname, _T("W3 test"),
WS_VISIBLE|WS_OVERLAPPEDWINDOW|WS_CLIPCHILDREN,
NWin::SRect(NWin::SPoint(CW_USEDEFAULT,CW_USEDEFAULT),
NWin::SSize(600,400)), NULL,
LoadMenu(hInstance, resWapp), NULL))
return false;
return true;
}
LRESULT CMainWnd::OnCreate(LPCREATESTRUCT lpcs, bool& bHandled)
{
Default();
NUtil::SResID resWapp(IDR_WApp);
_mnuupdt.LoadAccelerators(HInstance(), resWapp);
_mnuupdt.Attach(*this);
_mnuimg.GetCmdImgs().LoadCmdImages(HInstance(), resWapp);
_mnuimg.Attach(*this);
return 0;
}
Note that at the moment of attaching the additional wrapper, the window already
exists. In fact, at the end of OnCreate, the window appears to
have three referring wrappers. Note also that non "chain" is done in the
message map: this is automatically done by the EWnd::Attach-ing
and Detach-ing processes.
The menu commands File/New, File/Open, File/Save and Help/About have been
implemented through a dummy function.
The commands Menu/AutoUpdate and Menu/Images are toggles. They are checked if
the _mnuupdt and _mnuimg are respectively attached.
The commands are implemented by Attaching / Detaching the inner wrappers
alternatively.
Note that, when detaching _mnuupdt, menus stop to update their own
state. If the detachment is done immediately after the application startup,
without displaying other menus, menus will appear as "all enabled", and without
shortcut descriptions. If it is done later, menus will retain the "last set
state".
Debug "verbosity"
You can see at the very beginning of WinMain, the line "NUtil::STrace::_Filter()
= 2;". This has been done to reduce the debug output as generated by
STrace and STRACE.
In the code, I set all message retrieving as having "levl 0" and message
dispatching / memory allocation to have "level 1". If you like to have a more
verbose debug output, you can set the value to 1 or even to 0. The
inconvenience is that menu navigation becomes "slow" because of the wrapping /
unwrapping activity of GDI objects and all the messages that activity
generates.
Conclusion
May be I'm still reinventing wheels, but - strange - I really enjoyed while
enhancing these features even more while writing code with WTL.
I don't pretend all this to become a new library for "real good apps". The only
thing I hope is someone learns something with this.
And since it was a nice experiment, I will continue: docking toolbars, docking
windows, serializations ... many arguments to treat in future articles.
History
- 2004/3/29 - some text revision.
- 2004/4/26 - code bug fixed