Get base class of pointer via __super or equivalent!

Ah hello.

I have a helper class, CMyHelper, whose job is to manage certain messages of a given window.  I do this by replacing the original window procedure of the given window with one defined in my helper class (see below).  I set a property in the HWND of this given window to be a pointer to the CMyHelper class, so I can access it in my custom WNDPROC:

// .h
class CMyHelper
{
public:
      // Handler for the WM_NCPAINT message
      virtual LRESULT OnNcPaintHandler ( HRGN hUpdateRgn );
      // ...
protected:
      // Window whose WNDPROC we are replacing
      CWnd* m_pWnd;
      // Replacement WNDPROC
      static LRESULT CALLBACK CustomWndProc ( HWND hwnd, UINT msg, WPARAM wParam, LPARAM );      
}

//.cpp
/*static*/ LRESULT CALLBACK CMyHelper::CustomWndProc ( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
      CMyHelper* pHelper = ( CMyHelper*) GetProp ( hwnd, _T("HELPER") );
      switch ( msg )
      {
            // ...
      case WM_NCPAINT:
            {
                  // Call handler for WM_NCPAINT
                  pHelper->OnNcPaintHandler ( ( HRGN ) wParam )
            }
      }
      // ...
}

/* virtual */ LRESULT CMyHelper::OnNcPaintHandler ( HRGN hUpdateRgn )
{
      // Call base class handler for WM_NCPAINT ************* PROBLEM
      // Handle WM_NCPAINT message in our special way...
      return 0;
}

I have indicated the problem above.  I need to call the base class handler for WM_NCPAINT.  By "base class handler", I don't mean "original window procedure", I literally mean "base class".  Say that the m_pWnd member in our class is a CMyWnd, derived from CWnd: I want to call CWnd::OnNcPaint, not the original window procedure which would be CMyWnd::OnNcPaint.

Put more simply, I want to do the following, using Microsoft's magic __super keyword:

m_pWnd->__super::OnNCPaint().

The compiler complains though, stating that CMyHelper does not have any base classes.  Well, yeah, but that is not what I am trying to do...

Even

m_pWnd::__super::OnNCPaint()

does now work.

How can I achieve what I am after?

TIA
LVL 19
mrwad99Asked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Infinity08Commented:
How about :

    m_pWnd->CWnd::OnNCPaint()

?
0
mrwad99Author Commented:
Hi Infinity, thanks for the comment.

Hmm...

No, because m_pWnd might be a CMyDialog, derived from CDialog.  In that case, I would want to call CDialog::OnNcPaint().
0
mrwad99Author Commented:
(FYI, CDialog is derived from CWnd) :)
0
Fundamentals of JavaScript

Learn the fundamentals of the popular programming language JavaScript so that you can explore the realm of web development.

Infinity08Commented:
I assume that MS's __super is replaced by the base class of the current object. So, since you use __super in the CMyHelper class, it will look for the base class of the CMyHelper class, not of the CMyWnd class.

        http://msdn.microsoft.com/en-us/library/94dw1w7x(VS.80).aspx
0
mrwad99Author Commented:
Makes sense.  So if I can't use __super, is there any way I can do this?
0
Infinity08Commented:
You can always add a typedef like this to your derived class :
class CMyWnd : public CWnd {
  typedef CWnd super;
 
  // ...
};
 
// and then :
 
m_pWnd->super::OnNCPaint()

Open in new window

0
mrwad99Author Commented:
Hmm.  Could do, but that might imply changing a lot of existing code.

There must be a simple way of doing this...
0
Infinity08Commented:
Or something like this :
class CMyWnd : public CWnd {
  public :
    CWnd *super() { return this; }
 
  // ...
};
 
// and then :
 
m_pWnd->super()->OnNCPaint()

Open in new window

0
Infinity08Commented:
>> There must be a simple way of doing this...

Not really. C+ doesn't have a super keyword (or another keyword that does the same thing), because it causes problems with multiple inheritance. So, when you want such functionality, you need to provide it yourself.


