Click here to Skip to main content
Email Password   helpLost your password?

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:

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);  //instantiate a loop

    NUtil::SResID resWapp(IDR_WApp);

    LPCTSTR wcname = _T("W1MainWnd");
    //instantiate a window class

    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:

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:

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);  //instantiate a loop

    NUtil::SResID resWapp(IDR_WApp);

    LPCTSTR wcname = _T("W1MainWnd");
    //instantiate a window class

    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,             //command ID

  WORD nCode,           //notification code (if from a control)

  ICmdState* pCmdState, //the ICmdState whose functions modify the GUI state

  bool& bHandled      //setted to true before calling the function. 

  );      //return value: should always be 0.

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:

  1. Call FindResource passing a HMODLULE and an ID, to get a HRSRC.
  2. Call LoadResource passing the HRSRC to get a pseudo HGLOBAL.
  3. Call LockResource to get a LPVOID to cast appropriately.
  4. use the data, then ...
  5. Call UnlockResource passing the HGLOBAL and then ...
  6. 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:

For example, to read a RT_TOOLBAR resource into a XToolBarData, we can use the following code:

    // Note: suppose we already have

    //  HINSTANCE hInst; LPCTSTR lpszResourceName;

    //

    struct XToolBarData
    {
        WORD wVersion;
        WORD wWidth;
        WORD wHeight;
        WORD wItemCount;
        //WORD aItems[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);
    // use pData as needed.

    // resource will be release when leaving the scope

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:

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.

  1. 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.
  2. Declare OnDrawItem as abstract, and implement it in different derived classes.
  3. Don't respond to WM_DRAWITEM, and do it in a separate EWnd derived, letting to the Windows kernel to do the polymorphism.
  4. 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.

Resource files relations

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);  //instantiate a loop


    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

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralPart 3 is out
emilio_g
2:20 27 May '04  
Please check out part 3 [^]of the article.
I'm no more maintaining the code assiciated with this one.

In the new article, some code revamping and added functionality.

Generalhow about the thunk?
ChauJohnthan
15:34 9 May '04  
then how would you handle this?
GeneralRe: how about the thunk?
emilio_g
21:07 9 May '04  
ChauJohnthan wrote: then how would you handle this?
Can you be less "ermetic"? Handle what ? Which thunk ?

GeneralRe: how about the thunk?
ChauJohnthan
14:11 13 May '04  
Hi, emilio_g:
sorry. I mean this one:

http://www.codeproject.com/atl/atl_underthehood_5.asp


GeneralRe: how about the thunk?
emilio_g
22:25 13 May '04  
OK, now it more clear.

But there is the need to be clear on some points.
1) I'm not using ATL or rewriting ATL. What I presented here is an alternative. Of course, because managing Windows(tm) windows is always a same activity in all frameworks, the interface appearence is similar to ATL or WTL or other template based Win32 wrappers. But the "engine" is different.

2) The "engine" is the EWnd class that inherits ECHainedAction and Wrp<>, where Wrp is specialized to operate as a chain (wrappers chain together in a list embedded in a refcounter object that is mapped in a static global map, keyed by HWND)

Thus, no "ATL-like thunk" is needed. A global window procedure looks up a static map with HWND and find the XRefCount that represent all the wrapper that any program can had putted around a window. (Bucause you can wrap more times) than it "walks" the HWND associated chain calling their respective message maps.

So - basicly - the "thunk" (if we still want to name it this way) is an std::map mapping HWND to XRefChainWnd, that contains an std::list of EWnd*.

Clearly, the advantage is that the code is almost universal (does not depend on the way HWND or data structure are implemented in a given machine or system). The disavantage is that you don't have a "direct access" but you've to rely on STL algorithms to find where to dispatch what.


GeneralRe: how about the thunk?
ChauJohnthan
7:01 15 May '04  
(Bucause you can wrap more times) than it "walks" the HWND associated chain calling their respective message maps.
how about when serveral HWNDs corresponding to one winproc?
how about when subclass works in your system?


ok! I will download, and look at that map structure.
GeneralRe: how about the thunk?
ChauJohnthan
19:57 22 May '04  
seems have nothing new there.
another but unproved atl.

