Skip to main content
GameDev.net gamedev.net
🔒 Locked

WndProc() Inside a Window Class

Started by Goblin Nov 22, 2001 at 4:03 PM 5 replies 5.5k views
Original Post
Goblin
Goblin
For awhile I was simple using a static WndProc() function and a singleton of the class for windows, but then I realized I couldn't use the same window class for dialogs or anything, and I couldn't have more than one instance. So... my question is what's the best way to get WndProc() inside a class. I've heard that you can reserve extra memory through the window class and then put a pointer to the class instance through that... but I'm really unsure of how to accomplish this. Another idea I had was having a static std::map and associating each HWND with a pointer to the class instance, but I was told that was not worth the trouble. Any help would be appreciated, thanks. ----------------- The Goblin (madgob@aol.com) ----------------- "Before critisizing somebody, walk a mile in their shoes. That way, when you do critisize them, not only will you be a mile away, but you'll also have their shoes!" Edited by - Goblin on November 22, 2001 5:04:59 PM
The Goblin (madgob@aol.com)
KlePt0
KlePt0
About the encapsulated window class you may or may not like this suggestion.

but look at this
(sorry for the length but i find this INCREDIBLY USEFUL and i think others may too)

    
////////////////////////////////////////////////////////////////////////////////

// ATL/AUX -- COM/ATL Useful Helpers

// Copyright (c) by Andrew Nosenko (andien@geocities.com), 1997-99

// http://www.geocities.com/~andien/atlaux.htm

// Version: 1.10.0011


/*
óðåçàííàÿ ââåðñèÿ
*/

#ifndef _AtlAux2_78310e22_e3e6_4f10_9af5_a4e485060097
#define _AtlAux2_78310e22_e3e6_4f10_9af5_a4e485060097

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

////////////////////////////////////////////////////////////////////////////////
/* Win32 Callback thunking, the main idea was taken from ATL.

Thunks could be used to turn an object+method pair into a closure that could be
passed to Win32 API requiring a callback address. This is a clever idea of ATL
team, I've only tried to propagate it over a general case.
Thunks are very suitable and efficient way to avoid tiresome TLS/CritSecs/maps
(when providing apartment threading and reentrancy).

CAuxThunk<> is provided for __thiscall methods, CAuxStdThunk<> is for __stdcall.

Here is an example of how to set up Win32 hook the reentrant way, without a
use of global variable:

class CHook:
public CAuxThunk<CHook>
{
public:
CHook(): m_hhook(NULL)
{
InitThunk((TMFP)CBTHook, this);
}

LRESULT CBTHook(int nCode, WPARAM wParam, LPARAM lParam);
BOOL Hook() {
m_hook = SetWindowsHookEx(WH_CBT, (HOOKPROC)GetThunk(), NULL, GetCurrentThreadId());
return (BOOL)m_hook;
}

HHOOK m_hhook;
...
};

LRESULT CHook::CBTHook(int nCode, WPARAM wParam, LPARAM lParam)
{
if ( nCode == HCBT_CREATEWND ) {
UnhookWindowsHookEx(m_hook);
HWND hwnd = (HWND)wParam;
// do whatever we want with HWND
...
}
return CallNextHookEx(m_hook, nCode, wParam, lParam);
}

Another example. I need to process an event in my IE4 simple object
(windowless) *asynchronously* and use SetTimer API for that.
Unfortunately, TIMERPROC doesn't take any context info.
Here is how it has been solved:

struct TIMEOUT: CAuxThunk<TIMEOUT> {
UINT m_timerID;
CONTEXT m_contex;

TIMEOUT(CONTEXT& contex): m_contex(contex)
{
InitThunk((TMFP)TimerProc, this);
}
void TimerProc(HWND, UINT, UINT idEvent, DWORD dwTime);
...
};

void TIMEOUT::TimerProc(HWND, UINT, UINT idEvent, DWORD dwTime)
{
KillTimer(NULL, m_timerID); // one-shot callback
// do any processing task
...
delete this;
}
HRESULT CSimpleObj::Post(CONTEXT& contex) {
TIMEOUT* pTimeout = new TIMEOUT(context);
pTimeout->m_timerID = ::SetTimer(NULL, 0, timeout, (TIMERPROC)pTimeout->GetThunk());
}
//////////////////////////////////////////////////////////////////////////////*/



#ifndef _M_IX86
#pragma message("CAuxThunk/CAuxStdThunk is implemented for X86 only!")
#endif

#pragma pack(push, 1)