You can always automate it using templates or something ... But adding code is pretty much inevitable.


Maybe someone else has a better idea :)
0
Infinity08Commented:
I meant to say C++, not C+ of course heh
0
AndyAinscowFreelance programmer / ConsultantCommented:
Maybe I misunderstand what you want.
How about passing a custom message to the customised window class and let the handler of that do your NCPaint stuff ?  (Or do you not have control of the classes you are doing this for?)
0
ZoppoCommented:
Maybe you can solve this via a class template. You could i.e. make CMyHelper abstract by adding a virtual function which is called by your OnNcPaintHandler, i.e.


class CMyHelperBase
{
 ...
protected:
 virtual LRESULT InternalNcPaintHandler( HRGN hUpdateRgn ) = 0;
 ...
};

/* virtual */ LRESULT CMyHelperBase::OnNcPaintHandler ( HRGN hUpdateRgn )
{
      // Call base class handler for WM_NCPAINT ************* PROBLEM
      // Handle WM_NCPAINT message in our special way...
      return InternalNcPaintHandler( HRGN hUpdateRgn );
}

template <class _BASE>
class CMyHelper : public CMyHelperBase
{
...
 protected:
  virtual LRESULT InternalNcPaintHandler( HRGN hUpdateRgn )
  {
   return _BASE::OnNCPaint( hUpdateRgn );
  }
...
};

Then the only difference would be that you need to change creation of the CMyHelper-objects.

BTW: If you want to use 'CWnd::OnNcPaint' this won't work either since it is declared as 'protected' in CWnd.


ZOPPO
0
mrwad99Author Commented:
ZOPPO

Thank you.  That is one step closer; at least now I can determine the base class type.

>> BTW: If you want to use 'CWnd::OnNcPaint' this won't work either since it is declared as 'protected' in CWnd.

Um.  Arr.  Hmm...

I hadn't realised that.  Grrr....

OK, this is getting silly now.  Is there simpy no way I can get the message handled by the base class message map?
0
ZoppoCommented:
Well, I tested a hack which at least made it compiling - but I don't know if it does what you need.

If in addition you derive CMyHelper from CWnd you can access the protected functions, i.e.

template <class _BASE>
class CMyHelper : public CMyHelperBase, public CWnd
{
 ...
};

But, as told, I don't know if this may create side-effects ...

ZOPPO
0
mrwad99Author Commented:
Thank you ZOPPO.  That is an excellent suggestion: deriving from CWnd.  I had previously overlooked it because, well, CMyHelper is not a window :)  But, the good thing is I can access the base class handlers in the manner you described :)

As a related question (see http:Q_24438665.html) do you think you could participate there?

I will close this shortly when I am sure nobody has any comments left.

Thanks again :)
0
Infinity08Commented:
>> OK, this is getting silly now.  Is there simpy no way I can get the message handled by the base class message map?

Well, if the method is protected as Zoppo said, then you'll have to call it from within the Derived class (CmyWnd). So, quite basically, something like below should work.

Note that I'm looking for the simplest solution here. "Better" solutions would require a re-design of your application by properly making use of virtual functions to do what you want (And I'm not sure the CWnd class allows that).
class CMyWnd : public CWnd {
  public :
    void superOnNCPaint() { CWnd::OnNCPaint(); }
 
  // ...
};
 
// and then :
 
m_pWnd->superOnNCPaint()

Open in new window

0
Infinity08Commented:
>> That is an excellent suggestion: deriving from CWnd.

To be honest (and I think Zoppo will agree), this isn't really a nice solution ... It complicates the design and creates some quite strange relationships between the classes. I wouldn't recommend it.


May I ask why you need to call the OnNCPaint on the base class from CMyHelper anyway ? Are you sure you need to do that ? I'd say that's the first sign of a design problem.
0
itsmeandnobodyelseCommented:
>>>> I want to call CWnd::OnNcPaint, not the original window procedure which would be CMyWnd::OnNcPaint.

Why you didn't have like

