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

Pass window proc as argument

Started by _Sigma Feb 19, 2007 at 4:52 PM 28 replies 3.3k views
Original Post
_Sigma
_Sigma
I'm trying to pass my window proc through a function so I can assign it to my windows class. THis is what I have:

TRESULT TempestKernel::InitWindow(int width, 
				  int height, 
				  LPSTR wndTitle,
				  MODE windowMode, 
				  LRESULT CALLBACK WndProc)
{
....
winClass.lpfnWndProc = WndProc;
...
}

In my main code:
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
...
}
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
...
 game->InitWindow(400,400,"Tempest",WINDOW,WndProc);
...
}
Yet errors are generated on compile So, what do I need to do to pull this off? Cheers
Adam Hamilton
Adam Hamilton
You can't pass member functions to a window procedure because how does the window procedure know what data to point at?

The correct way to do this is use a static callback function. You can then embed the pointer to your window object within the window itself.

wndclassex.cbWndExtra = 4;

You can use the SetWindowLong / GetWindowLong function to retrieve your saved pointer.
Evil Steve
Evil Steve
LRESULT CALLBACK WndProc)
Should be:
WNDPROC WndProc)
WNDPROC is already typedef'd to the correct function pointer in one of the windows headers.
_Sigma
_Sigma
Evil Steve: Cool, didn't know that.

Adam Hamilton: Can you give an example of this? I'm pretty new to this whole deal :P
_Sigma
_Sigma
Quote:
Original post by LessBread
Creating a Win32 Window Wrapper Class

dammit, I've been pointed to that article before! I'd completely forgotten about it.

Too bad that the full code isn't there, as I would love to have seen everything, but that should work well.

many thanks.
_Sigma
_Sigma
Been reading that article, and I have a couple of quesitons:

1) g_wnd->Create(NULL, WS_OVERLAPPEDWINDOW|WS_VISIBLE);
what does the prototype for that look like? What I mean is, what needs to be done to allow one to OR values together like that?

2) In the chunk of code:
if((m_UsePeekMessage)     ? ::PeekMessage(&msg, m_hwnd, 0, 0, PM_REMOVE)     : ::GetMessage(&msg, m_hwnd, 0, 0))  {    ::TranslateMessage(&msg);    ::DispatchMessage(&msg);  }

What does
::TranslateMessage(&msg);    ::DispatchMessage(&msg);
mean? I haven't touched C++ for about a year, so I think I am just forgetting something? I always thought there needs to be something to the left of the :: ?

Cheers
Aardvajk
Aardvajk
1) Nothing special. windows.h (somewhere) #defines WS_OVERLAPPEDWINDOW and so on as numeric constants, which can automatically be ORed together with C or C++. The parameter is probably something like UINT or DWORD, which would allow for 32 "flag" bits to be packed together by this method, assuming a machine and compiler with 32 bit native words.

#define red 1#define green 2#define blue 4#define yellow 8void f(DWORD colors){}void g(){    f(red|blue|yellow);}


2) :: used in that way just means "in the global scope". It is sometimes used like this to explicitly specify that a name is in the global scope and can resolve ambiguities like:

int x;namespace y{    int x;}void f(){    using namespace y;    std::cout << x << std::endl; // ambiguous    std::cout << ::x << std::endl; // refers explicitly to the global scope x}
_Sigma
_Sigma
Thanks. In your example, how would I tell in f() what was sent in? Ie, how would I differentiate b/w red | yellow and red | blue ? As well, I assume you can pick any constants? They don't have to be powers of 2 like in your example?
Aardvajk
Aardvajk
Quote:
Original post by _Sigma
Thanks. In your example, how would I tell in f() what was sent in? Ie, how would I differentiate b/w red | yellow and red | blue ? As well, I assume you can pick any constants? They don't have to be powers of 2 like in your example?


In reverse:

Yes, they do have to be powers of two. The reason being that the numeric powers of two represent the values of each bit in a binary number, so each power of two number can be represented exclusively by a single bit.

Consider (4 bit examples)

1: 0001
2: 0010
3: 0011
4: 0100

Clearly 1, 2 and 4 can all exist within the same pattern without any ambiguity. However, you could not distinguish between 3 on one hand and combination of 1 and 2 on the other, since in binary the two are the same.

The windows headers will define stuff like WS_OVERLAPPEDWINDOW in terms of power of two values. These are more likely to be expressed in the headers as hex values, but the principle is the same.

To examine the DWORD to see if a value is in it (i.e. bit is set), you would AND the DWORD with the power-of-two value and see if the result is true. Note the single & (bitwise AND) as opposed to the more common double && meaning logical AND.

void f(DWORD colors){    if(colors & red) std::cout << "Red\n";    if(colors & blue) std::cout << "Blue\n";}


That's the principle. To be honest, now that space is cheaper and computers are faster, there is probably less need for using bitsets like this and more flexible solutions that use more memory are probably better to investigate for your own code. The Windows API makes extensive use of bitsets like this though so it is well worth having an understanding of how they work.

HTH
_Sigma
_Sigma
Of course, that makes sense to use powers of two. Should have seen that myself!

So when you say AND them and see if the result is true, you mean the result is 1?
Just to make sure I haven't confused anything, you can use multiple values OR'ed together, and do something like:
void f(DWORD colors){    if(colors & red) std::cout << "Red\n";    if(colors & blue) std::cout << "Blue\n";}f(red | blue);

would output
RedBlue


Correct?

Anyways, many thanks for the help!

Cheers
Emmanuel Deloget
Emmanuel Deloget
Quote:
Original post by _Sigma
Of course, that makes sense to use powers of two. Should have seen that myself!

So when you say AND them and see if the result is true, you mean the result is 1?
Just to make sure I haven't confused anything, you can use multiple values OR'ed together, and do something like:
*** Source Snippet Removed ***
would output
RedBlue


Correct?
Anyways, many thanks for the help!

Cheers

Correct.

To simplify: in C++, any value which is non-zero is intepreted as being true when used in a execution control statement (such as "if", "while" and so on). So even if (colors & blue) evaluate to the value of "blue" (4), it is still seen as "pass the test". What EasilyConfused want to say when he says "if the result is true" is in fact "if the result is not 0". That's a common language abuse - one which is quite specific to C and C++ programmers.

Regards,
_Sigma
_Sigma
Well thats kinda cool. So, does any negative number count as as 'true' too?
Aardvajk
Aardvajk
Quote:
Original post by _Sigma
Well thats kinda cool. So, does any negative number count as as 'true' too?


Yes. Anything non-zero. You can observe this:

if(-1) std::cout << "-1 is \"true\"\n";


I'd also agree with Emmanuel that I should have been more specific about the true/false thing. That's the kind of thing it is easy to start taking for granted after enough years of C or C++. [smile]
Zao
Zao
Quote:
Original post by Adam Hamilton
wndclassex.cbWndExtra = 4;


Interesting fact of the day: Pointers are 8 bytes on x64 Windows. Use SetWindowLongPtr and sizeof(LONG_PTR) to ensure portability.
To make it is hell. To fail is divine.
_Sigma
_Sigma
Excellent. Thanks you all. =)
_Sigma
_Sigma
Quote:
Original post by Adam Hamilton
You can't pass member functions to a window procedure because how does the window procedure know what data to point at?

