• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.
Sign in to follow this  
Followers 0
ATC

Why does CreateWindowW fail?

28 posts in this topic

I'm just doing a little practice with C++11 and using something I enjoy, DirectX11, to make it fun. And I decided to write a Win32 Window wrapper class to eliminate the need to rewrite Win32 window initialization code over and over every time I just want to make a simple DirectX11 program. But all of a sudden CreateWindowW just starts failing when I use my class's static method Window::Create, even though the Window initialization code is almost exactly the same as it was prior to creating the class -- which worked perfectly... I cannot figure out why but it keeps on returning a null HWND handle... Here is the code:

[b]gamewnd.h[/b]

[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 <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( );

HWND hWnd;
HINSTANCE hInstance;

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

private:
static LRESULT CALLBACK MsgRouter(
HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);
};


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

[b]gamewnd.cpp[/b]

[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;
}

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;

// 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!" );

pWindow->hWnd = hWnd;
pWindow->hInstance = hInstance; //! Store handles

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 );
}

// Routes messages to Window instances:
LRESULT CALLBACK Window::MsgRouter(HWND hWnd, UINT msg,
WPARAM wParam, LPARAM lParam) {

Window* Wnd = (Window *)0x00;

if( msg == WM_NCCREATE ) {
SetWindowLongPtr( hWnd, GWLP_USERDATA,
reinterpret_cast<LONG_PTR>( (LPCREATESTRUCT(lParam))->lpCreateParams) );
}

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 );
}
}

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

...for some reason CreateWindowW always returns null (0x00000000)! At first I thought maybe it was some invalid parameters, but I've stepped through in debug mode and cannot see anything wrong with them. I even switched some of the custom parameters to hard-coded values that I already know work in regular Win32 applications (e.g., the original version of this project) but it still fails.

