Click here to Skip to main content
Click here to Skip to main content

Windows subclassing and hooking with C++ classes

By , 9 Oct 2003
 

Introduction

The first time I saw the CWnd class documentation I found it great. A very good wrapper for W32 API into the C++ context. But as time goes on, certain choice MFC developers did in the past becomes no more suitable for what the enhancement of the language had been. In particular, there is no possibility to subclass a Windows “window” more than once by a CWnd object. The problem has been solved in a variety of ways (I remember Paul Di Lascia, from Microsoft). I propose this solution, based essentially on STL collections and a class designed to capture a window procedure and doing its own dispatching.

All you have to do is derive from CWndSubclasser class and override the SubWndProc function. You can instantiate on the heap as many instances you want and associate each instance to a window. Various subclassers can be associated to a same window. Windows are identified by their HWND, hence, they are not required to be CWnd windows.

Furthermore, with the same technique used for window subclassing I also created classes to capture the WH_CALLWNDPROC (they may be useful if you have to capture a particular message independently of the window it is directed) and the WH_FOREGROUNDIDLE (useful to manage idle time processing for example in a library, and you cannot necessarily have access to the CWinApp object) hooks.

These classes are independent. You don't need to hook to subclass or to subclass to hook.

Class derivation and subclassing

The two concepts must not be confused.

Class derivation happens in OOP languages, and - essentially - consist in a definition of a class based on other base classes, where certain functions (probably virtual) are replaced. This happens inside the definition of a class.

Windows subclassing happens when a window procedure is replaced by another that may (or not, or may sometime) call the original one. This happens outside and independently of the definition of the "class" (or ... what defines the original window procedure). In this sense, class derivation is "static" (done by the compiler), while window subclassing is "dynamic" (done at runtime).

In MFC all windows are based on the same window procedure (AfxWndProc) that, once detected the window (HWND) a message is referred, dispatch that message to a virtual function of a CWnd object (OnWndMsg) that parses the associated message map and calls the required handler (if any) or calls Default, that – in turns – pass the original message to DefWndProc or to an original window procedure eventually existing if the window was not created by MFC.

When doing this, MFC is itself doing a “subclassing”, but it stores the original procedure in a single member variable. Hence only one (or none, if you get the CWnd frown an existing non-MFC window) subclassing is possible.

The idea of “subclassing” is essentially the same that comes with Win32: you replace a window procedure with another and – while processing messages in the new procedure – decide when and how to call the previous one (the default behavior).

The need of subclassing happens when you have to make a particular task over a particular message for a variety of different windows.

Each different window may be – itself – a CWnd derived object, but if you have to trap some messages (for example to customize menu behaviour or appearance in a same way for all your windows) you have to re-implement the same handlers for all the CWnd classes. That’s where subclassing may be useful: You create another object that intercepts the window procedures, and associate an instance for each of the window.

This object defines what to do with the messages and calls the original window procedure when needed.

In this implementation, however, I didn’t want to use one window procedure for each subclass, but rely on a virtual function of a specific object. So I provide a global internal window procedure that replaces the old one (if it is AfxWndProc , it means we are subclassing an MFC window … without MFC knowing that) and dispatch the messages iterating with a recursion (I’ll be clearer later) through an HWND associated list of “subclassers”.

In fact, “subclassers” are stored in reverse order on an std::lst and list are stored in std::map associated to HWND . The very first time a CWndSubclasser object is associated to a window, the window is subclassed (in W32 sense). All subsequent CWndSubclassers eventually associated to that window, don’t subclass it again, but simply chain into the list associated to the window.

When a message comes to the window procedure, it identifies the list and calls a virtual function (WndProcSub) on the first object in the list (the last associated to that HWND).

Its up to you to call – in your processing – the Default() member function that recursively calls that virtual function on the next object (or the previous window procedure, if the list is ended). Thus, wherever you place the Default() call (at the beginning or at the and of your override) you – in fact – affect the order of processing. Exactly like calling DefWndProc in Win32.

