Wrapping up a window class or two [Resolved]
I am making an app, with two different windows. And I want to wrap the windows up in two classes.
But I have a few questions about what I should do with the message pump and the message handler (if that is what they are called).
Is the message handler (the one that takes cares of what to do with the different messages) supposed to be a member function in the two classes. But the message pump (is that what it is called? The one with TranslateMessage() and DispatchMessage()) supposed to be just a global function in the app, and not be wrapped up at all? If so, am I supposed to send a function pointer (to the message pump) as a parameter to each of the classes, when making new objects?
PS: Please correct the names of the two functions if they are a bit (or totaly) off.
Thanks in advance.
ØØ
Re: Wrapping up a window class or two
You should go for the message main loop (that's the GetMessage(), TranslateMessage() & DispatchMessage() thingee) into the WinMain. And the window procedure must be a static member of your class, because the calling convention __thiscall is not compatible with standard C calling conventions that are used to implement a window procedure (__stdcall). Just search the forum and you will find a ton of people who have tried this before you.
Re: Wrapping up a window class or two
You may want to look at some posts by Bond. I remember Bond working on something similar to this..
Re: Wrapping up a window class or two
Thanks, been seraching since I posted the thread. And searched for Bonds thread too. But couldn't find anything about the message pump in his thread. I found some other interesting threads about the topic.
It looks like the Mesage handler should be ok to have as a member function, but the message pump the (WNDPROC) has to be a static global function. But if the function that creates and registers the window is member functions, then I guess I have to pass a function pointer to the class when I am making it. How else should I set the wndClass.lpfnWndProc?
Code:
void Device::DefineWndCls(HINSTANCE hInstance){
wndClass.hInstance = hInstance; // Handle to this instance
wndClass.lpszClassName = szWinName; // The window class name
wndClass.lpfnWndProc = WndProc; // Window function
wndClass.style = 0; // Default style
wndClass.hIcon = LoadIcon(hInstance, (LPCTSTR)IDI_ICON1); // Load an icon.
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); // Load a cursour.
wndClass.lpszMenuName = NULL; // We are not using a menu
wndClass.cbClsExtra = 0; // No extra info needed
wndClass.cbWndExtra = 0; // No extra info needed
wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW); // Set a normal background color
}
Re: Wrapping up a window class or two
Sorry, didn't read fully before I sent that info...
here goes...
Quote:
Is the message handler (the one that takes cares of what to do with the different messages) supposed to be a member function in the two classes.
yes . That makes sense.. because different window classes would want to handle the messages differently, so as the name suggests, message handler, could be put as a class specific thing.
Quote:
But the message pump (is that what it is called? The one with TranslateMessage() and DispatchMessage()) supposed to be just a global function in the app, and not be wrapped up at all? If so, am I supposed to send a function pointer (to the message pump) as a parameter to each of the classes, when making new objects?
You are right. Message pump is supposed to be running all the time in a thread which creates windows. So, it is something you want to put either in the main thread ( in which case it would go into WinMain as suggested ) or in case of seperate thread, it would be a part of the thread procedure.
Re: Wrapping up a window class or two
Thanks, that was a good answer. But just to wrap this up. Passing in a functioni pointer to the class is the only way to tell the class about the Message pump right. It sounds like a ok idea. but if there is a better way. Please tell me before I mark this thread as resolved and hand out points.
Thanks
ØØ
Re: Wrapping up a window class or two
Quote:
Originally Posted by NoteMe
Thanks, that was a good answer. But just to wrap this up. Passing in a functioni pointer to the class is the only way to tell the class about the Message pump right. It sounds like a ok idea. but if there is a better way. Please tell me before I mark this thread as resolved and hand out points.
Thanks
ØØ
Why would you want the class to know about the message pump ? The class doesn't need to know . If the message pump has been implemented in the thread, then it's gonna dispatch message to the appropriate window and that window's wndproc should take care of doing the necessary ?
PS: Message pump is the
GetMessage/TranslateMessage/DispatchMessage thing..
Are you confusing message pump with message handler.
Re: Wrapping up a window class or two
Sorry, fell asleep on my coatch with my laptop on my lap last night..:D..
And I belive you are totaly right. After readin my post again, I can't even understand that you bothered to answer me. I made no sense. I did get confused again about what was the message pump and what was the message handler.
Well I got it now. Thanks for all your help. I really appreciate it. And totaly got it now.
ØØ
Re: Wrapping up a window class or two
Regarding to your highlighted source code:
Code:
wndClass.lpfnWndProc = WndProc;
It should be something like:
Code:
// Device.h
class Device
{
protected:
static LRESULT WndProc ( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
public:
void DefineWndCls(HINSTANCE hInstance);
};
Code:
// Device.cpp
#include "device.h"
void Device::DefineWndCls(HINSTANCE hInstance)
{
wndClass.hInstance = hInstance; // Handle to this instance
wndClass.lpszClassName = szWinName; // The window class name
wndClass.lpfnWndProc = (WNDPROC)Device::WndProc; // Window function
wndClass.style = 0; // Default style
wndClass.hIcon = LoadIcon(hInstance, (LPCTSTR)IDI_ICON1); // Load an icon.
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); // Load a cursour.
wndClass.lpszMenuName = NULL; // We are not using a menu
wndClass.cbClsExtra = 0; // No extra info needed
wndClass.cbWndExtra = 0; // No extra info needed
wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW); // Set a normal background color
}
DWORD Device::WndProc ( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
// handlers go here
return DefWindowProc(hwnd, msg, wParam, lParam);
}
Re: Wrapping up a window class or two
Yeah, thats what I ended up doing. Thanks.
Øyvind Østlund
Re: Wrapping up a window class or two
WNDPROC doesn't necessarilly have to be static. You could create machine code thunk (similar to ATL/WTL thunk) that strips most of class information from pointer to member function and points to class member directly. That way you could have a window class with only one WNDPROC function that ISN'T static.
The only problem with this method is that it is processor dependant and compiler dependant.
Here's an example of a thunk that works with msvc 6.0 on intel compatible and does not support multiple and virtual inheritance:
namespace WCL
{
#if defined(_M_IX86) && defined(_MSC_VER)
#pragma pack (push,1)
template <typename TFnStatic, typename TFnMember, typename TObject>
class CCallbackThunkT
{
protected:
BYTE m_byMov;
DWORD m_dwObject;
BYTE m_byJmp;
DWORD m_dwRelProc;
public:
CCallbackThunkT (VOID) : m_byMov (0), m_dwObject (0), m_byJmp (0), m_dwRelProc (0) {};
~CCallbackThunkT (VOID) {}
VOID Init (TObject* pObject, TFnMember fnMember)
{
m_byMov = 0xB9; // mov ECX, m_dwObject
m_dwObject = reinterpret_cast<DWORD> (pObject);
m_byJmp = 0xE9; // jmp m_dwRelProc
m_dwRelProc = *(reinterpret_cast<LPDWORD> (&fnMember)) - (reinterpret_cast<DWORD> (this) + sizeof (CCallbackThunkT)); // relative jmp
::FlushInstructionCache (::GetCurrentProcess(), this, sizeof (CCallbackThunkT));
}
__forceinline TFnStatic GetStaticProc (VOID) const
{
return (reinterpret_cast<TFnStatic> (this));
}
__forceinline operator TFnStatic (VOID) const
{
return (reinterpret_cast<TFnStatic> (this));
}
};
#pragma pack (pop)
#endif // defined(_M_IX86) && defined(_MSC_VER)
}
namespace WCL
{
class CWindow
{
protected:
typedef LRESULT (CWindow::* WCL_WNDPROC) (HWND, UINT, WPARAM, LPARAM);
CCallbackThunkT<WNDPROC, WCL_WNDPROC, CWindow> m_cbThunk;
HWND m_hWnd;
public:
CWindow (HWND hWnd = NULL) : m_hWnd (hWnd)
{
m_cbThunk.Init (this, WndProc);
}
LRESULT WndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
m_hWnd = hWnd;
switch (uMsg)
{
case WM_DESTROY:
::PostQuitMessage (0);
return 0L;
}
return ::DefWindowProc (hWnd, uMsg, wParam, lParam);
}
WNDPROC GetWndProc (VOID)
{
return m_cbThunk.GetStaticProc();
}
}
}
...
using namespace WCL;
...
(inside your WinMain):
CWindow Window;
WNDCLASS wc;
ZeroMemory (&wc, sizeof (WNDCLASS));
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = Window.GetWndProc();
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = GetModuleHandle (NULL);
wc.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor (NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = TEXT ("MyWindow");
// then you'll need to call some sort of create function (not present here)...
Re: Wrapping up a window class or two
Or you could just use WTL.
Re: Wrapping up a window class or two
You are wrong... The Window procedure has to be static because, a normal class member requires this to be passed as the first parameter. And how should the GUI subsystem know which class are you currently using? So the Window procedure must be static.
WTL just uses a static function pointer table which has pointer to member functions to be called on a specific message/id. Just take a carefully look on the WTL/MFC (especially CCmdTarget class) source code and you will find out that it's member procedure is also static.
Convinced? :)
Re: Wrapping up a window class or two
Actually I'm currently using my own window classes on msvc 6.0 without any static functions.
And I never said that I'm using the same thunking technique as ATL.
Now try the code first before posting again that this is impossible.
Still not convinced??
Check out theese links:
www.hackcraft.net/cpp/windowsThunk/thiscall/
http://www.codeproject.com/atl/atlaux.asp
(see CAuxThunk)
http://www.codecomments.com/archive2...-3-407950.html
http://www.codeproject.com/cpp/SoloGenericCallBack.asp
(see 3rd comment under "Use __thiscall" comment)
http://www.codeproject.com/cpp/thunk.asp
http://www.codeproject.com/cpp/Ellipses.asp
Re: Wrapping up a window class or two
Of course you can make pointers to member which to be called on specific id's or message. This is the way how MFC handles their action on buttons etc. But the window procedure which calls those application defined handlers is static. And that's the way it has to be. Apart from making it static you can also make it a global function.
Code:
LRESULT WndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
m_hWnd = hWnd;
switch (uMsg)
{
case WM_DESTROY:
::PostQuitMessage (0);
return 0L;
}
return ::DefWindowProc (hWnd, uMsg, wParam, lParam);
}
WNDPROC GetWndProc (VOID)
{
return m_cbThunk.GetStaticProc();
}
Your Window procedure here, is also a normal global function. And not the member. My point was to explain that a window procedure can (theoretically) never and absolutley never be a member of a class. Just take a look at the SoloGenericCallback example at the CodeProject:
Code:
class A{
_CallBackProcThunk thunk;
//start callback here
void Init(...){
thunk.Init((DWORD_PTR)StaticCallerProc, this);
SomeCallback(param1,..., (CALLBACK_TYPE)&thunk); //see this
}
static void StaticCallerProc(HWND hWnd, ...){
//At here, hWnd is already modified with pThis;
A* pThis = (A*)hWnd;
pThis->MemberCallbackProc(mHWnd, ...);
}
void MemBerCallbackProc(HWND hWnd, ...){
// we did
}
};
The CallerProc is static. And the CallerProc here is in the GUI handling the window procedure. And here it is static because the Idiom says that it should remain a member of the window class. I have never said that it is impossible that a static member (the Window procedure or the StaticCallerProc) can not call a non-static member function from a specified class. Even MFC does this. With help of the pointer to member Idiom.
That's the way it is.