Sign in to follow this  
psae0001

WNDPROC in a class... no idea

Recommended Posts

psae0001    100
Ok, I've trying to figure out how to put the CALLBACK WndProc function inside class--without assign "static" infront it. However, I can never do it when I try to get the address for WNDCLASSEX / WNDCLASS. I guess probably many of you have tried, so I'm looking for the solution. --------------- Just a simple codes here ------------------ #include <windows.h> #include <stdio.h> #include <stdlib.h> class Test1 { public: void testing (); LRESULT CALLBACK wndproc (HWND hwnd, UINT msg, WPARAM wp, LPARAM lp); } int main () { WNDPROC temp1 = Test1::wndproc; // <--- for sure error int i = Test1::wndproc; // NEVER works fprintf (stderr, "%d\n", Test1::wndproc); // Surpringly this line has no error and function correctly!!! ------------------------ end of simple ------------------------- Ok, if I put a static infront of the LRESULT, the code "WNDPROC temp1 = Test1::wndproc;" will work. But, the callback function can't call the "testing ()" since it's a non-static. The more crazy thing I don't understand is htf can the fprintf able to print the address of the wndproc as an integer, while the "int i = Test1::wndproc; " will get a compiler error as well.... So I got 2 main questions: 1) WNDPROC temp1 = (What should I put here?) Test1::wndproc; and is it possible to obtain the address of wndproc since it's inside a class and non-static function? 2) Or I should just make my own call back function and a dummy one for the WNDPROC temp1 to register? Thank you for the help

Share this post


Link to post
Share on other sites
psae0001    100
I've searching online more than weeks probably a month. I can't find any solution to tell how to obtain the address of wndproc, and I want to the callback function inside the class (if possible) because it'll give lots of advantages.

Share this post


Link to post
Share on other sites
psykr    295
Of the two options you posted, the second would probably be easier. (Would take a bit of hacking to get the first one done..), but google has plenty of links, and a site called GDNet has a tutorial on this, written by Oluseyi (can't be bothered to find the clicky right now).

Share this post


Link to post
Share on other sites
bytecoder    100
Quote:

I've searching online more than weeks probably a month. I can't find any solution to tell how to obtain the address of wndproc, and I want to the callback function inside the class (if possible) because it'll give lots of advantages.

here: http://www.rpi.edu/~pudeyo/articles/wndproc/
It was the first link after googling it; I even posted a link to the search results...

Share this post


Link to post
Share on other sites
psae0001    100
"here: http://www.rpi.edu/~pudeyo/articles/wndproc/
It was the first link after googling it; I even posted a link to the search results..."

I read that one a few times already (before posted this thread), I'm still searching a better solution (don't want to use MFC / ATL).

Share this post


Link to post
Share on other sites
psae0001    100
Ok, http://www.gamedev.net/reference/articles/article1810.asp
pretty much it's telling the samething that

WNDPROC temp1 = Test1::wndproc; <--- this code is never possible.

And, I guess, the only solution is to create my own callback function and set a dummy one for the WINCLASSEX / WINCLASS.

Share this post


Link to post
Share on other sites
Aprosenf    372
The problem is that a class member function has a hidden 'this' pointer that gets passed to it. However, when Windows wants to send a message to an HWND, it has no clue what CTest object to pass as the 'this' pointer. In other words, you can't use a non-static member function as a message handler. As mentioned in more detail in that article several people have pointed out, what you do is store the 'this' pointer in a memory address of your window known as the window's user data or application data. In other words, Windows is nice enough to let us give a window a pointer, so we don't have to deal with a std::map or whatever to associate additional info with windows. Once we set up that data, we use give Windows a global message handler whose only job is to call the class member function on the pointer stored in the window.

Share this post


Link to post
Share on other sites
bytecoder    100
Quote:

I read that one a few times already (before posted this thread), I'm still searching a better solution (don't want to use MFC / ATL).

I don't know where you get the mfc and atl comment; it shows you how to do it in plain win32. Here, I'll even copy and paste the section for you. I don't know how much easier I can make.
Quote:

Per-Window Data

You can associate a block of memory with an instance of every window that is created by Windows. That block, along with variables pointed to by pointers stored in that block, is called Per-Window Data. You can specify the size of this data area by providing appropriate value in cbWndExtra member of WNDCLASS structure, read it by using GetWindowLong or GetWindowLongPtr, and write it with SetWindowLong or SetWindowLongPtr. The use of Ptr versions are recommended because they are compatible with 64-bit Windows, and you should use them if you're interested in upward compatibility.

We want to associate a given Window object with a particular window by storing pointer to the object in the window's per-window data area. In other words, at some point we will call

SetWindowLong(hwnd, GWL_USERDATA, this);

to establish the association, and at a later time we will use

Window *w = (Window *) GetWindowLong(hwnd, GWL_USERDATA);

to get our Window pointer from the window. Our global WndProc will thus be:

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
Window *w = (Window *) GetWindowLong(hwnd, GWL_USERDATA);
if (w)
return w->WndProc(hwnd, msg, wp, lp);
else
return DefWindowProc(hwnd, msg, wp, lp);
}

The if statement is required to handle the case when WndProc is called before the association is established; more on this later. When do we establish the association, that is, call SetWindowLong? It may be tempting to do so right after CreateWindow call, as the following code snippet shows:

