?
Solved

Can WndProc come from within a Class?

Posted on 2003-03-21
5
Medium Priority
?
1,740 Views
Last Modified: 2009-07-29
Alright I'm trying to have a WndProc that exists within a window type class and I've not been able to get it to work.

Without Explict Type Casting I get this error.
"error C2440: '=' : cannot convert from 'long (__stdcall glwindow::*)(struct HWND__ *,unsigned int,unsigned int,long)' to 'long (__stdcall *)(struct HWND__ *,unsigned int,unsigned int,long)'"
code looks like this there "wc.lpfnWndProc          = WndProc;"

With Explicit Type Casting
"error C2440: 'type cast' : cannot convert from '' to 'long (__stdcall *)(struct HWND__ *,unsigned int,unsigned int,long)'
        None of the functions with this name in scope match the target type"
Code is now "wc.lpfnWndProc          = (WNDPROC)WndProc;"

Function is defined within the class as "LRESULT     CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);     //WndProc Function" and I've tried making it private and public.

Thanks!
0
Comment
Question by:Cryect
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 3
5 Comments
 
LVL 22

Expert Comment

by:nietod
ID: 8182076
The window procedure must be a _STATIC_ member function of the class.   I have a prepared explanation of this

continues
0
 
LVL 22

Accepted Solution

by:
nietod earned 200 total points
ID: 8182086
The problem is that a non-static member function must have a different function signature than a non-member  or static member function.  This is because a non-static member function has an extra, hidden parameter passed to it, the pointer to the object that it is invoked for.  Because of this extra parameter, a non-static member funciton can never be of the same types as a static member fucntion or non-member function, thus you cannot pass a pointer to a non-staitc member function to a procedure expecting an ordianry function pointer.  

For a window procedure Windows expects a pointer to an ordinary function, but you are passing a pointer to a non-static member function, and thus it has the wrong type.

The solution is to use an interface procedure that does have the right signature, then have this interface procedure call the non-static member


To make this work you need to write an interface procedure that is either a non-static member function or a non-member function, this will be used as the window procedure whose pointer is passed to RegisterClass.  However, this procedure will just be a short interface procedure, not a full window procedure.  this procedure then needs to obtains an object, the window object associated with the window.  Once it has the object (or pointer to it) it can then call the object's window procedure.

How how does it get the window object?   Well if you have only one such window object, then it could be stored as a global variable and you could obtain it directly.  However, most likely you have many such objects, so a single global will not work.  In that case, you can store a pointer to the object in the window's extra data.   However, there is one risk with this.  You cannot set the pointer to the object in the window, before the window procedure is first called.  This means that messages might be processed before this pointer is set.   To get around this, I use a 2nd window procedure.  One that is just used to initlaize the window object.   This window procedure just looks at the window message and looks for WM_NCCREATE.  If the message is not NCCREATE, it passes it to the defautl window procedure.   Otherwise if it is WM_NCCREATE, it obtians a pointer to the object (passed from CreateWindow()) and then sets the pointer stored in the window data.  Then it alters the window to use the regular interface window procedure (SetWindowLong()).  The regular window procedure cna then rely on the fact that the window object pointer will be set in the window extra data.  Note that the WM_NCCREATE is usually the first message sent, but might be the 2nd message sent, (after some size related message).

I hope that helps you.  Let me know if you have any questions.
0
 
LVL 12

Expert Comment

by:Salte
ID: 8182093
try declaring your wndproc class to be static.

class X {
private:

   long __stdcall WndProc(HWND hwnd, unsigned int, unsigned int, long);

   static X * wnd;

   static __stdcall long WndProc2(HWND hwnd, unsigned int, unsigned int, long);

public:
   void init();
};

The problem is that WndProc isn't of type

long (__stdcall *)(HWND,...);

WndProc is a non-static member function and as such it has a this pointer and the type is:

long (__stdcall X::*)(HWND, ...);

Such a type cannot be cast to the previous type in any reasonable way.