void CMyWnd::OnNcPaint(bool fromHelper)
{
    if (fromHelper)
        CWnd::OnNcPaint();
    else
        CMyWnd::OnNcPaint();
}


MFC does that you want by defining macros (what probably could be exchanged by templates in a pure C++ environment).

  BEGIN_MESSAGE_MAP(MyClass, MyBaseClass)

       ON_MESSAGE(WM_MY_MSG, onHandlingMyMessage)
       ...

  END_MESSAGE_MAP

Here the BEGIN_MESSAGE_MAP gets the class name and base class name (note, it not necessarily must be the direct base class). That way it could first (or last in END_MESSAGE_MAP) call the base class function handling a message if not handled in the derived class.
0
mrwad99Author Commented:
Thanks for that itsmeandnobodyelse

>> Why you didn't have like...

That would imply changing my existing code.  I simply want to have a class that can be made use of immediatey, without having to go back and change existing code to work around it.  

BTW, I have asked a related question regarding message maps, if you can participate there please do.
0
evilrixSenior Software Engineer (Avast)Commented:
What about this... is this the kind of thing you're trying to do?
#include <iostream>
 
struct CWnd
{
	virtual ~ CWnd(){}
 
	virtual void func() 
	{
		std::cout << "CWnd" << std::endl;
	}
};
 
struct CDialog : CWnd
{
	void func()
	{ 
		std::cout << "CDialog" << std::endl; 
	}
};
 
struct CMyWnd : CWnd
{
	void func()
	{
		std::cout << "MyCWnd" << std::endl;
	}
};
 
struct CMyDialog : CDialog
{
	void func()
	{
		std::cout << "CMyDialog" << std::endl;
	}
};
 
struct Helper
{
	Helper (CWnd * p) : p(p){}
 
	void func()
	{
		if(CMyDialog * p_ = dynamic_cast<CMyDialog *>(p))
		{
			p_->CDialog::func();
		}
		else
		if(CMyWnd * p_ = dynamic_cast<CMyWnd *>(p))
		{
			p_->CWnd::func();
		}
		else
		{
			// Doesn't have a known base class
			p->func();
		}
	}
 
	CWnd * p;
};
 
int main()
{
	CMyWnd myWnd;
	CMyDialog myDialog;
 
	Helper helperWnd(&myWnd);
	Helper helperDialog(&myDialog);
 
	helperWnd.func();
	helperDialog.func();
}

Open in new window

0
itsmeandnobodyelseCommented:
>>>> if(CMyDialog * p_ = dynamic_cast<CMyDialog *>(p))
You can check the type of baseclass by using the RUNTIME_CLASS macro. I made bad experiences using dynamic_cast on MFC classes. It throws an exception rather than returning NULL, so I needed a try catch block (it was VC7 and maybe the issue is solved for VC8 and VC9).

I would prefer using a template class helper as ZOPPO has described rather than asking for each possible baseclass.
0
evilrixSenior Software Engineer (Avast)Commented:
>> I made bad experiences using dynamic_cast on MFC classes. It throws an exception rather than returning NULL
Only the reference cast should throw, are you saying the pointer cast throws too? If so this is a compiler bug, since the standard states the behaviour is to return a NULL.

>> I would prefer using a template class helper as ZOPPO has described
Zoppo's solution will definitely be faster since dynamic casting has a performance overhead, however, since mrwad99 is looking for a solution with the least impact on existing code I thought this was worth consideration.
0
mrwad99Author Commented:
Hi RX!  LTNS!

Nice suggestion; that is kind of what I am after, except that the else-if /dynamic_cast combination is

a) ugly (sorry, nothing personal :) )

b) Going to be incredibly expensive if I have to do that for every single windows message that I receive

I could work around b) by obtaining a base class pointer just *once* (like in the constructor), but even so, when ZOPPO's comment about the member functions not being accessible unless I am derived from the containing class (I hope that makes sense: I cannot access CWnd::OnNcPaint() for example unless I am derived from CWnd as it is declared protected)