what makes you start this project?
GeneralRe: how about the thunk?
emilio_g
2:11 23 May '04  
ChauJohnthan wrote: seems have nothing new there. ... Apart that ATL doesn't subclass a window more than once
... Apart that ATL don't use STL
... Apart that ATL use proprietary collection classes
... Apart that ATL does'nt have a "real" generic code (GDI object ATL wrapper are all but generic!)

... Apart that I don't like it.

ChauJohnthan wrote: another but unproved atl. I never knew of a new project starting as "proved" !

ChauJohnthan wrote: what makes you start this project? The idea that writing for windows id not necessarily a problem of a framework library. There's no "framework" concept, in this implementation. The entalgment between classies is reduced to the minimum necessary.

GeneralRe: how about the thunk?
ChauJohnthan
6:32 23 May '04  
... Apart that ATL doesn't subclass a window more than once
really? I don't think so.


... Apart that ATL don't use STL
I don't use STL, there IS atl, why stick on STL?

... Apart that ATL use proprietary collection classes
not a bad thing, as I think.

... Apart that ATL does'nt have a "real" generic code (GDI object ATL wrapper are all but generic!)
so it's flexible. do you plan to code a bunch of gdi class? admire your endeavorSmile

The idea that writing for windows id not necessarily a problem of a framework library. There's no "framework" concept, in this implementation. The entalgment between classies is reduced to the minimum necessary.

I am using ATL, I think I can make it minimum.



GeneralRe: how about the thunk?
emilio_g
4:51 24 May '04  
I' suspecting you missed the beginning of the article:

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!

If you think that ATL is the best for anything, use it!
If you want to test a different approach ... try this.
That's all. I'm not competing with ATL.

GeneralRe: how about the thunk?
ChauJohnthan
0:14 25 May '04  
until now, there is nothing new in your source.
but this idea is not so bad.

If you think that ATL is the best for anything, use it! this is why I spent a whole day to read your source code.

If you want to test a different approach ... try this.
That's all. I'm not competing with ATL.

seems you need a framework design before you start this
lightweight approach.

anyway,in the end, I will give my 5 points to you.

good luck!


Chanenge? What's that? oh! I like it reallySmile
GeneralRe: how about the thunk?
ChauJohnthan
10:38 24 Apr '05  
hi, emilio_g:

i have extract the thunk implementation of atl as the following:

#pragma once
#include
struct CREATEWNDDATA
{
void* m_pThis;
DWORD m_dwThreadID;
CREATEWNDDATA* next;
CREATEWNDDATA* prev;
}* g_pWndDataList;

struct EXTWNDCLASSINFO
{
WNDCLASSEXW m_wc;
WNDPROC pWndProc;
LPCWSTR m_lpszCursorID;
ATOM m_atom;
};

template class __declspec(novtable) CminWindowImpl
{
public:
HWND m_hWnd;
CREATEWNDDATA cd;

#pragma pack(push,1)
struct _stdcallthunk
{
DWORD m_mov; // mov dword ptr [esp+0x4], pThis (esp+0x4 is hWnd)
DWORD m_this; //
BYTE m_jmp; // jmp WndProc
DWORD m_relproc; // relative jmp
void Init(DWORD_PTR proc, void* pThis)
{
m_mov = 0x042444C7; //C7 44 24 0C
m_this = PtrToUlong(pThis);
m_jmp = 0xe9;
m_relproc = DWORD((INT_PTR)proc - ((INT_PTR)this+sizeof(_stdcallthunk)));
// write block from data cache and flush from instruction cache
FlushInstructionCache(GetCurrentProcess(), this, sizeof(_stdcallthunk));
}
//some thunks will dynamically allocate the memory for the code
void* GetCodeAddress() { return this; }
} thunk;
#pragma pack(pop)

void Init(WNDPROC proc, void* pThis)
{
thunk.Init((DWORD_PTR)proc, pThis);
}

WNDPROC GetWNDPROC()
{
return (WNDPROC)thunk.GetCodeAddress();
}
public:
CminWindowImpl( HWND hWnd=NULL) : m_hWnd(hWnd)
{}

public:
virtual LRESULT ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

virtual WNDPROC GetWindowProc()
{
return WindowProc;
}

static LRESULT CALLBACK StartWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
CminWindowImpl* pThis = NULL;

CREATEWNDDATA* pEntry = g_pWndDataList;
if(pEntry != NULL) {
DWORD dwThreadID = ::GetCurrentThreadId();
CREATEWNDDATA* pPrev = NULL;
while(pEntry != NULL) {
if(pEntry->m_dwThreadID == dwThreadID) {
if(pPrev == NULL)
g_pWndDataList = pEntry->next;
else
pPrev->next = pEntry->next;

pThis = (CminWindowImpl*)(pEntry->m_pThis);
break;
}
pPrev = pEntry;
pEntry = pEntry->next;
}
}

assert(pThis != NULL);
pThis->m_hWnd = hWnd;
pThis->Init(pThis->GetWindowProc(), pThis);
WNDPROC pProc = pThis->GetWNDPROC();
WNDPROC pOldProc = (WNDPROC)::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)pProc);
pOldProc; // avoid unused warning
return pProc(hWnd, uMsg, wParam, lParam);
}

static LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
CminWindowImpl* pThis = (CminWindowImpl*)hWnd;
return pThis->ProcessWindowMessage(pThis->m_hWnd, uMsg, wParam, lParam);
}

HWND Create(HWND hWndParent, LPRECT rect, LPCTSTR szWindowName = NULL,
DWORD dwStyle = 0, DWORD dwExStyle = 0, LPVOID lpCreateParam = NULL)
{
static EXTWNDCLASSINFO wci = T::GetWndClassInfo();
if(wci.m_atom==0) {
wci.m_wc.hCursor = ::LoadCursorW(NULL,wci.m_lpszCursorID);
wci.m_wc.style &= ~CS_GLOBALCLASS; // we don't register global classes
wci.m_atom = ::RegisterClassExW(&wci.m_wc);
}

if(wci.m_atom == 0)
return NULL;

assert(m_hWnd == NULL);
cd.m_pThis = this;
cd.m_dwThreadID = ::GetCurrentThreadId();
cd.next = g_pWndDataList;
g_pWndDataList = &cd;

HWND hWnd = ::CreateWindowEx(dwExStyle, MAKEINTATOM(wci.m_atom), szWindowName,
dwStyle, rect->left, rect->top, rect->right - rect->left,
rect->bottom - rect->top, hWndParent,0U,wci.m_wc.hInstance, lpCreateParam);

assert(m_hWnd == hWnd);
return hWnd;
}
};

here is my window class def:

class xWindow :
public CminWindowImpl {
public:

xWindow(){;}
~xWindow(){;}

public:
LRESULT ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_CREATE:
return 1;
case WM_ERASEBKGND:
return 1;
case WM_DESTROY:
PostQuitMessage(1);
break;
default:
return DefWindowProc(hWnd,uMsg,wParam,lParam);
}
return 0;
}

static EXTWNDCLASSINFO& GetWndClassInfo()
{
static EXTWNDCLASSINFO wc =
{
{ sizeof(WNDCLASSEX), CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS, StartWindowProc,
0, 0, g_hInst, NULL, NULL, (HBRUSH)(COLOR_WINDOW + 1), NULL, _T("mini window"), NULL },
NULL, IDC_ARROW, 0
};
return wc;
}
}

at last i used it like the following:

xWindow* x = new xWindow();
RECT r = { 200, 200, 600, 400 };
x->Create(0, &r, _T("mini window"), WS_OVERLAPPEDWINDOW);
ShowWindow(x->m_hWnd,SW_SHOW);

now i want to avoid thunk things, maybe you can help me out here, bcz i heard ms will
not support thunk in the future. as you note, i prefer to use linked list, not std:map.


thanks in advance!




GeneralRe: how about the thunk?
emilio_grv
22:34 25 Apr '05  
By now, give a look at http://www.codeproject.com/cpp/ge_stdx1.asp[^].

It is the version 2, and has a better structure. I'm actually working on version 3, but, in fact, I'm still based on STL and std::map in particular. Note that the overall structure is an std::map of event sources that are themselves container for an std::list of receivers.

