Why does CreateWindowW fail?

Started by
27 comments, last by MarkS_ 11 years, 6 months ago
@ Brother Bob:

You're saying the same exact code works on your PC? ohmy.png
_______________________________________________________________________________
CEO & Lead Developer at ATCWARE™
"Project X-1"; a 100% managed, platform-agnostic game & simulation engine

Please visit our new forums and help us test them and break the ice!
___________________________________________________________________________________
Advertisement

You (OP) have a problem everybody seems to run into when doing this at some point.
You call CreateWindow: this->hwnd == NULL
CreateWindow sends WM_NCCreate - you set the window data, this->hwnd still == NULL
CreateWindow sends the other messages it sends - you call Wnd->WndProc, you pass this->hwnd to DefWindowProc but this->hwnd is still NULL
CreateWindow finally returns, after a check only now is this->hwnd set to a valid value

Long story short, you handle who knows how many messages by passing a NULL hwnd to DefWindowProc or to Begin/EndPaint.
The easiest way to fix it is to assign this->hwnd in WM_NCCreate.


I tried doing that too and it didn't work, I seem to remember... And then there's the issue that you can't get the Window* on the WM_NCCreate message anyway, so not sure how you could actually do this correctly...
_______________________________________________________________________________
CEO & Lead Developer at ATCWARE™
"Project X-1"; a 100% managed, platform-agnostic game & simulation engine

Please visit our new forums and help us test them and break the ice!
___________________________________________________________________________________

@ Brother Bob:

You're saying the same exact code works on your PC? ohmy.png