I think that the suggestion by ZOPPO is the way to go personally.

Closing this very soon...
0
evilrixSenior Software Engineer (Avast)Commented:
a) I know :)

b) I know :)

>> I think that the suggestion by ZOPPO is the way to go personally.
I agree, but I was hoping to provide you a non-intrusive solution as you said it was one of your requirements.

Cheers.
0
mrwad99Author Commented:
>> I agree, but I was hoping to provide you a non-intrusive solution...

I think that having the user specify the base of the window class they want to skin when constructing a CMyHelper is not instrusive at all.  Given, it is less than perfect, but what is perfect in the software world ?  :o)
0
AndyAinscowFreelance programmer / ConsultantCommented:
A 'minor' point - This is almost starting to sound like oops I've made a design fault, where is the sticking plaster?
0
mrwad99Author Commented:
>> ...oops I've made a design fault, where is the sticking plaster?

Andy!  Stealthily monitoring this question I see ;)

Maybe, yes.  I did not forsee this many complications.  All I know is that this method is far easier and more OO than duplicating the code to handle certain messages specially in every window class.  (I have already gone over the inability to have a base "CWnd" derived class that contains all this functionality, so that is not an option.)
0
Infinity08Commented:
>> A 'minor' point - This is almost starting to sound like oops I've made a design fault, where is the sticking plaster?

I wouldn't say that's a minor point. I've tried to make it earlier too. But apparently it was lost in the noise ... If you make a diagram of the class dependencies and relationships with Zoppo's solution, you'll soon see (I hope) that this quickly gets unmanageable.

You might have to consider a re-design today, or go for a less intrusive "quick patch" (like http:#24474959 or http:#24475041).
0
AndyAinscowFreelance programmer / ConsultantCommented:
>>Andy!  Stealthily monitoring this question I see ;)

Actually I did make a comment earlier ;-)
0
mrwad99Author Commented:
Andy:

>> Maybe I misunderstand what you want.
>> Actually I did make a comment earlier ;-)

Sorry, I did not see that comment.

Infinity:

>> To be honest (and I think Zoppo will agree), this isn't really a nice solution ...

Again, I did not see that comment either.  Sorry.


In follow up to the questions:

I have an app, that can contain many windows: be it the mainframe, dialog or message box (well, dialog again).  I want all of these windows to paint themselves in a special way.  Say, I want them all to have a red title bar, or whatever (please, nobody talk about the Windows ThemeAPI at this point, I have been there and it is not good enough :) )

I could have a "CRedTitleBarDialog" and a "CRedTitleBarSDIFrame" and a "CRedTitleBarMDIFrame" etc.  But that would mean changing a lot of existing code to derive from this new class instead of what they currently derive from (CDialog, CMainframe or CMDIMainFrame, or whatever).  Plus, if I suddenly find I have to change the code for drawing the window in some way, I have to change it in three, or however many base classes, I have.

Not good for maintenance.

So I came up with the idea of a CMyHelper class, that contains all the special code for drawing my red title bar *in one place*.  I can hook up the window to use the custom WNDPROC that does this as mentioned using SetWindowLong.  But then I came across the issue of needing to call the original (base class) handler, hence this question.

If I had CRedTitleBarDialog, I would have

/* afx_msg */ void CRedTitleBarDialog::OnNCPaint()
{
      // Let windows paint the title bar...
      CDialog::OnNCPaint().

      // Then do my special red painting here.
}

Note how I call the CDialog implementation.  Given, in reality I might not need to do this, but I would do if I only want to paint certain parts of the NC area red, and let windows paint the rest.

In CRedTitleBarDialog, I know that it derives from CDialog; even if I didn't, I could still replace

CDialog::OnNcPaint()

with

__super::OnNcPaint().

But in my CMyHelper class, all I have is a generic CWnd: I don't know if it is a dialog, a CMDIFrameWnd or even just a plain CWnd.  Therefore, I don't know which base class version of OnNcPaint() to call.  See the problem?