The point is: you have an HWND and you need a C++ pointer (smart pointer, in this case). std::map (or - in general - a MAP, keyed with the HWND value) is the natural structure to do this. A linked list requires you to linear scan the list to search for a given HWND. A map does this thorough an internal b-tree, and this is normally faster.

If your point is "I want to avoid STL at all" ... well ... it is possible, but I don't feel it as a "need".




2 bugs found.
> recompile ...
65534 bugs found.
D'Oh!

GeneralRe: how about the thunk?
ChauJohnthan
0:07 26 Apr '05  
hi, emilio_grv:


> If your point is "I want to avoid STL at all" ... well ... it is possible,
but I don't feel it as a "need".

yeah, i want to avoid stl. if not, i will don't think about your project
at all.

as to the stl:map, "an internal b-tree, and this is normally faster", you
should read CAtlMap, CAtlArray. but here, i think it doesn't matter, bcz it
doesn't have many data here, no one will have more than 100 windows in their
app. anyway, if it is really needed, to implement b-tree should not be a
problem for us coders.

frankly to say, (pls forgive me), i think you are wasting your time. you
really should try to use wtl for a while before you set about to refine your
work. i can't understand what you do it for. Confused


GeneralRe: how about the thunk?
emilio_grv
7:58 26 Apr '05  
Humm ... CAtlMap and std::map are mostly the same (in term of implementation), written with different code styling... The problem is what you like, rather them what is best.

But ... ATL/WTL use thunking. Or ... you want to use WTL but ask me how to replace WTL thunking without stl ... with your coder that can implement linked list ... Confused Confused .

You can potentially do everything. Just copy my idea, and re-implement it using the librqary you like. But it has very few with what I presented here. If you like ATL or WTL so much ... why don't simply use it? Thunking works fine on 32 bit systems, but needs to be rethought for W64. That's the point.

ChauJohnthan wrote: frankly to say, (pls forgive me), i think you are wasting your time. you
really should try to use wtl for a while before you set about to refine your
work. i can't understand what you do it for.

Just to enjoy myself it trying alternatives, giving some ideas to coders. I know WTL quite well, that's why I introduced the concept of "shared ownership" and "reference couting" applyed to handles and multiple message dispatching for a same window.

WTL miss it. It give ways to arrange it. But not structuring it.
If you are not interested in this, use WTL.
May be I'm wasting my time. But ... that's mine.



2 bugs found.
> recompile ...
65534 bugs found.
D'Oh!

GeneralRe: how about the thunk?
ChauJohnthan
10:33 26 Apr '05  

>But ... ATL/WTL use thunking. Or ... you want to use WTL but ask me how to replace WTL thunking without stl ... with your coder that can implement linked list ... .
i used a lot of my home-cooked linked list staff in my project. previously i used atl. i reinvent the wheel,
not because atl implemented that not well, it is because i need more flexibility.

>You can potentially do everything. Just copy my idea, and re-implement it using the librqary you like.
by now, i can't get your idea. sorry, i have no time to review. i can guess that you must have some splended idea
in your lib.

>But it has very few with what I presented here. If you like ATL or WTL so much ... why don't simply use it?
more easy to use always means less flexibility.

Thunking works fine on 32 bit systems, but needs to be rethought for W64. That's the point.
> thanks for this comment.

>Just to enjoy myself it trying alternatives,
i agree. i am finding alternative of wtl.

>giving some ideas to coders.
i wish you can, i am right here waiting.

>I know WTL quite well,
i don't suspect your knowledge about wtl, i mean you can take me as the user of your lib. how do you conceive me to
turn from wtl to your lib? this is the key point i want to present to you.

>WTL miss it. It give ways to arrange it. But not structuring it.
i think you should have a talk with Nenad.
he created wtl, and is still working on this by now as i know. he is
a telant nice guy, and very humorours from my eye.

>May be I'm wasting my time. But ... that's mine.
sorry for my rudeness again. thanks for your time to reply.




