Jump to content

  • Log In with Google      Sign In   
  • Create Account


#ActualBrother Bob

Posted 19 October 2012 - 05:49 PM

Ok, good news and bad news... Good news is I've found a solution. Bad news is I don't know why.

Joke aside, I found a difference: you set the window pointer when you receive the NCCREATE message, but I change it on the CREATE message. The NC-messages are sent before the non-NC messages to pass non-client (NC) messages to your window before the client are is set up. It could be that the window simply don't have any storage to store your pointer at that time, or that you are not allowed to store data there yet. In any case, I don't know why this makes a difference becuase you proceed to call DefWindowProc in the same order as me as far as I can see. Simply changing the pointer setup on the CREATE message instead of on the NCCREATE message seems to work just fine. You have to write your message router a bit different though.

I do it using two separate window handlers: one for the initial NC-messages and then I change handler once the CREATE message arrives.
LRESULT CALLBACK messagehandler::WndProcNC(HWND hwnd, UINT wmsg, WPARAM wparam, LPARAM lparam)
{
	if(wmsg == WM_CREATE) {
		LPCREATESTRUCT cs = reinterpret_cast<LPCREATESTRUCT>(lparam);
		window *win = reinterpret_cast<window *>(cs->lpCreateParams);
  
		SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(win));
		SetWindowLongPtr(hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(WndProc));
		return WndProc(hwnd, wmsg, wparam, lparam);
	} else {
		return DefWindowProc(hwnd, wmsg, wparam, lparam);
	}
}

LRESULT CALLBACK messagehandler::WndProc(HWND hwnd, UINT wmsg, WPARAM wparam, LPARAM lparam)
{
	// handle window messages here as usual
}
The window class structure is registered with the WndProcNC function which processes NC-messages until CREATE is received. At that point, the window pointer is fetched from the create parameter and put into the window data, the window's message handler is swapped to WndProc, and finally the message is passed manually to WndProc for further processing.

This method brings me to a problem with your method. If you just change it to set the window pointer on the CREATE message instead of the NCCREATE message, you will fall through the first if-statement and call you windows message handler on a null-pointer (or rather, throw the exception in that code path). You can either use my method and use separate message handlers for windows with and without pointer data (since the pointer data is set and the window handler is changed at the same time), or add some extra logic to your own method.
LRESULT CALLBACK Window::MsgRouter(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	Window* Wnd = reinterpret_cast<Window *>( GetWindowLongPtr( hWnd, GWLP_USERDATA ) );
	if(Wnd == nullptr) {
		if( msg == WM_CREATE ) {
			SetWindowLongPtr( hWnd, GWLP_USERDATA, reinterpret_cast<LONG>( (LPCREATESTRUCT(lParam))->lpCreateParams) );
		}
	  
		return DefWindowProc(hWnd, msg, wParam, lParam);
	}
	else
	{
	    if( !Wnd )
			throw new runtime_error( "No Window instance associated with HWND!" );
		
		return Wnd->WndProc( msg, wParam, lParam );
	}
}
The idea is the same as my method, as long as the window pointer is null, then you process messages in the if-statement; otherwise you have a window pointer and you process it in the else-statement.

#1Brother Bob

Posted 19 October 2012 - 05:45 PM

Ok, good news and bad news... Good news is I've found a solution. Bad news is I don't know why.

Joke aside, I found a difference: you set the window pointer when you receive the NCCREATE message, but I change it on the CREATE message. The NC-messages are sent before the non-NC messages to pass non-client (NC) messages to your window before the client are is set up. It could be that the window simply don't have any storage to store your pointer at that time, or that you are not allowed to store data there yet. In any case, I don't know why this makes a difference becuase you proceed to call DefWindowProc in the same order as me as far as I can see. Simply changing the pointer setup on the CREATE message instead of on the NCCREATE message seems to work just fine. You have to write your message router a bit different though.

I do it using two separate window handlers: one for the initial NC-messages and then I change handler once the CREATE message arrives.
LRESULT CALLBACK messagehandler::WndProcNC(HWND hwnd, UINT wmsg, WPARAM wparam, LPARAM lparam)
{
    if(wmsg == WM_CREATE) {
	    LPCREATESTRUCT cs = reinterpret_cast<LPCREATESTRUCT>(lparam);
	    window *win = reinterpret_cast<window *>(cs->lpCreateParams);
   
	    SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(win));
	    SetWindowLongPtr(hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(WndProc));
	    return WndProc(hwnd, wmsg, wparam, lparam);
    } else {
	    return DefWindowProc(hwnd, wmsg, wparam, lparam);
    }
}

LRESULT CALLBACK messagehandler::WndProc(HWND hwnd, UINT wmsg, WPARAM wparam, LPARAM lparam)
{
    // handle window messages here as usual
}
The window class structure is registered with the WndProcNC function which processes NC-messages until CREATE is received. At that point, the window pointer is fetched from the create parameter and put into the window data, the window's message handler is swapped to WndProc, and finally the message is passed manually to WndProc for further processing.

This method brings me to a problem with your method. If you just change it to set the window pointer on the CREATE message instead of the NCCREATE message, you will fall through the first if-statement and call you windows message handler on a null-pointer (or rather, throw the exception in that code path). You can either use my method and use separate message handlers for windows with and without pointer data (since the pointer data is set and the window handler is changed at the same time), or add some extra logic to your own method.
LRESULT CALLBACK Window::MsgRouter(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    Window* Wnd = reinterpret_cast<Window *>( GetWindowLongPtr( hWnd, GWLP_USERDATA ) );
    if(Wnd == nullptr) {
	    if( msg == WM_CREATE ) {
		    SetWindowLongPtr( hWnd, GWLP_USERDATA, reinterpret_cast<LONG>( (LPCREATESTRUCT(lParam))->lpCreateParams) );
	    }
	   
	    return DefWindowProc(hWnd, msg, wParam, lParam);
    }
    else
    {
	    Wnd = reinterpret_cast<Window *>( GetWindowLongPtr( hWnd, GWLP_USERDATA ) );

	    if( !Wnd )
		    throw new runtime_error( "No Window instance associated with HWND!" );
		
	    return Wnd->WndProc( msg, wParam, lParam );
    }
}
The idea is the same as my method, as long as the window pointer is null, then you process messages in the if-statement; otherwise you have a window pointer and you process it in the else-statement.

PARTNERS