Title: Callback madness
bmw5002 - September 3, 2004 12:41 AM (GMT)
So heres the deal. I have a few callback procedures within a class. Now when you use callbacks in a class, you have to declare them with the static keyword so windows can access the procedure and call it, am I right? But now if I want to use member variables declared in my class in the callback procedure, the compiler bitches about all those variables having to be declared static too. And that goes along with the documentation, which states:
| QUOTE |
| When modifying a member function in a class declaration, the static keyword specifies that the function accesses only static members. |
Ok, thats pretty gay. Anyway, when I change all the members that the callback uses to static, I get all "unresolved external symbol" linking errors. So is it possible to have a callback within a class and allow it to access other members from the class, or must it be completely self contained (this is the callback for running a thread with CreateThread)?
I think that option may be possible, since its the ThreadProc callback, which allows me to pass a structure into it and have it return a DWORD. Heres the declaration for it:
| CODE |
DWORD WINAPI ThreadProc( LPVOID lpParameter // thread data ); |
Is this the only way I can accomplish this?
Arrg. Have we no DECENCY??


-------------------------------------------------
Windows XP Pro
MSDEV 2002 v7.0.9466 with .NET Framework 1.0.3705
Dante Shamest - September 3, 2004 05:44 AM (GMT)
If I understand correctly, you wish to have a member function act as a window procedure.
You can achieve this by these steps:
1. Associating each window created with an object of your class using SetWindowLongPtr().
2. Your static window procedure will use GetWindowLongPtr() to get each object of your class, and call the object's member window procedure.
Here's the code.
main.cpp
| CODE |
#include "mywindow.h"
int WINAPI WinMain( HINSTANCE hInst, HINSTANCE hPrev, LPSTR args, int nShow ) { MyWindow w;
w.Create( "My Window", WS_OVERLAPPEDWINDOW|WS_VISIBLE, 0 ); MSG msg; while( GetMessage(&msg,0,0,0) > 0 ) { TranslateMessage(&msg); DispatchMessage(&msg); } return (int)msg.wParam; } |
mywindow.h
| CODE |
#ifndef MYWINDOWCLASS_H #define MYWINDOWCLASS_H #include <windows.h>
class MyWindow {
public:
/* * Creates/re-creates the window. */ void Create( LPCTSTR szText, DWORD dwStyle, DWORD dwExStyle );
/* * Handles the window's messages. */ LRESULT WndProc( UINT, WPARAM, LPARAM );
/* * Returns a reference to the window's handle. */ HWND& Handle() { return hWnd; }
private:
HWND hWnd;
};
#endif |
mywindow.cpp
| CODE |
#include "mywindow.h"
#define MY_WINDOW_CLASS "MyWndCls"
/* * This window procedure has internal linkage and only serves to direct * messages to each window's WndProc(). */ static LRESULT CALLBACK WndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) { MyWindow* w = (MyWindow*)(LONG_PTR)GetWindowLongPtr( hWnd, 0 );
if ( msg == WM_NCCREATE ) { MyWindow* w = (MyWindow*)((CREATESTRUCT*)lParam)->lpCreateParams;
w->Handle() = hWnd;
SetWindowLongPtr( hWnd, 0, (LONG)(LONG_PTR)w );
return DefWindowProc(hWnd,msg,wParam,lParam); }
else if ( w ) { return w->WndProc( msg, wParam, lParam ); }
return DefWindowProc(hWnd,msg,wParam,lParam); }
LRESULT MyWindow::WndProc( UINT msg, WPARAM wParam, LPARAM lParam ) { switch(msg) { case WM_CREATE: { SetWindowPos( hWnd, NULL, 0, 0, 400, 400, SWP_NOZORDER ); return 0; }
case WM_DESTROY: { PostQuitMessage(0); return 0; }
default: { return DefWindowProc( hWnd, msg, wParam, lParam ); } } }
void MyWindow::Create(LPCTSTR szText, DWORD style, DWORD exStyle) { static bool initialized = false; if ( !initialized ) { WNDCLASS wc; ZeroMemory( &wc, sizeof(WNDCLASS) ); wc.hInstance = GetModuleHandle(0); wc.lpszClassName = MY_WINDOW_CLASS; wc.lpfnWndProc = ::WndProc; wc.cbWndExtra = sizeof(MyWindow*); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE); if ( RegisterClass(&wc) ) initialized = true; }
if ( initialized ) { if ( IsWindow( Handle() ) ) DestroyWindow( Handle() );
style = style & (~WS_CHILD); CreateWindowEx( exStyle, MY_WINDOW_CLASS, szText, style, 0, 0, 0, 0, HWND_DESKTOP, 0, GetModuleHandle(0), this ); } }
|
C-Man - September 3, 2004 11:06 AM (GMT)
I would go about std::map to correct this situation
static std::map<HWND,pWindowClass *> window_map;
bmw5002 - September 4, 2004 02:40 AM (GMT)
No you misunderstood. I figured a way though. Basically I needed a way to use static callbacks within a class but still be able to access class members. The solution was to declare this static variable and static callback function and one regular fnction like so in the header file:
| CODE |
//Private Variables static CScroller * ClassPtr; //Private Functions VOID ScrollCallback(); static VOID CALLBACK ScrollCallbackWrapper( UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2);
|
Then in the implementation file, I put these:
| CODE |
CScroller * CScroller::ClassPtr; //stop the link error caused by static member functions
VOID CALLBACK CScroller::ScrollCallbackWrapper( UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2) { ClassPtr->ScrollCallback(); //call non-static member function from within this static function }
VOID CScroller::ScrollCallback() { //Code for actual callback goes here. It is a standard class member function //so is able to access member variables like normal. }
|
ClassPtr is filled with a pointer to the class instance at initilization like so: ClassPtr=this;
If anyone else has a problem like this, you can read more on the ussue at:
http://www.codeproject.com/cpp/SetTimer__non-static.asphttp://www.function-pointer.org/callback.htmlhttp://weblogs.asp.net/oldnewthing/archive...1/09/49028.aspx
C-Man - September 4, 2004 07:38 AM (GMT)
It's not possible do to a number of reasons u have to workaround it
FrozenKnight - September 6, 2004 08:47 AM (GMT)
you cant do this because classes have a 'hidden' paramiter passed to all member functions before they are actually called this 'hidden' value is a pointer basically a pointer to the class which allows access to the class members. so even though it looks possiable it really isn't. i have heard of some work arounds for this but no soultions.
one idea i had but havent tryed yet is to attempt to add the class refrence in via inline asm.
Nintendofreak88 - September 8, 2004 09:31 PM (GMT)
Hey Dante Shamest, I'm using your window class stuff in my program as a base to build on. If I create a new class for child windows, should i use a subclass? If so, how should I do that?
Dante Shamest - September 9, 2004 07:37 AM (GMT)
The code I posted is a variation of my own window class I use in my projects. I use it for top-level windows only (overlapped and popup windows).
For controls, I create separate classes for each control, but they all derive from a simple Window class that only contains a HWND.
Nintendofreak88 - September 9, 2004 12:03 PM (GMT)
Ohh so just a HWND in the class they're derived from. Gotcha. But for my main window i should probly still use this?
C-Man - September 9, 2004 12:36 PM (GMT)
| QUOTE |
| For controls, I create separate classes for each control, but they all derive from a simple Window class that only contains a HWND. |
I would add some basic methods to it like resizing , moving & seting text
Dante Shamest - September 9, 2004 01:27 PM (GMT)
| QUOTE |
| But for my main window i should probly still use this? |
Yup.
P.S. My TopLevelWindow class is actually an abstract class that also derives from my Window class. The MessageHandler() method is pure virtual.
Nintendofreak88 - September 13, 2004 11:54 PM (GMT)
Ok I'm trying to subclass a rich edit and I have this in my class method declarations:
| CODE |
| static LRESULT CALLBACK WndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam); |
Then the actual code:
| CODE |
LRESULT CALLBACK RichEdit::WndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam){ switch(message){ //blah blah } } |
And I subclass like this (in my creation method):
| CODE |
oldWndProc = (WNDPROC)GetWindowLong(Handle(),GWL_WNDPROC); SetWindowLong(Handle(),GWL_WNDPROC,(long)WndProc); |
It compiles fine but it doesn't work right... What am I doing wrong?
C-Man - September 14, 2004 11:03 AM (GMT)
Well it depends on what u mean by not working right
Nintendofreak88 - September 14, 2004 12:04 PM (GMT)
Well if it won't work maybe I should just leave the subclass WndProc outside the class? I've heard of problems with WndProc's in classes before. Dante what do you use? I basicly am just using some of my prior knowlage. I don't really know what I'm doing wrong and/or what I should be doing. :unsure:
C-Man - September 14, 2004 12:45 PM (GMT)
I'll try my method and if it works i'll post it here
Nintendofreak88 - September 14, 2004 09:04 PM (GMT)
Ok I'll wait then. And Dante if you have a method and you won't mind sharing it would be nice too! :)
Dante Shamest - September 15, 2004 06:38 AM (GMT)
Apologies for replying so late, Nintendofreak88.
The truth is, I don't wrap my child controls' window procedures at all. The window wrapper I posted is one I use only with top-level windows. I suppose you could try to subclass a child control's window procedure and somehow redirect it to a member function, but I think it's dangerous. As you said. I think it's better overall to leave the subclassing somewhere else.
The only time I try to wrap a window procedure for a child control is when I'm writing a custom child control.
Sorry for the confusion! :)
Nintendofreak88 - September 15, 2004 12:07 PM (GMT)
Alright, I'll just leave my wndproc's for my child windows outside of my classes then. Thanks anyway!
C-Man - September 15, 2004 12:45 PM (GMT)
Well i have made everything inside the class so far works ok but just need to
fix a few bugs
C-Man - September 16, 2004 01:09 PM (GMT)
Ok i have some basic code for my "method" but can't find where to upload :(
EDIT: borowed some space :) so here u go
GUI.rar
Nintendofreak88 - September 17, 2004 09:06 PM (GMT)
C-Man - September 18, 2004 06:14 AM (GMT)
Nintendofreak88 - September 28, 2004 09:49 PM (GMT)
Lol I seem to be bumping this all the time. Maybe it should be pinned LOL. Anyway I have a subclassed child. Now in this, I need to access some variables etc from the class, but I don't want to make the object global. Is there some way I can pass a pointer to it or somethin so that it has an object to use?
C-Man - September 29, 2004 11:26 AM (GMT)
i think protected might be the ancwer
Nintendofreak88 - September 29, 2004 12:10 PM (GMT)
No, you misunderstood me, but I think it works fine now with the classes created as temproary global variables.
C-Man - September 29, 2004 12:57 PM (GMT)
well you could explain it a little cleared :mellow: if i understood u right ... now ...
look up friend
Nintendofreak88 - September 29, 2004 09:18 PM (GMT)
Ok that was kind of what i wanted but not really. I'll try to explain it clearer. I have a class for a control. I subclassed that control. The WndProc I subclassed it with isn't a member of that control's class. Now the object I create from that class isn't global. I want to access some methods and such from that class in my WndProc, but I don't have an object. So basicly do I need to create a temporary object? Because like when I go like:
| CODE |
| TheClass::DoStuff(); |
The compiler says I can't access DoStuff without an object. And since it's a WndProc I can't pass a pointer to the object of that class (unless theres some way I don't know of...)
C-Man - September 30, 2004 05:59 AM (GMT)
I'll modify my code l8er to show u how would i go about doing this ;)
It all rounds up to puting your wndproc in the class as a nonstatic member function