Hooking

To “hook”, more simply, I just provide a static function that dispatch to an internal list of object. Such objects are derived from CHookIdle or from CWndProcHook as needed.

History and dependency

During the deployment of the subclasser, I found that a problem arise with subclasser destruction.

In particular, DefWndProc (that is mostly used by every Windows window) often process messages generating other messages. This leads to the window procedure (whatever it is) to be called recursively. This means that we cannot destroy a subclasser – for example – on the WM_NCDESTROY message, because its virtual function may be still invocated (with data pushed on the stack) from a previous (but not yet returned) WM_SYSCOMMAND (the click on the “close” button on the caption bar).

So, the following rule must be applied:

  1. Always construct subclassers object on the heap, and don’t associate their deletion to a CWnd destruction.
  2. Delete the object only after verifying that no more recursions are in progress.

A very simple way to do this is using smart pointers (uh-oh ... the header included here is more recent than the one posted in that article ... it will be better to post an update!): in fact, in the provided window procedure (is in WndSubclasser.cpp), every time a subclasser needs to be called, a smart pointer is defined on the stack and initialized to the subclasser instance.

If you also, after creating the instance on the heap, refer it with a GE_::Safe::PtrStrong, you can be sure that the subclasser will never be destroyed until “something” (you or the window procedure) still needs it.

When you don’t need your subclasser anymore, just set your referring smart pointer to NULL (or destroy the smart pointer). The object will be deleted only after all recursions have returned (because every recursion destroys it’s own originated smart pointer on return).

Now, being these object designed to be handled by smart pointers, I do it in the safest way, by deriving the subclasser base from Safe::ObjStrong. The provided sample does exactly what is described.

// ../utility/WndSubclasser.h"
    ...            
    class CWndSubclasser;
    typedef Safe::PtrStrong<CWndSubclasser> PWndSubclasser;
    ...
            
// Appwnd.h
...
class CAppWnd :
    public CFrameWnd
{
protected:
    PHookIdle1 _pHookIdle1;
    GE_::Utils::PWndSubclasser _pS1, _pS2;
public:
    CAppWnd(void);
    virtual ~CAppWnd(void);
    DECLARE_MESSAGE_MAP()
    afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
    afx_msg void OnPaint();
};
...


//AppWnd.cpp
...
int CAppWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
        return -1;
    
    CApp::OutConsole("Creating CApWnd: not yet subclassed\n");

    _pS1 = new CSub1;
    _pS1->Subclass(*this);
    _pS2 = new CSub2;
    _pS2->Subclass(*this);
    _pHookIdle1.New();

    return 0;
}
...

Using the code

I didn't specialize the smart pointers for these objects, thus, they assume by default that "dynamic_cast" is possible. For this reason, you must enable RTTI on your project.

If you don't want (or you cannot), typedef the smart pointers to your derived classes specifying the second template parameter to be GE_::Safe::FStaticCast (the defaut is FDynamicCast) and remove all the references to the debug class GE_::Mfc::STrace. (it's just for debugging). I - however - suggest to let RTTI enabled.

Sample code

The provided sample shows how a CFrameWnd (it does nothing but painting a piece of static text) is subclassed twice, using two objects derived from CWndSubclasser. Also, an idle processor and a hook are instantiated. You can easily redo the same things any number of time. In the subclassers, trapped messages display rows of text in an associated console.

Also, the “About” dialog is subclassed with another instance of the same object used to subclass the mainframe.

Note the way it is colored and the behavior of the cursor, and note how the code that does that has been written only once onto a specific object. (I didn’t recolor the static text control and handle the cursor shape over the button just to make the difference evident)

Description

The key point to understand is in the CWndSubclasser Subclasss function and the Destroy function.