template <class T>
class CAuxThunk
{
BYTE m_mov; // mov ecx, %pThis

DWORD m_this; //

BYTE m_jmp; // jmp func

DWORD m_relproc; // relative jmp

public:
typedef void (T::*TMFP)();
void InitThunk(TMFP method, const T* pThis)
{
union {
DWORD func;
TMFP method;
} addr;
addr.method = method;
m_mov = 0xB9;
m_this = (DWORD)pThis;
m_jmp = 0xE9;
m_relproc = addr.func - (DWORD)(this+1);
FlushInstructionCache(GetCurrentProcess(), this, sizeof(*this));
}
FARPROC GetThunk() const {
_ASSERTE(m_mov == 0xB9);
return (FARPROC)this; }
};

template <class T>
class CAuxStdThunk
{
BYTE m_mov; // mov eax, %pThis

DWORD m_this; //

DWORD m_xchg_push; // xchg eax, [esp] : push eax

BYTE m_jmp; // jmp func

DWORD m_relproc; // relative jmp

public:
typedef void (__stdcall T::*TMFP)();
void InitThunk(TMFP method, const T* pThis)
{
union {
DWORD func;
TMFP method;
} addr;
addr.method = method;
m_mov = 0xB8;
m_this = (DWORD)pThis;
m_xchg_push = 0x50240487;
m_jmp = 0xE9;
m_relproc = addr.func - (DWORD)(this+1);
FlushInstructionCache(GetCurrentProcess(), this, sizeof(*this));
}
FARPROC GetThunk() const {
_ASSERTE(m_mov == 0xB8);
return (FARPROC)this; }
};

#pragma pack(pop) // CAuxThunk


#endif //_AtlAux2_78310e22_e3e6_4f10_9af5_a4e485060097




*****************************************
EDIT: if you can't read the code above, just highlight all the text
*****************************************


for your case you would use it like....


  
class CMyWindow : public CAuxThunk<CMyWindow>
{
public:
CMyWindow()
{
InitThunk((TMFB)MyWindowProc);
}

// Notice its not static or different in anyway from

// normal member function

LRESULT MyWindowProc(HWND,UINT,WPARAM,LPARAM);

BOOL Create()
{
......

WNDCLASS Class;
....
Class.lpfnWndProc=(WNDPROC)GetThunk();
......
}
}



Edited by - KlePt0 on November 22, 2001 8:14:49 PM
Bobby Ward - COM Guru in training
amag
amag
Ohh, scary...
It looks like it''s compiler dependent...does it work for other compilers than VC++?
Oluseyi
Oluseyi
quote:
Original post by amag
Ohh, scary...
It looks like it''s compiler dependent...

GUIDs scare you?

[OT:] The February 2001 Platform SDK CAB files containing GDI+ had the files named using GUIDs, and there was a comment about that on CodeProject.com: "Microsoft developers are so obsessed with GUIDs, I wonder how they name their children?"

Picture it..."Hey Johnnie_A_18djtlsucm_85jso8nb, pass me the monkey wrench, will ya?"
burp
burp
Interesting... KlePt0

But, shouldn''t this line

InitThunk((TMFB)MyWindowProc);

be this

InitThunk((CAuxThunk::TMFP)MyWindowProc,this);

But, even then it still doesn''t work.

I think there is an easier way, but I''ll have to try it first.
burp
burp
    
/*
template <class T> class Thunk
{
BYTE asm86[12];
public:
typedef void (__stdcall T::*ThunkType)();

void InitThunk(ThunkType method, const T *pthis)
{
union uPtr { BYTE *byte; DWORD *dword; ThunkType method; };

uPtr pAsm86;
pAsm86.byte = asm86;
uPtr pFn;
pFn.method = method;

*pAsm86.byte++ = 0x58; //pop eax
*pAsm86.byte++ = 0x68; //push pthis
*pAsm86.dword++ = (DWORD)pthis;
*pAsm86.byte++ = 0x50; //push eax
*pAsm86.byte++ = 0xE9; //jmp method
*pAsm86.dword++ = (DWORD)pFn.dword - (DWORD)(this+1);

//This function is valid only on multiprocessor computers.
//FlushInstructionCache(GetCurrentProcess(), this, sizeof(*this));
}

FARPROC GetThunk() const
{
return (FARPROC)this;
}
};
class Window : private Thunk<Window>
{
LRESULT CALLBACK WindowProc(HWND,UINT,WPARAM,LPARAM);
public:
Window()
{
InitThunk((Thunk<Window>::ThunkType)WindowProc,this);
WNDCLASSEX wc;
wc.lpfnWndProc=(WNDPROC)GetThunk();
}
};
//*/





Edited by - burp on November 23, 2001 2:48:55 PM

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.