WM_PAINT 消息,BeginPaint 循环

WM_PAINT message, BeginPaint loop

我不明白为什么我在 BeginPaint 函数上出现循环。我已经阅读过关于这种循环的帖子,但几乎所有帖子都推荐:"Don't forget to use BeginPaint function on WM_PAINT message, because it entails subsequent WM_PAINT messages otherwise"。这不是我的情况。你能给我一些建议吗?

这是我的窗口类 ("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 函数。

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

注册类

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

创建窗口

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

显示

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

因为你从来没有打电话给 SetWindowLongPtr,

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

returns a nullptr,您随后尝试取消引用:

BeginPaint( windowPtr->handleWindow,  &ps )

这将触发访问冲突异常,导致 BeginPaint 调用永远不会执行,从而使无效区域保持原样。结果,系统不断生成 WM_PAINT 消息。这与完全不调用 BeginPaint 是同一个问题。1

要解决这个问题,您必须通过调用 SetWindowLongPtr 将 window 句柄附加到 window 实例,或者只需使用 hWnd 参数传入你的 CWindow::WindowProc.


1 请注意,在某些情况下,系统会在 64 位版本的 Windows 上静默处理 WindowProc 中未处理的异常.