Sign in to follow this  
PatrickBateman

[C++ Winapi] SetWindowLong and modal dialog problem

Recommended Posts

Hi I've been writing my own winapi wrapper based on the article here on gamedev.net by Oluseyi Sonaiya. This is the code for the static callback handler using SetWindowLong/GetWindowLong:
LRESULT CALLBACK WinClass::WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	// On window creation, WindowProc receives lParam as a LPCREATESTRUCT 
	// Store *this* pointer as long in GWL_USERDATA
	if (msg == WM_NCCREATE)
		::SetWindowLong(hwnd, GWL_USERDATA, reinterpret_cast<long>(reinterpret_cast<LPCREATESTRUCT>(lParam)>lpCreateParams));
	WinClass *wnd = reinterpret_cast<WinClass*>(::GetWindowLong(hwnd, GWL_USERDATA));
	
        // Call the actual winproc function
	if (wnd)
		wnd->WndProc(hwnd, msg, wParam, lParam);

	return ::DefWindowProc(hwnd, msg, wParam, lParam);
}
This works fine but I cant figure out where to store pointers to the main wnd and a modal dialog class if i create instances of both. Im using the same wrapper code above for the modal dialog within a separate MDlgClass class. If I store the long pointer for the modal dialog in the GWL_USERDATA for the dialog, it works okay but acts as a modeless dialog not modal. I've tried using DWL_USER for the dialog ptr instead but without success. I thought about using a hashmap to map the pointers but I want to implement it within SetWindowLong. Is there another way to approach this or isn't it possible?

Share this post


Link to post
Share on other sites
I'm not sure about your problem with dialogs, but there's an error in that code you posted. You always call DefWindowProc, even when you call the WndProc method of the WinClass instance. You'll only want to call DefWindowProc if you don't have a pointer to a WinClass:


if (wnd)
return wnd->WndProc(hwnd, msg, wParam, lParam);
else
return ::DefWindowProc(hwnd, msg, wParam, lParam);

Share this post


Link to post
Share on other sites
Shouldn't the DefWindowProc be called for unhandled messages too?

In other words, something like this (pseudo)

if (wnd) {

retval = wnd->WndProc(hwnd, msg, wParam, lParam);

if ( retval == unhandled )
return ::DefWindowProc(hwnd, msg, wParam, lParam);
else
return retval;

} else

return ::DefWindowProc(hwnd, msg, wParam, lParam);

Share this post


Link to post
Share on other sites
Thanks for the help so far.

MJP, even after correcting the code to only call wndproc when I have pointer to a winclass, the dialog still doesnt return focus to the main window when I call EndDialog or DestroyWindow.

LessBread, I forgot to mention that the wndproc method contains a switch-case handle the messages and defaults to calling DefWindowProc

Any other ideas, I'm completely stuck!

message handler for winclass:

LRESULT CALLBACK WinClass::WndProc(HWND &hwnd, UINT &msg, WPARAM &wParam, LPARAM &lParam)
{
switch (msg)
{
case WM_COMMAND:
// virtual function overriden in classes derived from WinClass
OnCommand(hwnd, LOWORD(wParam), HIWORD(wParam));
break;
case WM_CLOSE:
::DestroyWindow(hwnd);
break;
case WM_DESTROY:
::PostQuitMessage(0);
break;
default:
return ::DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}


message handler for mdlgclass:

BOOL CALLBACK MDlgClass::DlgProc(HWND &hwnd, UINT &msg, WPARAM &wParam, LPARAM&)
{
switch (msg)
{
case WM_INITDIALOG:
break;
case WM_CLOSE:
EndDialog(hwnd, 0);
break;
case WM_COMMAND:
// virtual function overridden in classes derived from mdlgclass
OnCommand(hwnd, LOWORD(wParam));
break;
default:
return FALSE;
}
return TRUE;
}

Share this post


Link to post
Share on other sites
Dialogs don't receive WM_NCCREATE so if the code in the initial post is all you have in the window procedure, your DlgProc will never be called because wnd will never be set. It'll also be calling DefWindowProc which is a no-no for dialogs. Looks like you call DefWindowProc twice too, once for unhandled messages in WinClass::WndProc and then unconditionally at the end of WinClass::WindowProc, that's also something to avoid.

If you desire 64-bit portability, you'll need to use the Ptr versions of Set/GetWindowLong and change the cast type in the setting of the wnd. Something like this:


::SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(reinterpret_cast<LPCREATESTRUCT>(lParam)->lpCreateParams));

WinClass *wnd = reinterpret_cast<WinClass*>(::GetWindowLongPtr(hwnd, GWLP_USERDATA));




You'd also need to change the return type of your DlgProc to INT_PTR, rather than BOOL.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this