1

I'm trying to clear the background of a window created with CreateWindowEx with the code separated into separate classes, with the use of D2D1. Here's the code.

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
    switch (msg) {
    case WM_PAINT:
        graphics->BeginDraw();
        graphics->EndDraw();
        break;

    case WM_CREATE:
        window->onCreate();
        break;

    case WM_DESTROY:
        window->onDestroy();
        ::PostQuitMessage(0);
        break;

    default:
        return ::DefWindowProc(hwnd, msg, wparam, lparam);
    }

    return NULL;
}

^ Window.cpp

bool Graphics::init(HWND windowHandle) {
    HRESULT hRes = D2D1CreateFactory(
        D2D1_FACTORY_TYPE_SINGLE_THREADED,
        &factory
    );

    if (hRes != S_OK) return false;

    RECT wCS;
    GetClientRect(windowHandle, &wCS);

    hRes = factory->CreateHwndRenderTarget(
        D2D1::RenderTargetProperties(),
        D2D1::HwndRenderTargetProperties(windowHandle, D2D1::SizeU(wCS.right, wCS.bottom)),
        &renderTarget
    );

    if (hRes != S_OK) return -1;
    return true;
}

void Graphics::ClearScreen(float r, float g, float b) {
    renderTarget->Clear(D2D1::ColorF(r, g, b));
}

^ Graphics.cpp

void BeginDraw() { renderTarget->BeginDraw(); }
void EndDraw() { renderTarget->EndDraw(); }

^ Graphics.h

My error occurs as soon as the window opens, just before it crashes. The BeginDraw() is responsible for it, as removing it from WM_PAINT resolves the issue, but that is not a viable option.

The error:

Exception thrown: read access violation. this was nullptr.

It is specified to be on the line void BeginDraw() { ... } in Graphics.h.

Notable things (Not important):

I've been trying to find a solution to this over the past several days, but I don't know what most of the code I read means, so I apologise for my lack of knowledge.

I presume the issue is BeginDraw() is being called before the graphics have initialised, but I'm not sure how to prevent that.

Thank you for any suggestions what so ever.

Yunnosch
  • 26,130
  • 9
  • 42
  • 54
Charlie
  • 77
  • 1
  • 2
  • 13
  • ***but I'm not sure how to prevent that.*** You could have a bool in `Graphics` that is initialized to false before `init()` is called and if false don't call `renderTarget->BeginDraw();` or `renderTarget->EndDraw();` – drescherjm Jan 05 '21 at 13:37
  • @drescherjm That didn't work unfortunately. I also tried doing a check if `this` was equal to `nullptr` and that caused nothing to happen while running (didn't crash nor did the background change), therefore `this` seems to be nothing other than a null pointer. Edit: To specify, only doing a check on `BeginDraw` moves the issue to `EndDraw`. – Charlie Jan 05 '21 at 13:42
  • Then your problem is you created your `Graphics` object after the window was created. – drescherjm Jan 05 '21 at 13:44
  • ***To specify, only doing a check on BeginDraw moves the issue to EndDraw*** You can do the check in both. However if `graphics` is a nullpointer then you can check for that in `WndProc()` – drescherjm Jan 05 '21 at 13:45
  • I did the check on both, that again causes nothing to happen. However, when the program is closed, Visual Studio opens `d2d1.h` and gives the initial error I was having. The code on that line is `return Clear(&clearColor);`. – Charlie Jan 05 '21 at 13:48
  • 1
    I think you will have to spend some time with your debugger. The bugs are not in the code that is presented. – drescherjm Jan 05 '21 at 13:54
  • I'll do that. I really appreciate your help regardless. – Charlie Jan 05 '21 at 13:55
  • I think part of your problem is the order of initialization between creating the window and the other objects you have. – drescherjm Jan 05 '21 at 13:56
  • I'll have a go changing the order they're created. Correct me if I'm wrong, but the graphics object has to be initialised before the window, as the initialisation, specifically `HwndRenderTargetProperties` requires the window handle, yes? I may be completely misinterpreting how this works. – Charlie Jan 05 '21 at 14:00
  • The graphics object needs to be initialized before `case WM_PAINT:` is called. I think you can do that in whatever you do in `case WM_CREATE:` – drescherjm Jan 05 '21 at 14:05
  • 1
    @drescherjm Thank you for all of your suggestions. Your most recent one combined with the suggestion to use the debugger helped me realise my mistake. The answer is at the top of the post if you wish to see it. – Charlie Jan 05 '21 at 14:34
  • Please do not edit solution announcements into the question. Create yourself an answer instead. – Yunnosch Jan 05 '21 at 17:10

1 Answers1

0

The problem is the graphics object being used for BeginDraw is a different object to the one which was initialised. It needs to be the same object.

Charlie
  • 77
  • 1
  • 2
  • 13