For example, I just hard-coded the window title to L"MyWindow", changed the width/height params to 1024 and 768, changed the last parameter to NULL, etc... Still doesn't work, still returns a null HWND... :'(

Any idea what's going on here?

Thanks,

--ATC--

P.S. -- Just getting back into C++ to resharpen my old skills, so if you see any other coding problems or bad practices that don't have anything to do with the actual issue I welcome your corrections and suggestions...
0

Share this post


Link to post
Share on other sites
[b]NOTE::[/b]

I just call Window::Create in my wWinMain function like so:

Window* pWindow = Window::Create( 1024, 768, L"DirectX11 Tutorial", hInstance );
0

Share this post


Link to post
Share on other sites
From MSDN for CreateWindow:

[quote]If the function fails, the return value is NULL. To get extended error information, call GetLastError.[/quote]

Have you tried calling GetLastError to get more info?
0

Share this post


Link to post
Share on other sites
Your Window::MsgRouter function doesn't have a return statement for the code path when the WM_NCCREATE message is passed. Your compiler should have complained about that.

Chances are that the function happens to return zero, which is the response to the NCCREATE message to abort the window creation and return a null handle.
0

Share this post


Link to post
Share on other sites
[b]@ Brother Bob:[/b]

Ahhh, good eye... I didn't notice that. But I changed it to this:

[source lang="cpp"]// Routes messages to Window instances:
LRESULT CALLBACK Window::MsgRouter(HWND hWnd, UINT msg,
WPARAM wParam, LPARAM lParam) {

Window* Wnd = (Window *)0x00;

if( msg == WM_NCCREATE ) {
SetWindowLongPtr( hWnd, GWLP_USERDATA,
reinterpret_cast<LONG_PTR>( (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 );
}
}[/source]

...now the code does not crash but I don't get a proper window. I just get a borderless square on the screen without any control buttons/boxes or anything. Just a blank square client area it appears.

[b]EDIT:[/b]

It appears this is happening because my window never recieves a WM_PAINT... every time WndProc is invoked it seems that msg == 0x00... ?

[b]EDIT 2:[/b]

Or is the above happening (no WM_PAINT) because the window is occluded/minimized as I debug the application?

[b]EDIT 3 (lol):[/b]

Yes, that's why... so the window IS getting WM_PAINT, leaving me with no idea why it's all screwed up... [img]http://public.gamedev.net//public/style_emoticons/default/tongue.png[/img]

BTW, this is a [i]very[/i] odd Windows behavior if you ask me... Edited by ATC
0

Share this post


Link to post
Share on other sites
I see an ambiguity, but I am not sure how compilers handle this, so I could be wrong.

You mean to set the hInstance of the window class to the argument from Window::Create. However, you also have a NULL hInstance member variable. If the NULL variable is being substituted for the function argument, then you'll see issues.

Try changing the variable names to remove the ambiguity or move "pWindow->hInstance = hInstance;" immediately prior to setting up wndClass.

Again, I could be wrong, but this is the only thing that jumps out at me.
0

Share this post


Link to post
Share on other sites
[quote name='MarkS' timestamp='1350682010' post='4991941']
I see an ambiguity, but I am not sure how compilers handle this, so I could be wrong.

You mean to set the hInstance of the window class to the argument from Window::Create. However, you also have a NULL hInstance member variable. If the NULL variable is being substituted for the function argument, then you'll see issues.

Try changing the variable names to remove the ambiguity or move "pWindow->hInstance = hInstance;" immediately prior to setting up wndClass.

Again, I could be wrong, but this is the only thing that jumps out at me.
[/quote]
Name resolution is well defined. Function parameters have higher priority than members, so that is not a problem.

Anyway, I have copied your code and I truly cannot see how your code flow is different from mine, yet mine works and your behaves just as you describe. I will see if I can spend some more time later and investigate some more if you haven't found a solution. My observation so far is that the window becomes OK after a while, you just need to, for example, minimize and maximize the window and it starts behaving as expected.
1

Share this post


Link to post
Share on other sites
[quote name='Brother Bob' timestamp='1350683320' post='4991952']
Name resolution is well defined. Function parameters have higher priority than members, so that is not a problem.

[/quote]

Thanks for the clarification.
0

Share this post


Link to post
Share on other sites
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. Edited by adeyblue
0

Share this post


Link to post
Share on other sites
[b]@ Brother Bob:[/b]

You're saying the same exact code works on your PC? [img]http://public.gamedev.net//public/style_emoticons/default/ohmy.png[/img]
0

Share this post


Link to post
Share on other sites
[quote name='adeyblue' timestamp='1350684479' post='4991961']
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.
[/quote]

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...
0

Share this post


Link to post
Share on other sites
[quote name='ATC' timestamp='1350684591' post='4991962']
[b]@ Brother Bob:[/b]

You're saying the same exact code works on your PC? [img]http://public.gamedev.net//public/style_emoticons/default/ohmy.png[/img]
[/quote]
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.
0

Share this post


Link to post
Share on other sites
[quote name='Brother Bob' timestamp='1350684866' post='4991965']
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.
[/quote]

If it's not some sort of proprietary code would you mind showing me for me to examine and correct this problem?
0

Share this post


Link to post
Share on other sites
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.
0

Share this post


Link to post
Share on other sites
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.
[code]
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
}
[/code]
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.
[code]
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 );
}
}
[/code]
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. Edited by Brother Bob
1

Share this post


Link to post
Share on other sites
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...

"[i]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[/i]"

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... Edited by ATC
0

Share this post


Link to post
Share on other sites
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...
0

Share this post


Link to post
Share on other sites
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:

[b]gamewnd.h ::[/b]

[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]

[b]gamewnd.cpp ::[/b]

[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--

[b]EDIT:[/b]

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:

[i]#include <atc_standards.h>
#include <Windows.h>
#include <list>
#include <string>
#include <stdexcept>[/i] Edited by ATC
0

Share this post


Link to post
Share on other sites
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! [img]http://public.gamedev.net//public/style_emoticons/default/sleep.png[/img]

Any ideas?

[b]EDIT:[/b]

Even more odd is that my Window gets the WM_SETTEXT message if I call SetWindowText, but still, nothing happens... the text remains untouched... Edited by ATC
0

Share this post


Link to post
Share on other sites
Probably irrelevant, but you are passing messages to DefWindowProc() only if they are not in the switch statement, and returning 0 for all messages in the switch statement. This requires that you check that 0 is the expected return value for each and every message you handle in the switch statement. Instead, forget about "default:", and have the function return the value returned from DefWindowProc(), and explicitly return 0 or whatever is appropriate for messages which you process and which you don't want DefWindowProc() to process. So you get this:

[CODE]
switch( Msg )
{
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(&ps);
this->OnPaint( hdc );
EndPaint(&ps);
}
return 0; // We've already handled it and we don't want to pass it to DefWindowProc().

case WM_SIZE:
// Here lies code which we want to execute whenever the window is resized,
// but we still want DefWindowProc() to handle the message, so we break.
this->OnSize();
break;
}