I see how I expressed myself incorrectly. What I meant was that I compared your code with my own window code and I cannot see any difference in the basic structure of the code. When I run your code it works as you describe (in other words; doesn't work), but my own code works, so apparently there's something different that I just cannot see even when I compare the codes side by side.

I see how I expressed myself incorrectly. What I meant was that I compared your code with my own window code and I cannot see any difference in the basic structure of the code. When I run your code it works as you describe (in other words; doesn't work), but my own code works, so apparently there's something different that I just cannot see even when I compare the codes side by side.


If it's not some sort of proprietary code would you mind showing me for me to examine and correct this problem?
_______________________________________________________________________________
CEO & Lead Developer at ATCWARE™
"Project X-1"; a 100% managed, platform-agnostic game & simulation engine

Please visit our new forums and help us test them and break the ice!
___________________________________________________________________________________
It is by no means proprietary or closed, but it's a part of a bigger windowing system where I can't easily rip out the relevant parts without going through some editing for it to make sense by itself. Not trying to tell you to STFW, but it's probably quicker to just find some standard window tutorial code and compare to it. My way of doing it is really no different than any other code you can find. But I'll keep looking at it.
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.
I can't get either of those methods to work...

The first way won't even compile because of this line:

SetWindowLongPtr(hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(WndProc));

WndProc is member function of type Window, and no matter what I do I cannot obtain/use its address in calling SetWindowLongPtr... doesn't make sense to me...

"Error 1 error C2440: 'reinterpret_cast' : cannot convert from 'LRESULT (__cdecl Window::* )(UInt32,WPARAM,LPARAM)' to 'LONG_PTR' C:\Users\ATC\Documents\Visual Studio 2012\Projects\D3D11Tutorial01\D3D11Tutorial01\gamewnd.cpp 142 1 D3D11Tutorial01"

The second way compiles and runs but no window is created or show at all... just a loop running and I have to click Stop Debugging to exit...
_______________________________________________________________________________
CEO & Lead Developer at ATCWARE™
"Project X-1"; a 100% managed, platform-agnostic game & simulation engine

Please visit our new forums and help us test them and break the ice!
___________________________________________________________________________________
Well I think I finally got it... gonna clean my code up a little bit, double-check the results and I will post me code...
_______________________________________________________________________________
CEO & Lead Developer at ATCWARE™
"Project X-1"; a 100% managed, platform-agnostic game & simulation engine

Please visit our new forums and help us test them and break the ice!
___________________________________________________________________________________
And here is the (now) working implementation -- though it lacks an implementation of methods to manipulate the actual window once created. This is just the "framework" of it to create a window, route messages to the proper instance and have a virtual WndProc derrived classes can override to do custom message handling... Anyways, here's the working code:

gamewnd.h ::

[source lang="cpp"]
#pragma region Description
/* gamewnd.h ::
* Implements a game window class for DirectX applications.
*
* Copyright © ATCWARE 2010, All Rights Reserved.
*/
#pragma endregion

/*----------------------------------
* INCLUDE DIRECTIVES ::
*----------------------------------*/

#pragma once
#define WIN32_LEAN_AND_MEAN

#include <atc_standards.h>
#include <Windows.h>
#include <list>
#include <string>
#include <stdexcept>

using namespace std;

/*----------------------------------
* PREPROCESSOR DIRECTIVES ::
*----------------------------------*/

#define WND_CLASS_NAME_W L"GameWndClass"
#define MAX_WINDOW_WIDTH 0x1000
#define MAX_WINDOW_HEIGHT 0x1000

/*-----------------------------------------------------------------------------*
* GLOBAL VARIABLES ::
*-----------------------------------------------------------------------------*/



/*-----------------------------------------------------------------------------*
* PROTOTYPES & FORWARD DECLARATIONS ::
*-----------------------------------------------------------------------------*/

enum WndState
{
DEFAULT = 0x00,
MAXIMIZED = 0x01,
MINIMIZED = 0x02,
};

class Window
{
public:
virtual void Show( );
virtual void Hide( );
virtual void Close( );

static void ClientToAbsSize( Int32* const pWidth, Int32* const pHeight );
static Window* Create( Int32 width, Int32 height, wstring title, HINSTANCE hInstance );

protected:
Window( );
~Window( );

HWND hWnd;
HINSTANCE hInstance;

virtual LRESULT WndProc( UInt32 msg, WPARAM wParam, LPARAM lParam );

private:
static LRESULT CALLBACK MsgRouter( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam );
};

/*-----------------------------------------------------------------------------*
*******************************************************************************
*-----------------------------------------------------------------------------*/
[/source]

gamewnd.cpp ::

[source lang="cpp"]
#pragma region Description
/* gamewnd.cpp ::
*
*
* Copyright © ATCWARE 2010, All Rights Reserved.
*/
#pragma endregion

/*----------------------------------
* INCLUDE DIRECTIVES ::
*----------------------------------*/

#include "gamewnd.h"

/*----------------------------------
* PREPROCESSOR DIRECTIVES ::
*----------------------------------*/



/*-----------------------------------------------------------------------------*
* GLOBAL VARIABLES ::
*-----------------------------------------------------------------------------*/



/*-----------------------------------------------------------------------------*
* GameWndBase Implementation ::
*-----------------------------------------------------------------------------*/

Window::Window( ) {
this->hInstance = 0x00;
this->hWnd = 0x00;
}

Window::~Window( ) {

this->Close();
}

void Window::Show( ) {
ShowWindow( this->hWnd, SHOW_OPENWINDOW );
}

void Window::Hide( ) {
ShowWindow( this->hWnd, HIDE_WINDOW );
}

void Window::Close( ) {
CloseWindow( this->hWnd );
}

void Window::ClientToAbsSize( Int32* const pWidth, Int32* const pHeight ) {

RECT rc = { 0, 0, *pWidth, *pHeight };

AdjustWindowRect( &rc, WS_OVERLAPPEDWINDOW, FALSE );
*pWidth = (rc.right - rc.left);
*pHeight = (rc.bottom - rc.top);
}

Window* Window::Create( Int32 width, Int32 height, wstring title, HINSTANCE hInstance ) {

#if DEBUG || PERFORM_CHECKS //! Validate arguments
if( (width < 0) || (width > MAX_WINDOW_WIDTH) )
throw new invalid_argument( "Specified Window width out of range." );

if( (height < 0) || (height > MAX_WINDOW_HEIGHT) )
throw new invalid_argument( "Specified Window width out of range." );

if( !(title.data()) || (title.length() < 1) )
throw new invalid_argument( "Invalid Window title parameter." );

if( !hInstance )
throw new invalid_argument( "HINSTANCE parameter is NULL." );
#endif

Window* pWindow = new Window;
pWindow->hInstance = hInstance; //! Store handle

// Create & register window class ::
WNDCLASSEX wndClass = { 0 };
wndClass.cbSize = sizeof( WNDCLASSEX );
wndClass.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
wndClass.lpfnWndProc = Window::MsgRouter;
wndClass.hCursor = LoadCursor( null, IDC_ARROW );
wndClass.style = (CS_VREDRAW | CS_HREDRAW);
wndClass.hInstance = hInstance;
wndClass.lpszMenuName = null;
wndClass.lpszClassName = WND_CLASS_NAME_W;

if( !RegisterClassEx( &wndClass ) )
throw new runtime_error( "Window registration failed!" );

// Calculate absolute window size ::
Int32 abs_width = width, abs_height = height;
ClientToAbsSize( &abs_width, &abs_height );

// Create the Win32 window ::
HWND hWnd = CreateWindowW( WND_CLASS_NAME_W, title.data(), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
abs_width, abs_height, null, null, hInstance, static_cast<LPVOID>( pWindow ) );

// Show the game window ::
if( !hWnd )
throw new runtime_error( "Window creation failed!" );

return pWindow;
}

LRESULT Window::WndProc( UInt32 msg, WPARAM wParam, LPARAM lParam ) {

HDC hDC;
PAINTSTRUCT paintStruct;

switch( msg )
{
case WM_PAINT:
hDC = BeginPaint( this->hWnd, &paintStruct );
EndPaint( this->hWnd, &paintStruct );
break;

case WM_DESTROY:
PostQuitMessage( 0x00 );
break;

default:
return DefWindowProc( this->hWnd, msg, wParam, lParam );
}

return static_cast<LRESULT>( 0x00 );
}

LRESULT CALLBACK Window::MsgRouter( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam ) {

#if DEBUG || PERFORM_CHECKS
if( !hWnd )
throw invalid_argument( "Null window handle!" );
#endif

auto wnd = (Window *)null;

if( Msg == WM_NCCREATE )
{
// Get the Window pointer from lpCreateParams ::
wnd = reinterpret_cast<Window *>( ((LPCREATESTRUCT)lParam)->lpCreateParams );

// Save our Window pointer and the valid HWND ::
SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<long>(wnd));
wnd->hWnd = hWnd;
}

else // Get Window pointer from our window info ::
wnd = reinterpret_cast<Window *>( GetWindowLongPtrW( hWnd, GWLP_USERDATA ) );

return wnd->WndProc(Msg, wParam, lParam); //! Invoke Window::WndProc
}

/*-----------------------------------------------------------------------------*
*******************************************************************************
*-----------------------------------------------------------------------------*/
[/source]

Enjoy! BTW, I know it says "Copyright" in the description section but that's just because my default .h, .cpp, .c, etc file templates automatically format the code files that way. So disregard the copyright notice; you're free to use this code.

Regards,

--ATC--

EDIT:

For some reason the code block feature on the forums makes anything enclosed in <> braces disappear, so it screws up C/C++ include directives and C# generics, for example. The includes for gamewnd.h are:

#include <atc_standards.h>
#include <Windows.h>
#include <list>
#include <string>
#include <stdexcept>
_______________________________________________________________________________
CEO & Lead Developer at ATCWARE™
"Project X-1"; a 100% managed, platform-agnostic game & simulation engine

Please visit our new forums and help us test them and break the ice!
___________________________________________________________________________________
Now that I got that WndProc problem solved I've got a pretty good Window class coming along... But now I'm having a REALLY stupid problem that makes no sense at all...

The SetWindowTextW and SetWindowTextA functions flat out don't work... they do NOTHING... And just to make sure it wasn't a bad HWND handle at fault I tried this:

SetWindowPos( hWnd, null, 700, 700, width, height, null );

...and it worked exactly as expected! So I know my handle is good. But why won't SetWindowText work? I've used this function a million times in the past, even called in from C# with P/Invoke and it always worked. Why is it not working now? Even if I just hard-code something like:

SetWindowTextW( hWnd, L"WTF!?" );

...even that doesn't work! sleep.png

Any ideas?

EDIT:

Even more odd is that my Window gets the WM_SETTEXT message if I call SetWindowText, but still, nothing happens... the text remains untouched...
_______________________________________________________________________________
CEO & Lead Developer at ATCWARE™
"Project X-1"; a 100% managed, platform-agnostic game & simulation engine

Please visit our new forums and help us test them and break the ice!
___________________________________________________________________________________

This topic is closed to new replies.

Advertisement