Another problem is that WndProc doesn't have a this pointer. The function wasn't defined in the days when object oriented programming and C++ was very well known among the microsoft programmers and in their infinite lack of wisdom they never defined an extra 'void * userdata' pointer argument to the function.

This means that if you have such a class, only one instance of that class can actually receive messages. This means that if you have 10 windows the WndProc function can only be asscociated with one of them and the other 9 won't have a WndProc function.

so, what you can do is select which object is going to be the lucky winner (if you need one) and initialize the static member named 'wnd' above with a pointer to that object.

doing something like this will do the trick:

void X::init()
{
   wnd = this;
   ...initialize the WndProc thing...
   wc.lpfnWndProc          = WndProc2;

   Note that I use WndProc2 here, this is the function that windows actually will know about. This is a function that doesn't know about any particular X object.

   ...Finish the initalization....
}

Since It is only WndProc2 that is called by windows you can remove the __stdcall from WndProc, I only included it at first so that you won't argue about the missing __stdcall before I got to say what I just said :-)

The static WndProc2 is easily defined. If you need a particular windows object to do the WndProc thing you can easily use the static member wnd and from WndProc2 do:

long __stdcall X::WndProc2(HWND hWnd, ...)
{ return wnd -> WndProc(hWnd, ...); }

And then you put the meat of the job into WndProc.

However, from the above discussion you might want to say "But I wanted each of the windows to have their own separate WndProc function"

in That case you can't do it as I did above, then you must do it in a simlar way as MFC does it. In this case you let WndProc be the static function and you do something like this:

class Wnd {
private:
    HWND M_wnd; // need the HWND for the window.

    .....other members....

    static std::map<HWND,Wnd *> all_windows;

    static long S_WndProc(HWND hWnd, ....);

    virtual long WndProc(unsigned int, unsigned int, long) = 0;

public:
    Wnd(HWND wnd);
    virtual ~Wnd();

    static void init();

    ....
};

step 1. have an init function and let it put S_WndProc as your WndProc function.

long Wnd::S_WndProc(HWND hWnd, unsigned int m, unsigned int wparam, long lparam)
{
   std::map<HWND,Wnd *>::iterator p = all_windows.find(hWnd);

   if (p == all_windows.end()) {
      // Error!!! No Wnd * object for this HWND value!
   } else {
      return (*p) -> WndProc(m,wparam,lparam);
   }
}

WndProc is even virtual in this case so any class can override it and it is per class. Since it is per class there's no reason to provide the HWND argument and it is not included.

The constructor is something like:

Wnd::Wnd(HWND hWnd)
   : M_wnd(hWnd)
{
   all_windows[M_wnd] = this;
}

And the destructor is something like:

Wnd::~Wnd()
{
   all_windows.erase(M_wnd);
}

Of course, the MFC doesn't use std::map but it uses a CMap or some such which provide similar functionality.

Hope this is of help.

Alf
0
 
LVL 22

Expert Comment

by:nietod
ID: 8182099
I was just re-reading that and there is a type on it.  I have

To make this work you need to write an interface procedure that is either a non-static member function or a non-member function,

That "non-static" should be "statuc, that is

To make this work you need to write an interface procedure that is either a static member function or a non-member function,
0
 

Author Comment

by:Cryect
ID: 8182227
That fixed it and good explaination for why.
0

Featured Post

Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

When writing generic code, using template meta-programming techniques, it is sometimes useful to know if a type is convertible to another type. A good example of when this might be is if you are writing diagnostic instrumentation for code to generat…
Written by John Humphreys C++ Threading and the POSIX Library This article will cover the basic information that you need to know in order to make use of the POSIX threading library available for C and C++ on UNIX and most Linux systems.   [s…
The viewer will learn additional member functions of the vector class. Specifically, the capacity and swap member functions will be introduced.
The viewer will learn how to clear a vector as well as how to detect empty vectors in C++.
Suggested Courses

752 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question