That is why passing the base class in as a template argument might do the trick.  However, as stated by ZOPPO, even if I did this, I could not access CWnd::OnNCPaint(), as it is protected.

Hence the need to derive from CWnd.

Yes, it gets messy at that point.  I Could "protected" derive, therefore prevent users of CMyHelper calling CWnd functions, which would clarify it a bit, but still it is not ideal.

Therefore, I don't feel it is a design flaw, more of a necessity.

With this clarification, has any light been shed on the problem?

Thanks.
0
Infinity08Commented:
Just one question : is this red titlebar (or whatever) something that is decided at compile time, or does it have to be modifiable at runtime ?
0
ZoppoCommented:
Hm - I don't think I understand why you need to call the handler for the base class. Couldn't you simply let the original WNDPROC of the window in question handle the message and afterward do your own handling?
0
mrwad99Author Commented:
The information on colour is red at runtime, say, from a .ini file
0
mrwad99Author Commented:
>> Hm - I don't think I understand why you need to call the handler for the base class

Yes, I am thinking that too.

If I have CMyDialog, derived from CDialog.  Do I need to call CDialog::OnNcPaint()...

Hmm...

I suppose it depends what CMyDialog::OnNcpaint() is doing.  If it is doing nothing except calling the base class CDialog::OnNcPaint(), then I don't need to call it.

However, can I *guarantee* that CMyDialog::OnNCPaint() is doing this?  What if it is trying to do special painting that I am trying to override with CMyHelper?  It might not call CDialog::OnNcPaint().  
The result from this would be my painting done in CMyHelper overridden with the paining done by CMyDialog.

...
0
Infinity08Commented:
>> The information on colour is red at runtime, say, from a .ini file

But it's a one-time choice, right - ie. at start-up of the application.
0
mrwad99Author Commented:
At this stage, yes.  It might need to be changed at runtime though (like Winamp; we can change the skin on that at runtime) at some point, but not right now.

Have you had a revelation?
0
ZoppoCommented:
You could try to retrieve the WNDPROC for the base-class via it's window class. AFAIK the window class keeps the WNDPROC which is used for all windows of that class when they're not subclassed. So, maybe you can pass the WM_NCPAINT message to the classes WNDPROC to achieve what you want.

ZOPPO
0
mrwad99Author Commented:
>> You could try to retrieve the WNDPROC for the base...

Sounds good.  What API function is there to do that though?  Is there one?
0
ZoppoCommented:
You can use GetClassName to retrieve the window class name of a window, then with this name you can use GetClassInfo to retrieve the WNDCLASS which holds the data of the window's class.

ZOPPO
0
evilrixSenior Software Engineer (Avast)Commented:
This is getting far too Windozey for my liking and since you guys have it well in hand, I'm bailing. Good luck mrwad99 and all. I might pop back later just to see what solution you come up with in the end :)
0
Infinity08Commented:
The code below illustrates the idea I was playing with.

(a) it has a minimal one-time impact on the existing objects (ie. add another base class, and modify the OnNCPaint method to call the themed version)

(b) any later theme related changes are done to the Theme and Themed objects only.

(c) it's easy to use (as illustrated in main), and allows setting and changing the theme whenever necessary. When no theme is specified, it simply uses the default ...
#include <iostream>
 
typedef enum {
  C_RED = 0,
  C_GREEN,
  C_BLUE
} Colour;
 
class Theme {
  private :
    Colour _titleBarColour;
  public :
    Theme(const Colour &colour) : _titleBarColour(colour) { }
    friend std::ostream &operator<<(std::ostream &os, const Theme &theme);
};
 
std::ostream &operator<<(std::ostream &os, const Theme &theme) {
  switch (theme._titleBarColour) {
    case C_RED   : os << "red";     break;
    case C_GREEN : os << "green";   break;
    case C_BLUE  : os << "blue";    break;
    default      : os << "default"; break;
  }
  os << " title bar";
  return os;
}
 
 
class Themed {
  private :
    static const Theme *_theme;
  public :
    static void setTheme(const Theme &theme) { _theme = &theme; }
    void OnNCPaint() const {
      std::cout << "Themed::OnNCPaint()";
      if (_theme) {
        std::cout << " - using Theme : " << *_theme;
      }
      std::cout << std::endl;
    }
};
 
const Theme *Themed::_theme = 0;
 
class CWnd {
  protected :
    void OnNCPaint() { std::cout << "CWnd::OnNCPaint()" << std::endl; }
};
 
class CDialog : public CWnd {
  protected :
    void OnNCPaint() { std::cout << "CDialog::OnNCPaint()" << std::endl; }
};
 
class MyCWnd : public CWnd, public Themed {
  public :
    void OnNCPaint() {
      std::cout << "MyCWnd::OnNCPaint()" << std::endl;
      CWnd::OnNCPaint();
      Themed::OnNCPaint();
    }
};
 
class MyCDialog : public CDialog, public Themed {
  public :
    void OnNCPaint() {
      std::cout << "MyCDialog::OnNCPaint()" << std::endl;
      CDialog::OnNCPaint();
      Themed::OnNCPaint();
    }
};
 
 
int main() {
  Theme themeRed = Theme(C_RED);
  Themed::setTheme(themeRed);
  
  MyCWnd wnd;
  MyCDialog dialog;
  
  wnd.OnNCPaint();
  std::cout << std::endl;
  dialog.OnNCPaint();
 
  std::cout << std::endl;
  std::cout << std::endl;
 
  Theme themeBlue = Theme(C_BLUE);
  Themed::setTheme(themeBlue);
 
  wnd.OnNCPaint();
  std::cout << std::endl;
  dialog.OnNCPaint();
  
  return 0;
}

Open in new window

0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
ZoppoCommented:
Sorry, Infinity08, but this is not possible since the mentioned classes CWnd and CDialog are already existing (MFC-) classes, so it's not possible to override functionality directly for these classes ...
0
Infinity08Commented:
>> so it's not possible to override functionality directly for these classes ...

What functionality am I overriding ?
0
Infinity08Commented:
>> >> so it's not possible to override functionality directly for these classes ...
>> 
>> What functionality am I overriding ?

I simply provided skeletons for those classes to make it compile for me. Is my understanding of these classes incorrect ? Did I miss something ?
0
ZoppoCommented:
ok, sorry - just saw you declared a class CWnd which isn't possible in MFC - didn't think about that you just implemented them for this reason ...
0
Infinity08Commented:
I don't have MFC - I'm not even on Windows here ;) So, I have to emulate those classes heh.
0
mrwad99Author Commented:
Infinity: thank you, that is nice.

The only problem I can see with it is that existing code would need to be changed to a) derive from Themed and b) update the message handlers to call the appropriate Themed:: version.  (It is not just OnNcPaint I need to override, it is about a dozen functions in total, but I didn't want to complicate things here.)  Either way, this solution is the closest to solving my problem I feel, so will accept this as "the" answer, splitting points amongst the other comments.

Overall, thanks a lot all for the help given here :o)
0
Infinity08Commented:
>> (It is not just OnNcPaint I need to override, it is about a dozen functions in total, but I didn't want to complicate things here.)

I see.


>> Either way, this solution is the closest to solving my problem I feel

You could give the others some more time to come up with a better solution ... Together we're stronger, and can get you the perfect solution ;)
0
mrwad99Author Commented:
>> You could give the others some more time to come up with a better solution

OK, if anyone else can see any other way of doing this, please post: I have no problem giving more points away.
0
itsmeandnobodyelseCommented:
>>>> If so this is a compiler bug, since the standard states the behaviour is to return a NULL.
Yes, that is my opinion as well. When it firstly occured to me, it was really hard to believe cause even a C cast in VC6 returned NULL if casted to a non-baseclass. I assume it went wrong because of our special environment using many many dlls with exported classes but nevertheless it was frustrating.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Microsoft Development

From novice to tech pro — start learning today.