GeneralRe: how about the thunk?
emilio_grv
22:16 26 Apr '05  
Thanks for your appreciations, but I must wan you about certain possible misinterpretations:
WTL is a true "product", with lots of man hours (man years!) of development, providing lot of classes that started with the idea to simplify coding for COM and COM-related technologies (like OLE, ActiveX etc.).
What I presented here is not a "product" (and doesn't want to be) but only a set of ideas (reference counting has not been invented by me, of course), and a tutorial showing about how these ideas can be implemented in the WinXX context.
You can certainly write Win32 apps with my code, but to think to my code as a full replacement of WTL … there is lot of gaps to be filled.
I didn't deploy any support for COM, active-X etc. What I did is use the features of C++ in a way as standard as possible, to manage windows handles with the less possible coding.
You can reuse part of my code to handle interfaces etc., but don't expect Wizards, IIL, etc. from me.
You can complement WTL with some of my classes, if you find it convenient in certain situation, but not think in term of WTL replacement. Or … well, not by now!

About including certain ideas in WTL … I'm not going to put myself in the "big project", but –if you know Nenad- just pass him the pointer to these articles. I think he has all the staff do catch the ideas and give them an implementation within WTL, if he finds it useful.

About thunking: the difference between 32 bits and 64 bits is the length of pointers. Hence, the "structure that places the call", should have a different size and a different offset to do a proper jump.
Note that this is not related to Win32 or Win64 APIs or to Microsoft deployments or choices, but to the way pointers are implemented by those systems processors. It is –however- always a trick that is not "granted" to always work properly: you allocate a data structure whose data are treated as instruction code (note: "instructions", not "pointers to …"). This means that the hosting system must be capable to "run" instructions placed inside the data segment. In general it's not something that can be assumed by default as "always true" for every processor or system.


2 bugs found.
> recompile ...
65534 bugs found.
D'Oh!

GeneralRe: how about the thunk?
ChauJohnthan
23:28 26 Apr '05  
hi, emilio_grv:

i forgot to tell you that my project invovled into COM things
heavily. atl is designed for ActiveX which is mostly worked in IE.
but i design widget used by my own host app.

> –if you know Nenad- just pass him the pointer to these articles.
Nenad taught me a lot about WTL, but i can't still grasp what
your lib done(sorry). so you'd better send msg to him by yourself.
seems he is always busy. but i am sure he is really a kind guy.

anyway, all best wish to your project!
GeneralGreat endeavor...crash on exit in VC7.1
Norman Bates
9:23 3 Apr '04  
I love the concept & the approach!

seems to crash on cleanup doing an UnregisterEvent in EventRcv at list::iterator::remove_if, where the return call on begin() asserts;

Unhandled exception at 0x0046cab2 in W3.exe: 0xC0000005: Access violation reading location 0x00000000.

fyi: there are a couple of typename prefixes missing in ResWrp.h "SResourceData" on Access, Dereference, CAccess and CDefference.

keep up the great work, i'm looking forward to parts 3 and 4...

nb
GeneralRe: Great endeavor...crash on exit in VC7.1
emilio_g
8:33 4 Apr '04  
Thank you for the suggestion.
I'm not planning to go to 7.1 by now. So ... "missing typename" is a nightmare ! (I have pratically no way to catch this error)

About the crash: it seems something of static had been cleened up AFTER something else,instead of BEFORE. It happens when XMap is instantiated AFTER a global Event (like the idle event). This mainly depend on the order files are compiled or linked.

I already solved ... should no more happen since part 3.
By now, in MsgLoop.cpp there is an unnamed namespace containing an initializator. Move it in Wrp.h just after XMap definition, like this

struct XMap: public NUtil::EMap<LPVOID, XRefCount_base*>
    {};
namespace{
//make us shure NWrp::XMap begins to exist as soon as possible
// (and hence, destroyed as late as possible)
struct XMap_init
{
XMap_init() {NWrp::XMap::Unmap(0);}
};

_declspec(selectany) XMap_init g_xmap_init;
}

Hope it will help.
GeneralRe: Great endeavor...crash on exit in VC7.1
Norman Bates
4:04 5 Apr '04  
thanks, no luck yet though...

here's what seems to be happeing;

t_lstptrsink (std::list) is destroyed in EventRcv, then ~EWin to ~WrpBase goes thru EMenuUpdater back to EventRcv to UnregisterEvent in the already destructed std::list - see below.

W3.exe!std::listW3.exe!std::list::remove_if() *** accessing destructed list here ***
W3.exe!NWrp::EventRcv::UnregisterEvent()
W3.exe!NWin::EMenuUpdator::OnActivate(bool bActive=false)
W3.exe!NWin::EMenuUpdator::OnRelease(NWrp::XRefCount_base * pRC=0x00858070)
W3.exe!NWrp::XRefChain::Release(NWin::EWnd * pW=0x0012fe28, bool bOwn=false)
W3.exe!NWrp::WrpBase::Detach()
W3.exe!NWrp::WrpBase::~WrpBase()
W3.exe!NWin::EWnd::~EWnd()
W3.exe!std::list::~list() *** destroyed here ***
W3.exe!NWrp::EventRcv::~EventRcv()
W3.exe!NWin::EMenuUpdator::~EMenuUpdator()
W3.exe!CMainWnd::~CMainWnd()

GeneralRe: Great endeavor...crash on exit in VC7.1
emilio_g
5:00 5 Apr '04  
Oh f#@k !!

EMenuUpdator is a member of CMainWnd. It goes away, destroying its bases and causing a Detach, and the consequent release calls it back ... but it is no more there !

You crash while calling remove_if, but - in fact - calling OnRelease is wrong: the wrapper is already ded !

It's strange on my machine (7.0 compiler) I didn't get it: may be the memory is still there (~CMainWnd is not yet returned), while 7.1 probably consider it already invalid (as correctly is ...)

I fix this and repost the zip as soon as possible.

Thanks. Very nasty bug ...


GeneralRe: Great endeavor...crash on exit in VC7.1
emilio_g
22:14 5 Apr '04  
Just sent the new zip-s to the webmaster.
UPDATED (2004 Apr 27)

I must very thanks Norman Bates, for discovering a bug that - on analizing- I discovered to be a "design bug".

WrpBase::~WrpBase was originally calling pThis->Detach().
This is wrong for two reasons:

  1. pThis() casts WrpBase to the derived W type, but, inside WrpBase destructor ... W is already
    destroyed !
  2. Detach call the reference counter object Relese function,
    that may call-back the W object (still already destroyed)

There is apparently no way to get out of this: Autodetaching of wrappers is the
strength of wrappers, but ... that's tricky.

The solution is ... call Detach() from every WrpBase derved class
destructor
. And to avoid forgetting some, I placed an ASSERT inside the WrpBase destructor: no _pRC must yet
exist at that time !

Another nasty bug is in XRefCount_base: it must have a virtual
destructor, or the "delete this" inside eventually derived Release functions ... will not destroy properly.




GeneralQuestion ?
HumanOsc
2:43 27 Mar '04  
I don't understand one thing... Why use the autor often the struct keyword ?

For example:

//generic sync object
template<
class A //parameters class
> struct XSink: //will be created by the receiver when registering
public XSink_base,
public WrpBase, XRefChain >,
public EChainedAction {
friend TRefCount;
friend TChainedAction;
private:
virtual bool DoAction(const A& a)=0; //abstract: different type of sink will chain together ...
bool OnAction(const A& a)
{ return DoAction(a); }
virtual Event_base* Get() const { return TWrpBase::Value(); }
public:
virtual ~XSink() {}
virtual void Unregister()=0;
};

Exists any reasons for this (Performance or other) ?

THX...




GeneralRe: Question ?
emilio_g
0:06 28 Mar '04  
HumanOsc wrote: Exists any reasons for this (Performance or other) ?
No, struct and class are essentially equivalent keyword.
I used them differently depending on the usage expected for such objects.

In my mind a "class" is something that is made available to represent something with a given identity (and usually hides its content), while a "struct" is just a data container with some helper functions. But it's only a personal opinion.

The class viewer of visualstudio, also, uses different icons for classes and structs, and sorts them differently,letting classes to appear before.

So, class can be used to represent something you can rely on to create something new. In contrast, struct can be used to represent something to be used "as is".

Again, prefixes also give another ortogonal vision:
C means "normally on heap"
E means "nomally a component to multiply derive from"
S means "normally on stack"
X means "internally used"

Of course, if we're creating low level object or functions ... those prefix tekes a different importance !



Last Updated 27 Apr 2004 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010