4

I'm following the NeHe gamedev tutorials (while altering them to be OO) and I've run into a problem with the CreateWindowEx demo (http://nehe.gamedev.net/tutorial/creating_an_opengl_window_(win32)/13001/).

I'm trying to pass the WndProc a pointer to my Window object via the lpParam (as detailed here: http://web.archive.org/web/20051125022758/www.rpi.edu/~pudeyo/articles/wndproc/) but if I attempt to do so, CreateWindowEx fails with GetLastError returning 1400 - ERROR_INVALID_WINDOW_HANDLE.

I'm a complete beginner at the windows API and have exhausted every method I know of resolving this, please could you point out my mistake here? Relevant code below:

LRESULT CALLBACK cog::WindowProc(HWND window, UINT msg, WPARAM wParam, LPARAM lParam) {
    // Member method as windowproc: http://web.archive.org/web/20051125022758/www.rpi.edu/~pudeyo/articles/wndproc/

    if(msg == WM_NCCREATE) {
        LPCREATESTRUCT cs = (LPCREATESTRUCT)lParam;
        SetWindowLong(window, GWL_USERDATA, (long)cs->lpCreateParams);
    }

    cog::Window* w = (cog::Window*)GetWindowLong(window, GWL_USERDATA);
    if(w) {
        return w->windowProc(msg, wParam, lParam);

    } else {
        return DefWindowProc(window, msg, wParam, lParam);
    }
}

cog::Window::Window(int width, int height, int bits, bool fullscreen) :
fullscreen(fullscreen), appInstance(GetModuleHandle(NULL)), active(FALSE) {

    // Generate a rectangle corresponding to the window size
    RECT winRect = {0, 0, width, height};

    WNDCLASS winClass;
     winClass.style         = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;   // Redraw On Size, And Own DC For Window.
    winClass.lpfnWndProc    = (WNDPROC) cog::WindowProc;            // WndProc Handles Messages
    winClass.cbClsExtra     = 0;                                    // No Extra Window Data
    winClass.cbWndExtra     = sizeof(this);                         // Window Data - pointer to Window object
    winClass.hInstance      = this->appInstance;                    // Set The Instance
    winClass.hIcon          = LoadIcon(NULL, IDI_WINLOGO);          // Load The Default Icon
    winClass.hCursor        = LoadCursor(NULL, IDC_ARROW);          // Load The Arrow Pointer
    winClass.hbrBackground  = NULL;                                 // No Background Required For GL
    winClass.lpszMenuName   = NULL;                                 // We Don't Want A Menu
    winClass.lpszClassName  = TEXT("OpenGL");   

    if(!RegisterClass(&winClass)) {
        throw cog::WindowException(std::string("Failed to register class"));
    }

    if(this->fullscreen) {
        DEVMODE screenSettings;
        memset(&screenSettings, 0, sizeof(DEVMODE));

        screenSettings.dmSize = sizeof(DEVMODE);
        screenSettings.dmPelsWidth = width;
        screenSettings.dmPelsHeight = height;
        screenSettings.dmBitsPerPel = bits;
        screenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;

        if(DISP_CHANGE_SUCCESSFUL != ChangeDisplaySettings(&screenSettings, CDS_FULLSCREEN)) {
            if(MessageBox(NULL, "Cannot start in full screen mode - start in windowed mode instead?", "OpenGL", MB_YESNO | MB_ICONEXCLAMATION)) {
                this->fullscreen = FALSE;

            } else {
                throw cog::WindowException(std::string("Refused to launch program in windowed mode"));

            }
        }
    }

    DWORD winExStyle;
    DWORD winStyle;
    if(fullscreen) {
        winExStyle = WS_EX_APPWINDOW;
        winStyle = WS_POPUP;
        ShowCursor(FALSE);

    } else {
        winExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
        winStyle = WS_OVERLAPPEDWINDOW;
    }

    AdjustWindowRectEx(&winRect, winStyle, FALSE, winExStyle);

    /*
     * !! BLOWS UP AT THIS CALL - WindowException triggered
     */
    if(!(this->window = CreateWindowEx(
    winExStyle,
    TEXT("OpenGL"),
    TEXT("OpenGL Testing"),
    winStyle,
    0, 0,
    winRect.right - winRect.left,
    winRect.bottom - winRect.top,
    NULL,
    NULL,
    this->appInstance,
    this))) {
        throw cog::WindowException(std::string("Failed to create window"));
    }

    // ... cut here ...
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE lPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
    MSG msg;
    cog::Window* w = NULL;

    try {
        w = new cog::Window(100, 100, 16, TRUE);

        // ... cut here ...

    } catch(cog::Exception e) {
        MessageBox(NULL, e.what(), "Exception Raised", MB_OK | MB_ICONEXCLAMATION);
    }

    if(w) {
        delete w;
    }
}

The member windowProc:

LRESULT CALLBACK cog::Window::windowProc(UINT msg, WPARAM wParam, LPARAM lParam) {
    switch(msg) {
    case WM_ACTIVATE:
        if(HIWORD(wParam)) {
            this->active = FALSE;

        } else {
            this->active = TRUE;
        }

        return 0;;

    case WM_SYSCOMMAND:
        switch(wParam) {
        case SC_SCREENSAVE:
        case SC_MONITORPOWER:
            return 0;
        }

        break;

    case WM_CLOSE:
        PostQuitMessage(0);
        return 0;

    case WM_KEYDOWN:
        this->keys[wParam] = TRUE;
        break;

    case WM_KEYUP:
        this->keys[wParam] = FALSE;
        break;

    case WM_SIZE:
        this->resize(LOWORD(lParam), HIWORD(lParam));
        return 0;

    default:
        break;
    }

    return DefWindowProc(this->window, msg, wParam, lParam);
}
Alex Jeffrey
  • 523
  • 6
  • 17
  • can you post your declaration of your static WindProc and the Class member WndProc in your header file? – cppanda Dec 25 '12 at 02:27
  • The static WindowProc was already there, but I've edited in the member windowProc now – Alex Jeffrey Dec 25 '12 at 03:18
  • do you have a declaration of the static WndProc in a header file in the cog namespace? if not, try without the namespace in the cpp file – cppanda Dec 25 '12 at 03:23

1 Answers1

2

Could the fact that you're requesting fullscreen be causing a problem? w = new cog::Window(100, 100, 16, TRUE);

If it helps, this works in my code base:

HWND impl::window_impl::create_window_(
    window_impl* window // associated window object
) {
    auto const INSTANCE = ::GetModuleHandleW(L"");

    WNDCLASSEXW const wc = {
        sizeof(WNDCLASSEXW),
        CS_OWNDC | CS_HREDRAW | CS_VREDRAW,
        window_impl::top_level_wnd_proc_,
        0,
        0,
        INSTANCE,
        nullptr,
        ::LoadCursorW(nullptr, MAKEINTRESOURCE(IDC_ARROW)),
        nullptr,
        nullptr,
        CLASS_NAME,
        nullptr
    };

    ::RegisterClassExW(&wc); // ignore return value

    auto const result = ::CreateWindowExW(
        0,
        CLASS_NAME,
        L"window",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT,
        CW_USEDEFAULT, CW_USEDEFAULT,
        (HWND)nullptr ,
        (HMENU)nullptr,
        INSTANCE,
        window
    );

    return result;
}

[edit] I think your call to CreateWindowExW has the params in the wrong order -- specifically the instance paramater. Do you compile with STRICT on? It should detect this kind of problem.

[edit] Not directly related, but your implementation won't work when compiled as 64bit code, and it doesn't check for possible errors -- you should use something like:

//--------------------------------------------------------------------------
//! Get the userdata for the window given by @c hwnd (our window object).
//! @throw bklib::platform::windows_exception
//--------------------------------------------------------------------------
impl::window_impl* get_window_ptr(HWND hwnd) {
    ::SetLastError(0);
    auto const result = ::GetWindowLongPtrW(hwnd, GWLP_USERDATA);

    if (result == 0) {
        auto const e = ::GetLastError();
        if (e) {
            BOOST_THROW_EXCEPTION(bklib::platform::windows_exception()
                << bklib::platform::windows_error_code(e)
            );
        }
    }

    return reinterpret_cast<impl::window_impl*>(result);
}

//--------------------------------------------------------------------------
//! Set the userdata for the window given by @c hwnd to be our
//! window object.
//! @throw bklib::platform::windows_exception
//--------------------------------------------------------------------------
void set_window_ptr(HWND hwnd, impl::window_impl* ptr) {
    ::SetLastError(0);
    auto const result = ::SetWindowLongPtrW(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(ptr));

    if (result == 0) {
        auto const e = ::GetLastError();
        if (e) {
            BOOST_THROW_EXCEPTION(bklib::platform::windows_exception()
                << bklib::platform::windows_error_code(e)
            );
        }
    }
}

[edit] More code in case it helps

//------------------------------------------------------------------------------
//! Top level window procedure which forwards messages to the appropriate
//! impl::window_impl instance.
//! @throw noexcept
//!     Swallows all exceptions at the API boundary.
//------------------------------------------------------------------------------
LRESULT CALLBACK impl::window_impl::top_level_wnd_proc_(
    HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam
) try {
    // set the instance pointer for the window given by hwnd if it was just created
    if (msg == WM_NCCREATE) {
        auto const cs =
            reinterpret_cast<CREATESTRUCTW const*>(lParam);
        auto const window_ptr =
            reinterpret_cast<window_impl*>(cs->lpCreateParams);

        set_window_ptr(hwnd, window_ptr);
    }

    // the window object to forward the message to
    auto const window = get_window_ptr(hwnd);

    if (window) {
        return window->window_proc_(hwnd, msg, wParam, lParam);
    } else {
        // it's possible we will receive some messages beofre WM_NCCREATE;
        // use the default handler
        return ::DefWindowProcW(hwnd, msg, wParam, lParam);
    }
} catch (std::exception&) {
    ::PostQuitMessage(-1);
    return 0;
} catch (...) {
    ::PostQuitMessage(-1);
    return 0;
}

//------------------------------------------------------------------------------
//! Called by the top level window proc. Dispatches messages to their
//! appropriate handler function.
//------------------------------------------------------------------------------
LRESULT impl::window_impl::window_proc_(
    HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam
) {
    return ::DefWindowProcW(hwnd, msg, wParam, lParam);
}

//------------------------------------------------------------------------------
void impl::window_impl::create() {
    handle_ = create_window_(this);
}

//------------------------------------------------------------------------------
void impl::window_impl::show(bool visible) {
    ::ShowWindow(handle_, SW_SHOWDEFAULT);
    ::InvalidateRect(handle_, nullptr, FALSE);
    ::UpdateWindow(handle_);
}
Brandon
  • 724
  • 5
  • 12
  • I tried modifying my code to look more like yours (minus the unicode modifications) and the problem has persisted. The only thing that solves the problem is to remove the lpParam, but I need that to be present so that's not a valid solution. – Alex Jeffrey Dec 28 '12 at 21:19
  • I added some more code in case it helps you to track down the source of your problem. – Brandon Dec 28 '12 at 22:09