return DefWindowProc();
[/CODE]

Of course, you could still replace your "break;" statements with a "return DefWindowProc(...);" statement, but that seems counter-intuitive to me.

There is an somewhat old article which discusses coupling a C++ object with a native window handle (HWND) [url="http://angelcode.com/dev/window2/window2.html"]here[/url]. It's one of the better ways to do it. The finished code is in what he calls the [url="http://angelcode.com/toolbox/"]AngelCode Tool Box[/url], and the files most relevant to this discussion are acWindow.h and acWindow.cpp which you can find [url="https://actb.svn.sourceforge.net/svnroot/actb/trunk/source/win/"]here[/url].

Notice that WM_NCDESTROY is guaranteed to be the last message that a window will receive from the system, WM_NCCREATE is not the first message it will receive. I think the first message received by a window's message processor is WM_NCCALCSIZE, but I don't think it says so in the docs, so it can change, so good code will not depend on that piece of information. The article I linked above talks about how to do it by setting a hook procedure that gets called whenever a window is created, creating the window (and consequently the hook procedure that will do the coupling), then removing the hook procedure.

Good luck.
0

Share this post


Link to post
Share on other sites
Thanks Arm0. I did already get my Window class working though, but this is good information and thanks for the links!

My problem now is explained on the last post of page 1... SetWindowTextW/A will not work... they don't do anything. And it's driving me crazy! :P
0

Share this post


Link to post
Share on other sites
SetWindowText will fail if:
* The supplied window handle is invalid. Maybe you're passing the wrong handle somehow. Check which window you are passing by retrieving the window text and displaying it in, say, the debug output window (see [url="http://msdn.microsoft.com/en-us/library/windows/desktop/aa363362(v=vs.85).aspx"]OutputDebugString()[/url]).
* The WM_SETTEXT message is not being passed to DefWindowProc(), which will do what is necessary to change the window's text.
0

Share this post


Link to post
Share on other sites
@ Arm0: No, my handle is valid. I will re-describe the problem:

Does anyone know why SetWindowText W/A does not work? No matter what I do or what variation of parameters and such I try nothing happens...

HWND hWnd;

//! All of these get same, valid HWND handle
hWnd = GetActiveWindow();
hWnd = FindWindow( null, WINDOW_TITLE );
hWnd = FindWindow( WND_CLASS_NAME, null );
hWnd = FindWindow( WND_CLASS_NAME, WINDOW_TITLE );
hWnd = pWindow->hWnd; // << made public for testing
SetWindowTextW( hWnd, L"TESTING 123" ); // nothing happens...
SetWindowTextA( hWnd, "TESTING 123" ); // nothing happens...


I've confirmed my HWND handle is valid. Using it I can call other Win32 functions and easily move around or resize the window or do any number of other things. And as I show above, I've tested my Window class internal HWND against GetActiveWindow and FindWindow to make sure I've got the right handle and yes, they return an identical handle. So the the HWND IS valid.
Calling the SetWindowText function returns a value of true/1 for success, and the Window recieves the WM_SETTEXT message which I can intercept and update my Window's "title" field, like so:

[source lang="cpp"] case WM_SETTEXT:
this->title = wstring( reinterpret_cast<WCHAR_STR>(lParam) );
break;[/source]


...I've run in debug mode and the text I get on the WM_SETTEXT message matches the text I use in SetWindowText calls, demonstrating that everything about this seems to be working correctly except for one incredibly annoying fact: nothing happens and the text in the caption/title bar remains untouched! *double facepalm*
What the hell is going on here? This is serious frustrating... [img]http://public.gamedev.net//public/style_emoticons/default/sleep.png[/img]

Thanks,

--ATC-- Edited by ATC
0

Share this post


Link to post
Share on other sites
Try replacing the message processor code in your window class with
[CODE]
LRESULT MsgProc( UINT Msg, WPARAM wParam, LPARAM lParam ) { return DefWindowProc( this->hWnd, Msg, wParam, lParam ); }
[/CODE]
0

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  
Followers 0