Daniil Vorobeyv
Daniil Vorobeyv

Reputation: 165

WM_PAINT message, BeginPaint loop

I can't understand why I got a loop on BeginPaint function. I have already read posts about this kind of loop but almost all of them recommend: "Don't forget to use BeginPaint function on WM_PAINT message, because it entails subsequent WM_PAINT messages otherwise". This isn't my case. May you give me some advices?

This is my windowclass ("CWindow"):

class CWindow {
public:

   CWindow();
   virtual ~CWindow();
   bool RegisterClass(HINSTANCE hInstance);
   bool CreateWnd(HINSTANCE hInstance);
   bool Show(int nShow);
private:

    HWND handleWindow;
    ATOM atom;
    bool isRegistered;
    bool isCreated;
    static LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
    void OnPaint();
    void OnDestroy();
};

WndProc function.

LRESULT CALLBACK CWindow::WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{  
    CWindow* windowPtr = reinterpret_cast<CWindow*> ( GetWindowLongPtr( hWnd, GWLP_USERDATA ) );
    PAINTSTRUCT ps;
    HDC hdc;
    switch( msg ) {  
        case WM_PAINT:
            // There is a loop right here!
            hdc = BeginPaint( windowPtr->handleWindow,  &ps );
            // The code below doesn't executed!
            RECT rect;
            (void)GetClientRect(windowPtr->handleWindow, &rect);

            (void)DrawText(hdc, TEXT("Hello, Windows 98!"), -1, &rect,
            DT_SINGLELINE | DT_CENTER | DT_VCENTER);

            EndPaint( windowPtr->handleWindow, &ps );
            break;
        case WM_DESTROY:
            windowPtr->OnDestroy();
            break;
        default:
            return DefWindowProc( hWnd, msg, wParam, lParam );
    }
    return 0;
}

RegisterClass

bool CWindow::RegisterClass(HINSTANCE hInstance)
{
    const TCHAR app_name[] = TEXT("HelloWin");
    WNDCLASSEX  windowClass;

    ZeroMemory( &windowClass, sizeof(windowClass) );
    windowClass.cbSize = sizeof(windowClass);
    windowClass.style = CS_HREDRAW | CS_VREDRAW;
    windowClass.lpfnWndProc = WindowProc;
    windowClass.cbClsExtra = 0;
    windowClass.cbWndExtra = 0;
    windowClass.hInstance = hInstance;
    windowClass.hIcon = 0;
    windowClass.hIcon = 0;
    windowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
    windowClass.hbrBackground = 0;
    windowClass.lpszMenuName = NULL;
    windowClass.lpszClassName = app_name;
    windowClass.hIconSm = NULL;

    atom = RegisterClassEx( &windowClass );
    DWORD errorCode = GetLastError();
    if( errorCode ) {
        isRegistered = 0;
        std::wcout << L"ErrorCode: " << errorCode << std::endl;
    } else {
        isRegistered = 1;
    }
    return isRegistered;
}

CreateWindow

bool CWindow::CreateWnd(HINSTANCE hInstance)
{
    handleWindow = CreateWindow((PCTSTR)atom,               // window class name or atom
                         TEXT("The Hello Program"),  // window caption
                         WS_OVERLAPPEDWINDOW,        // window style
                         CW_USEDEFAULT,              // initial x position
                         CW_USEDEFAULT,              // initial y position
                         CW_USEDEFAULT,              // initial x size
                         CW_USEDEFAULT,              // initial y size
                         NULL,                       // parent window handle
                         NULL,                       // window menu handle
                         hInstance,                  // program instance handle
                         NULL);                      // creation parameters 
    DWORD errorCode = GetLastError();
    if( !handleWindow ) {
        isCreated = 0;
    } else { 
        isCreated = 1;
    }
    return isCreated;
}

Show

bool CWindow::Show(int nShow)
{
    if( isCreated ) {
        ShowWindow( handleWindow, nShow );
        return UpdateWindow( handleWindow );
    } else {
        return 0;
    }
}

WinMain

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevINstance, LPSTR lpCmdLine, int nShow )
{
    CWindow window;
    window.RegisterClass( hInstance );
    window.CreateWnd( hInstance );
    window.Show( nShow );
    int response = 0;
    MSG msg;
    while( GetMessage( &msg, 0, 0, 0 ) ) {
        TranslateMessage( &msg );
        DispatchMessage( &msg );
    }
        return 0;
    }

Upvotes: 1

Views: 2278

Answers (1)

IInspectable
IInspectable

Reputation: 51511

Since you never call SetWindowLongPtr,

CWindow* windowPtr = reinterpret_cast<CWindow*>( GetWindowLongPtr( hWnd, GWLP_USERDATA ) );

returns a nullptr, that you subsequently try to dereference:

BeginPaint( windowPtr->handleWindow,  &ps )

That will trigger an access violation exception, causing the BeginPaint call to never even get executed, leaving the invalid region as is. As a consequence, the system keeps generating WM_PAINT messages. That's the same issue as not calling BeginPaint altogether.1

To solve this, you'll either have to attach the window handle to the window instance by calling SetWindowLongPtr, or simply use the hWnd parameter that's passed into your CWindow::WindowProc.


1 Note that the system silently handles unhandled exceptions in your WindowProc on 64-bit versions of Windows under certain conditions.

Upvotes: 1

Related Questions