bool CWndSubclasser::Subclass(HWND hWnd)
{
    if(!hWnd || _hWnd) return false;
    _hWnd = hWnd;
    _bCleared = false;
    TLstpWndSubClasser& lst = g_map[hWnd];
    lst.push_front(this);

    WNDPROC& oldPrc = g_prcmap[hWnd];
    if(!oldPrc) //not yet subclassed
    {
        oldPrc = (WNDPROC) GetWindowLongPtr(hWnd, GWL_WNDPROC);
        SetWindowLongPtr(hWnd, GWL_WNDPROC, (LONG_PTR)SubWindowProc);
    }
    
    return true;
}

bool CWndSubclasser::Clear()
{
    if(!_hWnd) return false;
    _bCleared = true;

    TLstpWndSubClasser& lst = g_map[_hWnd];
    lst.remove(this);
    
    if(lst.size() == 0)
    {
        g_map.erase(_hWnd);
        SetWindowLongPtr(_hWnd, GWL_WNDPROC, (LONG_PTR)g_prcmap[_hWnd]);
        g_prcmap.erase(_hWnd);
    }

    return true;
}

The Clear function it is called also in the destructor (but you can call it an unlimited number of times) and the Subclass function must be called by you on an existent window.

The g_xxx variables are std::maps defined as globals in an unnamed namespace.

In this implementation Subclass means push the subclasser object into a HWND associated dedicated list, and set the HWND window procedure to the one provided in WndSubclasser.cpp.

