Search code examples
c++oopmsdngraphicdirect2d

Why wont this code, almost identical to one from MSDN, won't work?


I have left some //Comments to help you navigate through the issue

I have Main.cpp

int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nShowCmd)
{
    Game game;

    game.Initialize(hInst);
    game.MainMenu();

    while(GetMessage(&msg,NULL,0,0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return 0;
}

Game.h

#pragma once

#define WIN32_LEAN_AND_MEAN

/*Various includes*/

template<class Interface>
inline void SafeRelease(Interface** ppInterfaceToRelease)
{
    if (*ppInterfaceToRelease != NULL)
    {
        (*ppInterfaceToRelease)->Release();

        (*ppInterfaceToRelease) = NULL;
    }
}


class Game{
public:
    Game();
    ~Game();

    HRESULT Initialize(HINSTANCE);

    void MainMenu();

    void OnResize(UINT width, UINT height);

    void OnRender();

private:
    static LRESULT CALLBACK WinProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

private:
    HWND m_hWnd;
    ID2D1Factory* pD2DFactory;
    ID2D1HwndRenderTarget* pRT;

};

And Game.cpp

#include "Game.h"
#include <comdef.h>

Game::Game() :
    pD2DFactory(NULL),
    pRT(NULL)
{
}

Game::~Game()
{
    SafeRelease(&pD2DFactory);
    SafeRelease(&pRT);
}

HRESULT Game::Initialize(HINSTANCE hInst)
{
    HRESULT hr;

    // Create factory
    hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &pD2DFactory);

    // Create a window class
    WNDCLASSEX wClass;
    ZeroMemory(&wClass,sizeof(WNDCLASSEX));
    wClass.cbClsExtra=NULL;
    wClass.cbSize=sizeof(WNDCLASSEX);
    wClass.cbWndExtra=NULL;
    wClass.hbrBackground=(HBRUSH)COLOR_WINDOW;
    wClass.hCursor=LoadCursor(NULL,IDC_ARROW);
    wClass.hIcon=NULL;
    wClass.hIconSm=NULL;
    wClass.hInstance=hInst;
    wClass.lpfnWndProc=Game::WinProc;
    wClass.lpszClassName="Window Class";
    wClass.lpszMenuName=NULL;
    wClass.style=CS_HREDRAW|CS_VREDRAW;

    m_hWnd=CreateWindowEx(NULL,
            "Window Class",
            "Game", // Replace with gameName
            WS_OVERLAPPEDWINDOW|WS_MAXIMIZE,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            NULL,
            NULL,
            hInst,
            NULL);

    RECT rc;
    GetClientRect(m_hWnd,&rc);

    // Creates render target
    if(SUCCEEDED(hr))
    {
        pD2DFactory->CreateHwndRenderTarget(
        D2D1::RenderTargetProperties(),
        D2D1::HwndRenderTargetProperties(
                                        m_hWnd,
                                        D2D1::SizeU(
                                                rc.right - rc.left,
                                                rc.bottom - rc.top)),
                                        &pRT);
    }

    ShowWindow(m_hWnd,SW_MAXIMIZE); //When my program gets there, WinProc gets called with the WM_PAINT message. The problem then happens.
    UpdateWindow(m_hWnd);

    return hr;
}

void Game::OnRender()
{
    // The following code will eventually be deleted. It is left for test purpose only.
    HRESULT hr;

    RECT rc;
    GetClientRect(m_hWnd,&rc); // The error happens when my program gets to this line of code.

    ID2D1SolidColorBrush* pBlackBrush = NULL;
            pRT->CreateSolidColorBrush(
                D2D1::ColorF(D2D1::ColorF::White),
                &pBlackBrush);

            pRT->BeginDraw();

            pRT->DrawRectangle(
                D2D1::RectF(
                    rc.left + 100.0f,
                    rc.top + 100.0f,
                    rc.right - 100.0f,
                    rc.bottom - 100.0f),
                    pBlackBrush);

            hr = pRT->EndDraw();
}

LRESULT Game::WinProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    if(msg==WM_CREATE)
    {
        LPCREATESTRUCT pcs = (LPCREATESTRUCT)lParam;
            Game* pGame = (Game*)pcs->lpCreateParams;

            ::SetWindowLongPtrW(
                hWnd,
                GWLP_USERDATA,
                PtrToUlong(pGame)
                );
    }
    else
    {
        Game* pGame = reinterpret_cast<Game*>(static_cast<LONG_PTR>(
            ::GetWindowLongPtrW(
                hWnd,
                GWLP_USERDATA
                )));

        switch(msg)
        {

        case WM_PAINT:
            {
                pGame->OnRender();
                ValidateRect(hWnd, NULL);
            }
            break;

        case WM_SIZE:
            {
                UINT width = LOWORD(lParam);
                UINT height = HIWORD(lParam);
                pGame->OnResize(width, height); //OnResize is called here
            }
            break;

        case WM_DISPLAYCHANGE:
            {
                InvalidateRect(hWnd, NULL, FALSE);
            }
            break;

        case WM_DESTROY:
            {
                //SafeRelease(p2D2Factory);
                PostQuitMessage(0);
                return 0;
            }
            break;
        }
    }

        return DefWindowProc(hWnd,msg,wParam,lParam);
}

This is the exception I receive:

Unhandled exception at 0x001f1666 in Arena Clash client.exe: 0xC0000005: Access violation reading location 0x00000000.

I removed some code to make it easier to read. What happens is I get an unhandled exception. For reasons I don't understand, m_hWnd seems to have no value.

I've tried to find the solution for hours. Almost for all day actually. I really need help.

Actually, what I want is to redraw my image upon resize and similar changes.


Solution

  • You're passing NULL for the lpParam parameter (i.e. the last one) to CreateWindowEx.

    Then, in WinProc, you say

    Game* pGame = (Game*)pcs->lpCreateParams;
    ::SetWindowLongPtrW(
                hWnd,
                GWLP_USERDATA,
                PtrToUlong(pGame)
                );
    

    So you'll set the user data pointer to NULL, as well.

    You should pass thisto CreateWindowEx instead.