The correct way to do this is use a static callback function. You can then embed the pointer to your window object within the window itself.

wndclassex.cbWndExtra = 4;

You can use the SetWindowLong / GetWindowLong function to retrieve your saved pointer.

Sorry to be back here, but I ran into an issue.

Going through the code provided here, what do I do with Window::MsgRouter? I see it does something with the SetWindowLong, but how do I associate Window::MsgRouter to the window in the Window::Create(...) procedure? I probably just missed something.

edit
as well, I'm having troubles with this:
1>c:\documents and settings\chris\my documents\programming\c++\tempest\working source\twindow.h(11) : error C2065: 'TWindow' : undeclared identifierAnd the source is:#ifndef TWINDOW_H#define TWINDOW_H#include <Windows.h>#include <WindowsX.h>#include <map>#include "TRESULT.h"typedef long (* tyMessageHandler)(TWindow &, HWND, long, long);typedef std::map<long, tyMessageHandler> tyMessageMap;typedef tyMessageMap::iterator tyMessageIterator;class TWindow{public:	TWindow(HINSTANCE hInstance);	TRESULT Create(DWORD style,int width, int height);	LRESULT CALLBACK MsgRouter(HWND hwnd, UINT message,	WPARAM wparam, LPARAM lparam);	tyMessageIterator GetMessageHandler(long message);	tyMessageHandler RegisterMessageHandler(long message,tyMessageHandler handler);	void SetHWND(HWND hwnd);	bool IsExit();	TRESULT Show();	tyMessageIterator GetMessageHandler(long message);	tyMessageHandler RegisterMessageHandler(long message,tyMessageHandler handler);	bool TWindow::HandleMessages();	long OnClose(TWindow &wnd, HWND hwnd, long param0, long param1);	long OnDestroy(TWindow &wnd, HWND hwnd, long param0, long param1);	int ExitCode();	void SetExitCode(long code);private:	tyMessageMap m_MsgHandlers;	bool m_exit; // true = exiting	HWND m_hwnd;	WNDCLASSEX m_winClass; //Main windows class	HINSTANCE m_hInstance;	long m_exitCode;};#endif


Any ideas what that is all about? Well I understand the problem, but I must admit I don't know how to fix it. :(

Cheers
Chris

[Edited by - _Sigma on February 23, 2007 1:08:51 PM]
_Sigma
_Sigma
I hate to bump, but I"m really stuck with this problem...
rip-off
rip-off
Quote:
Original post by _Sigma
I hate to bump, but I"m really stuck with this problem...


Line 11 looks to be your typedef line. Look at that line:

long (* tyMessageHandler)(TWindow &, HWND, long, long);



The type "TWindow" hasn't been declared yet, that comes later on in your file. To quiet the compiler tell it that the type exists, but that you arent going to flesh it out just yet, like so:

// tell the compiler that this type existsclass TWindow;long (* tyMessageHandler)(TWindow &, HWND, long, long);// etc


This is called forward declaration, and until you give it a full class declaration TWindow is an incomplete type. Incomplete types can be used as references or pointers and in function signatures as arguments and return values ( When implenting the function the type needs to be fully defined I believe ).
_Sigma
_Sigma
yup that works. Thanks.

now the issue is
>c:\documents and settings\chris\my documents\programming\c++\tempest\working source\twindow.cpp(52) : error C2664: 'TWindow::RegisterMessageHandler' : cannot convert parameter 2 from 'long (__thiscall TWindow::* )(TWindow &,HWND,long,long)' to 'tyMessageHandler'1>        There is no context in which this conversion is possible

when I try
this->RegisterMessageHandler(WM_CLOSE,&TWindow::OnClose);

Is this not how its done?

Cheers

Topic Locked

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

Sign in to reply to this topic.