Window w;
HWND hwnd = CreateWindow(TEXT("BaseWnd"), TEXT("Hello, World!"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, 0, 0, hinst, 0);
if (!hwnd)
return -1;
SetWindowLong(hwnd, GWL_USERDATA, &w);

Unfortunately, this will leave many messages unprocessed, including the important WM_CREATE message, because for them WndProc will be called before CreateWindow returns. It would be nice to establish the association earlier.

WM_NCCREATE comes to the rescue. As MSDN says, it is sent before WM_CREATE, and it comes with a pointer to CREATESTRUCT that we will use to pass this pointer. Note that CreateWindow has a parameter called lpParam; this same parameter is lpCreateParams member of CREATESTRUCT. By passing a pointer to our Window object, w, in lpParam, we can retrieve it later through lpCreateParams. Having acquired the pointer, we can call SetWindowLong in WM_NCCREATE handler. When WM_CREATE comes around, the pointer will be retrieved via GetWindowLong and the member handler function will be called. See the complete source code here: windowdata.cpp

There is one disadvantage of this approach, and that is the fact that WM_NCCREATE is not the first message received by WndProc. If you are lucky and don't check whether the value retrieved by GetWindowLong was non-null and access member variables when your this pointer is NULL, your program will crash. More precisely, window procedure recieves a WM_GETMINMAXINFO message before it receives WM_NCCREATE.

Share this post


Link to post
Share on other sites
Colin Jeanne    1114
Quote:
Original post by Empirical
You cannot.

(I did once see an asm solution, but never in practice)

Does the ATL count as 'in practice'? I think it only works on certain systems though (that's what I remember from the #defines).

Share this post


Link to post
Share on other sites
Empirical    190
Quote:
Original post by Invader X
Quote:
Original post by Empirical
You cannot.

(I did once see an asm solution, but never in practice)

Does the ATL count as 'in practice'? I think it only works on certain systems though (that's what I remember from the #defines).


Well, possibly, though in reality isn't it using a methord simular to the above behind the scenes?

Truth is I know sweet FA about ATL. Win32 through and through in my narrow little world ;)

Besides I assumed he wanted to use Win32, and I was right! :P

Share this post


Link to post
Share on other sites
antareus    576
The whole, "I don't want to use MFC/ATL" thing strikes me as yet another meme. ATL has no runtime requirements and has very small overhead.

Use WTL (which uses ATL) and don't worry about the minute details of routing messages to window instances. As a special bonus, it uses CClassname notation!

Share this post


Link to post
Share on other sites
Evil Steve    2017
From my custom control:

// When creating window...
m_hWnd = CreateWindowEx(0,WINDOW_CLASS_NAME,"Druink IM",WS_OVERLAPPEDWINDOW,x,y,w,h,NULL,NULL,hInstance,this);

// The static WNDPROC (the one you put in your WNDCLASS structure when you register the window class with RegisterClass):
LRESULT CALLBACK CWindow::StaticWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
CWindow* pParent;

pParent = (CWindow*)GetWindowLong(hWnd,GWL_USERDATA);
if(uMsg == WM_CREATE)
{
pParent = (CWindow*)(((CREATESTRUCT*)lParam)->lpCreateParams);
SetWindowLong(hWnd,GWL_USERDATA,(LONG)pParent);
return pParent->WndProc(uMsg,wParam,lParam);
}
else if(!pParent)
return DefWindowProc(hWnd,uMsg,wParam,lParam);
assert(pParent->m_hWnd == hWnd);
return pParent->WndProc(uMsg,wParam,lParam);
}

// The 'real' WNDPROC (non-static, in the class):
LRESULT CWindow::WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
// Code here
}
return DefWindowProc(m_hWnd,uMsg,wParam,lParam);
}


The static wndproc tries to get the pointer to the class thats saved in the HWND's user data.
WM_CREATE is one of the first messages sent to the window. When the function gets that message, it extracts the pointer to the class passed as the extra parameter to CreateWindowEx() - the 'this' pointer. It then saves this pointer into the HWND's user data, and calls the non-static WndProc.
If this is a message before WM_CREATE (e.g. WM_NCCREATE), then we don't have a pointer to the class yet, and we have to just call the DefWindowProc().
For normal messages, we do a quick check to see that the HWND in the class we got from the HWND's user data is actually the same as the one we're using (just a sanity check), and then pass the message off to the non-static window proc.

The above code assumes that the CWindow class has a HWND member variable called m_hWnd.

Hope this helps.
Cheers,
Steve

Share this post


Link to post
Share on other sites
Verg    450
Quote:
Original post by psae0001
I've searching online more than weeks probably a month. I can't find any solution to tell how to obtain the address of wndproc, and I want to the callback function inside the class (if possible) because it'll give lots of advantages.


This post is one day old... right in this very forum.

As others explained, you can't do WndProc as a member function because WINDOWS itself is what calls it. Since it doesn't have an object of your Window-class type, how would it call it?

If using the class that contains your "WndProc" looks like this:

YourClass object;
object.WndProc(HWND,UINT,WPARAM,LPARAM)...

How will Windows get "object" in order to call WndProc?

It can't.

Everyone else has made good suggestions on how to get around this.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this