The window procedure is defined in these terms:

    struct 
    SWndSubclasserParams {
        TLstpWndSubClasser::iterator
        i; TLstpWndSubClasser::iterator
        end; HWND
        hWnd; UINT uMsg; WPARAM wParam; LPARAM lParam; LPVOID
        prevtoken; };
    LRESULT

    CALLBACK SubWindowProc( HWND
    hwnd, //      handle to window UINT
    uMsg, //      message identifier WPARAM
    wParam, //  first message parameter LPARAM
    lParam //   second message parameter )
    {
        TMapHwndLstSubClasser::iterator
        mapI = g_map.find(hwnd); ASSERT(mapI
        != g_map.end()); 
        static CCriticalSection ccs; 
        CSingleLock    lock(&ccs, true);
        SWndSubclasserParams prms;
        TLstpWndSubClasser& lst = mapI->second;
        prms.i = lst.begin();
        prms.end = lst.end();
        prms.hWnd = hwnd; prms.uMsg = uMsg; 
        prms.wParam = wParam; prms.lParam = lParam;
        prms.prevtoken = NULL;

        return CWndSubclasser::Call(&prms);
    }

Note: all this code is into an unnamed namespace. You will never use it directly.

Basically, a SWndSubclasserParams internal structure is allocated on the stack and filled in.

It is then passed as an address in the token parameter of the static Call function, that does the real job.

LRESULT CWndSubclasser::Call(LPVOID token)
{
    SWndSubclasserParams* pPrms = (SWndSubclasserParams*)token;
    
    if(pPrms->i == pPrms->end) 
        //finished the list: just call the original window procedure. 
        //This will go into AfxWndProc and hence in 
        //CWnd::OnWndMsg and into the message map.
        return CallWindowProc(
           g_prcmap[pPrms->hWnd], 
           pPrms->hWnd, 
           pPrms->uMsg, 
           pPrms->wParam, 
           pPrms->lParam
        );
    
    CWndSubclasser* pWS = *pPrms->i;
    PWndSubclasser pKeep(pWS);
    pPrms->i++; // the nextime Call is called, 
                   // will process the next in list
    pPrms->prevtoken = pWS->_token; //save for later
    pWS->_token = token; //remember for Defauklt calling
    LRESULT r=0;
    if(pPrms->uMsg == WM_NCDESTROY)
        pWS->_bCleared = true; //will not destroy yet
    
    if(pWS->_bCleared) r = pWS->Default(); //skip a declared 
                                                 // as "destroyed"
    else r = pWS->WndProcSub(
        pPrms->uMsg, 
        pPrms->wParam, 
        pPrms->lParam); //just call the subproc
    
    pWS->_token = pPrms->prevtoken; //restore previous token 
                                 //(now can return also from recursion)
    return r;
}

The iterator in CWndSubclasser is intended to point to the “next to process” subclasser.

If we are at the end of the list, we just call the ex window procedure.

Otherwise

  1. We retrieve the pointed object
  2. We increment the reference counting (PWndSubclasser pKeep(pWs) will live until return)
  3. We increment the iterator (for future recursions)
  4. We put the “token” into the object we’re just calling, after saving its old value.
  5. We call (apart some particular cases) the WndProcSub virtual function.

It is expected that, depending on your needs, WndProcSub calls Default that, in turn, calls Call again (but the iterator has been incremented, hence the next subclasser will be referred).

This trick allows you to define as many subclassers you want: all you need to do is override the WndProcSub and, depending on the message:

  • Call Default() and then do some processing (the subclasser acts as an add-on to the “default” action taken by previous subclasser and default window procedure)
  • Do your process and don’t call Default (you replace the functionality with yours)
  • Call Default() after your process.

Other objects

With the same identical technique, I also defined CHookIdle and CWndProcHook.

The difference is, they are general Windows hooks, and are not associated to a particular window.

CHookIdle chains in a list and a static hook procedure installed on WH_FOREGROUNDIDLE, calls the OnIdle virtual function. (It’s up to you in your override to call Default(). Note: the base, just calls Default)

CWndProcHook chains in another list, and a static hook procedure installed on WH_CALLWNDPROC calls OnHook.

CWndSubclasser and CWndProcHook

There is certain overlap in the functionality of the two objects, but they are not the same.

I think it is important to note the differences:

  • CWndSubclasser when instantiated does nothing until it is associated to a specific HWND. From then on, it responds only for the HWND it has been associated. CWndProcHook respond when Windows is about to call a window procedure. It does not refer to a particular window, but can “spy” everything.
  • CWndSubclasser relies on an “alternative” window procedure. The original one is called by Default(). It’s up to you to decide “if” and “when” (or “where”) to call it. CWndProcHook relies on a Windows hook. It can take actions, but it is not a real override of a window procedure. The window procedure is always called. Default() is only to define "if" and "when" to process different instances of CWndProcHook you may have created and hence, chained.

Other stuff

I included in the package also SmartPtr.h (they are used in the classes) and also another very simple class: GE_::Mfc::STrace. It can be used on RTTI enabled projects to do tracing into functions, tacking the recursion level.

Its usage is simple: declare a variable on the stack, and call the Trace function. The typical case is: GE_::Mfc::STrace trc; trc.Trace(typeid(*this),this,"<<yourtext>>"); and in the output window of the debugger you will see:

  • a number of dots as the number of Trace recursion in progress
  • the runtime type name of the object
  • the address of the object
  • the text you provide

It is used in debug versions of the objects I just described in this article.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Emilio Garavaglia
Architect
Italy Italy
Member
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.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 1mvpMichael Haephrati20 Feb '13 - 11:53 
This solution is outdated
GeneralRe: My vote of 1memberEmilio Garavaglia15 May '13 - 10:13 
zzzzzzzzzzzz.......

2 bugs found.
> recompile ...
65534 bugs found.
D'Oh! | :doh:


GeneralMy vote of 1memberAndy Bantly13 Nov '10 - 5:15 
Vote 1 because the author can't update the solution to modern times
GeneralRe: My vote of 1memberEmilio Garavaglia14 Nov '10 - 2:11 
"Modern time" ?
This makes me think ... but "modern time" still requires this kind of things? "Modern" developer may use .net!
Isn't it better to let all this into its historical value (for who still need to be in this kind of stuffs) and rethink it differently, may be in a different article, may be using more recent compilers, language and tools?
 
"Updating" or "Another new version" letting the old still available?

2 bugs found.
> recompile ...
65534 bugs found.
D'Oh! | :doh:


GeneralRe: My vote of 1, no, 5memberDan Bloomquist27 Dec '10 - 15:10 
I personally would like to thank you. I still program with MFC, I only use that managed and c# stuff when I have to. I'd rather produce code with Boost and that can cross away from MS.
 
Anyway, today I needed to hook the list window of a CComboBox. There is another example here that uses a single combo box global pointer. I was not comfortable with that as I have more than one comboEx at times. So I used a simple version of what you did as part of my extended combo. I only needed one hook per combo.
 
Best, Dan.
 
class HEComboBox;
struct HEComboBoxhookStruct
{
WNDPROC oldProc;
CHEComboBox* pCombo;
};
 
typedef std::map< HWND, HEComboBoxhookStruct > HEComboBoxhooks;
typedef std::map< HWND, HEComboBoxhookStruct >::iterator HEComboBoxhooksIt;
GeneralRe: My vote of 1, no, 5memberEmilio Garavaglia27 Dec '10 - 21:56 
What can I say ...
I consider this a "dead code" because relates to something that have been replaced by many new version of the things it relates on.
 
But ... It still has an average of 40 to 60 view per days, that probably means the idea behind it it still useful to people like you, that don't see any reason to move to certain "code monster" that takes hours and gigabyte to install just because they need a standard control with a bit more of "mascara".
 
About the vote of Mr. Bantley, I voted 1 for an article of him when it was still in the approving phase: He did a good work, but the article itself was not at the same level. I was simply trying -with that 1- to delay the article approval to give him the time to make his article even more interesting.
He replayed to my vote by saying (literally) "your vote has been noticed", than -in a couple of days- voted 1 a number of my already published articles with pretestual reasons.
After some days other "1" votes from the "US" where added by identity with "no profile"
This perfect "mafia style" behavior, in a context where he cannot change the history of things, makes me qualified him as a "not yet grown bully", try to adopt a "mafia style behavior" against me, forgetting I'm right now 10 years away from him, with a career that certainly does not depend on his vote, and published articles whose content had already made their history.
 
Apart that, it is certainly right that this article is not "adequate to modern time". But it is a picture of its own time.
Updating it -to me- sounds like "take a picture of my grandmother when she was young, and photoshop her dresses to make her to look like a today 16 y.o. girl"

2 bugs found.
> recompile ...
65534 bugs found.
D'Oh! | :doh:


GeneralRe: My vote of 1 ? Not only harsh but, can you tell me what "Modern Times" means for you ?membergordon8831 Dec '11 - 7:39 
What do you means by "Modern Times" ?
 
This technology haven't changed (Or I missed a very major SDK update)
Others technologies exist (WPF .Net etc too many maybe) of course but so differently minded that I am curious to see what would look like a "modern version" of this issue.
Gordon

AnswerSolution to make the files build in VS2005 and newer.memberMember 364418129 Jan '09 - 1:04 
Due to breaking changes in VS2005 compiler (read more here[^]) some changes need to be made to the source files to make it build.
 
In SmartPtr.h line 193 friend declarations need to look like this:
template <class T, class C>
friend class PtrStrong;
template <class T, class C>
friend class PtrWeak;
 
In SmartPtr.h line 218 function must include return type (int was default prior to VS2005).
virtual int Set(T* pT)=0;
 
In SmartPtr.h line 298 friend declaration need to look like this:
template <class T>
friend class _PtrSmart;
 
In SmartPtr.h line 411 and 533 function must include return type and the function must return a value.
virtual int Set(T* pT) { _Assign(pT); return 0; }
 
Hope this helps anyone having trouble building the sample files.
GeneralRe: Solution to make the files build in VS2005 and newer.memberemilio_grv29 Jan '09 - 1:30 
wow, thanks!
I wrote this article in 2003.
I never had thought that 6 years later there had still being interest in it.
This is a lesson to me as well ... may be it is the time for me to upgrade some old code...
 

2 bugs found.
> recompile ...
65534 bugs found.
D'Oh! | :doh:


Questionhow do I embed another modeless window within another?membernooboon20 Jul '07 - 6:23 
I would like to host different dialog templates in one dll and then re-use those same templates in other dlls. The alternative is to redefine the same template/code in each different dll that requires it.
 
I would like to instantiate this modeless dialog from host dll and then capture it and embed it in dll B's dialog ..
 
any ideas?
AnswerRe: how do I embed another modeless window within another?memberemilio_grv21 Jul '07 - 23:53 
When calling CreateDialog specify as HINSTANCE the one of DLL where the resource is to be loaded.
The created HWND live in the creating DLL (the one that calls CreateDialog).
 
Hope I undestood...
 

2 bugs found.
> recompile ...
65534 bugs found.
D'Oh! | :doh:

QuestionPlace a dialog in anothermembersireesha_sree27 May '07 - 23:09 
Hi all,
 
Can anyone suggest how to place a dialog in an other dialog just llike placing some control in a dialog.
 

Thanks

AnswerRe: Place a dialog in anothermemberemilio_grv28 May '07 - 4:40 
The inner dialog must be created "modeless" (don't call DialogBox, but CreateDialog) and set up with the outer as parent.
 

2 bugs found.
> recompile ...
65534 bugs found.
D'Oh! | :doh:

GeneralSubclassing IE ...memberKFC12312 Apr '07 - 8:11 
Hi there,
I wrote a simple code to get the handle of IE component. I also wrote a class to wrap the handle
 

LRESULT CALLBACK OutIEProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
 
class IEWrapper
{
private:
HWND IE;

LRESULT CALLBACK IEProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
...
}
 
For handling the message from IE, I subclass the IE to call OutIEProc which is defined outside the class. Everything is fine! However, I would like to make the subclass happend inside the class, i.e. have it invoke the member function IEProce instead of OutIEProc. I wonder if your code can do that. I try to have my IEWrapper derived from your class, but lots of errors returns!

GeneralRe: Subclassing IE ...memberemilio_grv12 Apr '07 - 21:36 
One of the major "source of errors" can be the difference in the template syntax and semantic that exist between VC6, 7.0, 7.1 and 8.
 
The C++ standard cometee did lot of work on temnplates across the years and MS followed with a certain delay.
 
Now everything seems stabilized around C++ 8 (VC express / VS2005).
Unfortunately this article refers to 7.1 (is a bit dated) and may be not suitble for certain compilers.
 
You can try to investigate certain "type errors" or mistaken identifiers by considering the use of the keyword typename inside template classes, when referring to types declared inside parametrized classes.
 
Otherwise, please be more specific about the nature of the errors.
 

2 bugs found.
> recompile ...
65534 bugs found.
D'Oh! | :doh:

GeneralHooking / Subclassing driving me nuts :confused:memberale2125 Aug '06 - 18:27 
I read your article but had a hard time understanding a few things.
I have a C++ project that hooks a specific window that is child of another. Once the DLL is mapped on the process I increase the ref count with LoadLibrary, then unhook the window and then subclass it. The problem comes when the child window is closed. I want to unload the DLL at that point but if I unsubclass and FreeLibrary in WM_NCDESTROY the process hangs. I'm going crazy trying to fix this and would appreciate any help you can give me.
 
Thank you very much!Confused | :confused:
Alejandro.
GeneralRe: Hooking / Subclassing driving me nuts :confused:memberemilio_grv26 Aug '06 - 10:48 
If the window procedure you used to subclass is inside the DLL, the process hangs because of the re-entrancy of such procedure.
WM_NCDESTROY is the last message received, but is not sent by the message loop, but -normally- by another message default behavior (for example WM_CLOSE) probably called by the default handling of the subclassing proc. (that typically calls the original proc, and waits for its return)
 
Because of that, there are other pending call of your procedure inside the stack waiting to be unrolled, but, since you free the library... you are in fact dismissing from the system some code that is still in use.
 
The correct way to do this is not discard the library while still processing message.
To achieve that you can, for example, keep a static counter in the winproc, increment when entering and decrement before exiting, like
 
LRESUT YourProc(HWND h, UMSG u, WPARAM wp. LPARAM lp)
{
    static unsigned count =1;
    LRESULT result(0);
    ++count;
    switch(u)
    {
        ...
        ...
    case WM_NCDESTROY:
        --count;
        break;
        ...
    }
    --count;
    if(!count) FreeLibrary(...)
 
    return result;
}
 
By doing this, the actual value of count is the number of nested calls plus 1. Such 1 will be removed by the decrement in the WM_NCDESTROY handler, but FreeLibrary will be delayed when count will be zeroed, and this is just before the most external recursion, when we are sure that no stacked calls of YourProc are waiting YourProc to return.

 

2 bugs found.
> recompile ...
65534 bugs found.
D'Oh! | :doh:

GeneralRe: Hooking / Subclassing driving me nuts :confused: [modified]memberale2127 Aug '06 - 16:19 
Thank you very much for the explanation, I really truly appreciate you taking time in trying to help me!.
Unfortunately that didn't solve the problem, the process still crashes :"(. I cleaned up my winproc for simplicity as follows:
LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  static unsigned int iRefCount = 1;
 
  ++iRefCount;
	
  switch (uMsg)
  {	
    case WM_NCDESTROY:
      --iRefCount;
      break;
  }
 
  --iRefCount;
  if (!iRefCount)
  {
    UnSubclass();
    FreeLibrary(m_hInstDLL);
  }
  return CallWindowProc(lpfnOldProc, hwnd, uMsg, wParam, lParam);
}
 
With or without the UnSubclass() it crashes, but I'll post the proc just in case:
BOOL UnSubclass(VOID)
{
  BOOL bRet = false;
 
  if (lpfnOldProc)
  bRet = (BOOL) SetWindowLong(m_hwndHooked, GWL_WNDPROC, reinterpret_cast<LONG>(lpfnOldProc));
 
  return bRet;
}
 
My DllMain:
BOOL APIENTRY DllMain( HMODULE hModule, DWORD fdwReason, LPVOID lpReserved )
{
  switch(fdwReason)
  {
    case DLL_PROCESS_ATTACH:
      char dllName[255];
 
      m_hInstDLL = static_cast<HINSTANCE>(hModule);
			
      if (msh_hHook) //data_seg section variable
      {
        GetModuleFileName(hModule, dllName, 255);
	LoadLibrary(dllName);
	UnhookWindowsHookEx(msh_hHook);
      }
			
      if (msh_hwndTarget) //data_seg section variable
      {
        m_hwndHooked = msh_hwndTarget;
	SubClass(m_hwndHooked);
      }
      break;
  }
  return TRUE;
}
 
I hope I'm not bothering you too much but you are the only one I found with such a great understading of how this technique works.
 
Thank you again!
Alejandro.
GeneralRe: Hooking / Subclassing driving me nuts :confused:memberemilio_grv27 Aug '06 - 22:23 
There are two main problem I can see (plus some little things to be adjusted):
 
The most important is in
  if (!iRefCount)
  {
    UnSubclass();
    FreeLibrary(m_hInstDLL);
  }
  return CallWindowProc(lpfnOldProc, hwnd, uMsg, wParam, lParam);
Now: after FreeLibrary, the library doesn't exist anymore, how can you execute the CallWindowProc ??
 
The second is in DllMain: it seem you call LoadLibrary ... in the library itself...
 
Load / FreeLibrary are not for this: when you run DllMain the library is already loaded.
Who loads it? May be:
 
- the library is statically binded by a project dependency: if so, the import library (the "lib" with the same name of the DLL) loads the DLL at program startup end unload it at program end. No need of explicit Load / Free are required.
 
- if no explicit dependency exist, Load / Free should be called by the "exe" or "dll" that requires your DLL.
 
Self-loading is required only if you want your DLL to "survive" to the "exe" that loads it.
 
In the second case (explicit load from a "parent" exe) the best way is to "post" a message from the DLL to a window whose procedure is not in the DLL itself. (Note: Post, not Send), so that you can discard the DLL after the program execution exited from it.
 
If this is not clear, I'll probably need an article, but I've not -at the moment- the time for it.
 
Note: since we're not discussing on this article, consider to move your request to the C++ forum: there could be many other people reading on it
 

2 bugs found.
> recompile ...
65534 bugs found.
D'Oh! | :doh:

GeneralRe: Hooking / Subclassing driving me nuts :confused:memberale2128 Aug '06 - 4:46 
Emilio:
Thank you once again for replying!.
 
The reason why I call LoadLibrary inside de DLLMain is because the DLL is injected into X process by the means of a hook. I have no special interest in the hook itself, I only use it to load the DLL into the other process space, so I increment the DLL's ref counter and unhook the hook that loaded the dll. My one and only target is cross process subclassing.
 
By the way, I don't see what is wrong with the part of my code you wrote about. That's inside the subclassed WinProc. If I don't call the previous (original) winproc forwarding the messages I think that would be pretty bad :S.
 
And yes, you are right, I should post on the forum :X. I dind't think it would become a ping-pong of replies :P.
 
Thank you once again for your kindness, Emilio!.
Cheers.
GeneralVery well explained.memberauds16 Aug '06 - 4:52 
I found the explanation and destructor stuff very usefull.
 
Thanks for this gr8 work.
GeneralRequires VC7 or latermemberNeville Franks17 May '06 - 18:30 
Just a note to anyone interested in this, that it requires VC7 or later for the smart ptr code.
 
Neville Franks, Author of Surfulater www.surfulater.com "Save what you Surf" and ED for Windows www.getsoft.com
 

QuestionJust because STL?memberinternal10 May '04 - 16:55 
I'm fighting the subclasswindow assertion failed now. The reason is cwnd can only be subclassed once. So I'm looking for solutions here. I've got three other articles similar to this.
http://www.microsoft.com/msj/0397/mfcp2/mfcp2.aspx
http://www.codeproject.com/miscctrl/subclasswnd.asp
http://www.codeproject.com/cpp/chookwnd.asp
I've not tried your code but it looks good. So I'm wondering the low rating maybe just because stl.D'Oh! | :doh:
 
Sing when we're programming.
AnswerRe: Just because STL?memberemilio_g10 May '04 - 20:20 
Not sure.
I've written other articlels like this, having other rates.
 
I think it is probably because this article had been read by many MFC programmers that finds STL an overhead (MFC has its own collections, and mixing STL and MFC increase the code: MFC cannot do wothout its collectios, because it used it internally) and that expect some sort of "message mapping" I didn't provide in these classes. (There's just a virtual function to override: no stuff to help in message dispatching).
 
I didn't do that, because with MFC it's not so easy to handle message map outside CWnd. They're based on internal data structure and the massage map-processing does some pointer-to-member caching, sometime with unpredictable results if you try to dynamically modify the map data structure.
 
However, if you are interessed in this kind o things, give a look at here[^]
 
Part 3 is near to come. But you can see there how windows can be handled even without MFC or ATL at all.
 
cheers.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130516.1 | Last Updated 10 Oct 2003
Article Copyright 2003 by Emilio Garavaglia
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid