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

function pointers

Started by Ranthalion Jul 17, 2003 at 10:57 PM 14 replies 1.3k views
Original Post
Ranthalion
Ranthalion
Is there any rule against having a function pointer specify the callback procedure of a window? I want to handle user input differently in different situations, so I have two different WndProc functions: WndProcA, and WndProcB. I have a function pointer MyProc that is fed into the WNDCLASSEX structure. I can't seem to get it to compile though...
LRESULT CALLBACK (*MyProc)(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    LPSTR lpCmdLine, int nCmdShow)
{
	

    WNDCLASSEX wc;
    HWND hwnd;
    MSG Msg;

	MyProc=&WndProcB

    wc.cbSize        = sizeof(WNDCLASSEX);
    wc.style         = 0;
    wc.lpfnWndProc   = MyProc;
    wc.cbClsExtra    = 0;
//etc...

But I get these errors when I try to compile it.
 error C2059: syntax error : '('
 error C2065: 'MyProc' : undeclared identifier
 error C2440: '=' : cannot convert from 'long (__stdcall *)(struct HWND__ *,unsigned int,unsigned int,long)' to 'int'
        This conversion requires a reinterpret_cast, a C-style cast or function-style cast
error C2440: '=' : cannot convert from 'int' to 'long (__stdcall *)(struct HWND__ *,unsigned int,unsigned int,long)'
        Conversion from integral type to pointer type requires reinterpret_cast, C-style cast or function-style cast
Error executing cl.exe.


What can I do to fix this? [edited by - Ranthalion on July 17, 2003 11:59:33 PM]
griffenjam
griffenjam
It''s been a while since I''ve messed with function pointers but I think it may work if you did something like this.


LRESULT CALLBACK function1(...);
LRESULT CALLBACK function2(...);

void *fntPtr;

WinMain(...)
{
...

fntPtr = function1;
wc.lpfnWndProc = (LRESULT CALLBACK)fntPtr;

...
}



Function pointers were always hard for me because I never really read about how to do them right. I think my code is very close to what you want though.
Neosmyle
Neosmyle
Have you tried wc.lpfnWndProc = reinterpret_cast(MyProc);
Ranthalion
Ranthalion
Neither of those compile either. With the void *MyProc it still gives me an error concerning __stdcall and casting. Thanks for the ideas though. ^_^

reinterpret_cast also isn''t working for me...
antareus
antareus
wc.lpfnWndProc = MyProc;

Doesn''t work?
--God has paid us the intolerable compliment of loving us, in the deepest, most tragic, most inexorable sense.- C.S. Lewis
Ranthalion
Ranthalion
That''s what I tried at first, but it gave me those 4 errors... There must be something wrong with the way I am declaring the function pointer, but for the life of me, I can''t figure it out...
Beer Hunter
Beer Hunter
I see no error in the code you posted here; is this a direct copy-and-paste? If not, check the spelling/capitalisation of the declaration of MyProc.
Ranthalion
Ranthalion
yeah, it is a copy/paste...
kspansel
kspansel
WNDPROC wndProc;

if( ... )
wndProc = WndProcA;
else
wndProc = WndProcB;

.
.
.

wc.lpfnWndProc = wndProc;

kory spansel
"The pioneers of a warless world are the youth who refuse military service" - Albert Einstein
Ranthalion
Ranthalion
I had my hopes up for a minute! At first that seemed to work, but when I tried to switch the Message Handler to the second one, it wouldn't change it. It continued to use the same function, completely ignoring my statement.
I wrote a small program to test it out. The first time you press a key, it pops up a message box and changes the WNDPROC to the other one. The problem is that Windows continues to use the first WNDPROC...


//funcptr.cpp

//Switches WndProc via a function pointer...


#include <windows.h>

LRESULT CALLBACK MyProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK YourProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
WNDPROC WndProc;

LRESULT CALLBACK MyProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CLOSE:
DestroyWindow(hwnd);
break;

case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}

LRESULT CALLBACK YourProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_KEYUP:
MessageBox(NULL, "A key!", "you pressed", MB_OK);
WndProc=MyProc;
break;

case WM_CLOSE:
MessageBox(NULL, "Bye", "Bye", MB_OK);
DestroyWindow(hwnd);

case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc;
HWND hwnd;
MSG Msg;

WndProc=YourProc;

wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.lpszMenuName = NULL;
wc.lpszClassName = "ClassName";
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

if(!RegisterClassEx(&wc))
{
MessageBox(NULL, "Window Registration Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}

hwnd = CreateWindowEx(
WS_EX_CLIENTEDGE,
"ClassName",
"The title of my window",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 240, 120,
NULL, NULL, hInstance, NULL);

if(hwnd == NULL)
{
MessageBox(NULL, "Window Creation Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}

ShowWindow(hwnd, SW_MAXIMIZE);
UpdateWindow(hwnd);

while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return Msg.wParam;
}


[edited by - ranthalion on July 18, 2003 11:14:25 AM]
superpig
superpig
Oh, LOL.

When you register the window class, Windows stores the wndproc internally. Pointers that you change within your own app don''t affect that.

There are two ways you could do this:

1) Have a ''redirect'' wndproc. That one never changes, but it just passes messages onto a wndproc determined by a function pointer that you *can* set up.

2) Use SetWindowLong() to ''subclass'' the window - that is, change it''s internal WndProc pointer.

Superpig
- saving pigs from untimely fates, and when he''s not doing that, runs The Binary Refinery.
Enginuity1 | Enginuity2 | Enginuity3
Richard "Superpig" Fine - saving pigs from untimely fates - Microsoft DirectX MVP 2006/2007/2008/2009
"Shaders are not meant to do everything. Of course you can try to use it for everything, but it's like playing football using cabbage." - MickeyMouse
Ranthalion
Ranthalion
I wanted to avoid the extra function, otherwise I could just put an if/else statement in my WndProc. I tried the SetWindowLong function, but I can''t get it to compile. I have:
SetWindowLong(hwnd,GWL_WNDPROC,MyProc);
But when I try to compile, I recieve the errror:

funcptr.cpp(33) : error C2664: ''SetWindowLongA'' : cannot convert parameter 3 from ''long (__stdcall *)(struct HWND__ *,unsigned int,unsigned int,long)'' to ''long''
This conversion requires a reinterpret_cast, a C-style cast or function-style cast

Here are the function definitions of my 2 message handlers:

LRESULT CALLBACK MyProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK YourProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);

Am I doing something wrong here?
kspansel
kspansel
Yeah, superpig is right. If you want to dynamically change the window proc, you have to use SetWindowLong.

Your problem now is casting the pointer to a long:

SetWindowLong( hWnd, GWL_WNDPROC, (long)MyProc );
WNDPROC proc = (WNDPROC)GetWindowLong( hWnd, GWL_WNDPROC );

Probably should use reinterpret_cast:

SetWindowLong( hWnd, GWL_WNDPROC, reinterpret_cast( MyProc );

However I''ve had problems using reinterpret_cast when casting a pointer to a long with vs7, for some reason it wants an int64.

Kory
"The pioneers of a warless world are the youth who refuse military service" - Albert Einstein
Ranthalion
Ranthalion
Awesome! You guys are great. I just got home from work and I tried it out. reinterpret_cast kept giving me a problem with an extra ''('' until I googled on it an found I had to specify what to cast it to. I ended up using:
SetWindowLong(hwnd,GWL_WNDPROC,reinterpret_cast (MyProc));
It''s working just fine now.

As a follow up question, would the same thing work for changing the callback function on a Keyboard hook? I need to do the same thing to a hook, but for some reason, I don''t think this would work with the same call, but with the different handle...
kspansel
kspansel
Yeah, sorry about the reinterpret_cast, it's a templated function and I forgot the type. This is what it should be:

long lPointer = reinterpret_cast< long >( MyProc );

As for the keyboard hook, I don't have much experience in that area, but there is probably a better solution for what you are doing. I've read that hooks should be used sparsely because they can slow the system tremendously. Anyways, if you want to change a hook procedure, just remove the old hook and add the new one:

HHOOK hKbdHook = ... // Some valid keyboard hook

UnhookWindowsHookEx( hKbdHook );
hKbdHook = SetWindowsHookEx( WH_KEYBOARD, NewKeyboardProc, NULL, 0 );

Also note: If you are writing a system-wide keyboard hook, the hook procedure MUST be in a seperate dll.

kory spansel

[edited by - kspansel on July 19, 2003 2:07:47 AM]
"The pioneers of a warless world are the youth who refuse military service" - Albert Einstein
Ranthalion
Ranthalion
Again, thanks for all your help.

Topic Locked

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

Sign in to reply to this topic.