본문 바로가기
개발/Windows

[Win32] WM_ERASEBKGND에서 리턴 값의 의미?

by lucidmaj7 2022. 2. 24.
728x90
반응형

최근 UI를 개발하다가 WM_ERASEBKGND 콜백을 구현할 일이 생겼었다. MFC에서 주로 개발을 했었고, 깜박임 방지 등을 위해 습관적으로 FALSE를 리턴하고 있었다.(사실 잘못 쓰고 있었다) 이번에도 아무생각 없이 WM_ERASEBKGND콜백에서 0을 리턴을 해버렸는데 코드리뷰에서 문제가 제기 되었다. 1과 0의 차이는 무엇일까? 이번 기회에 좀 찾아 보았다.

참고로 나의 경우 WM_ERASEBKGND에서 배경을 그리고 WM_PAINT에서 글씨를 그리는 작업을 하고 있었다.

WM_ERASEBKGND 는?

가장 정확한 레퍼런스는 MSDN이다.
https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-erasebkgnd

우선 WM_ERASEBKNG 콜백에서 하는일을 잠깐 알아보자면, 다음 과 같이 설명하고 있다.

윈도우의 배경이 지워질때 전송된다.(예를 들어 창조절시) 무효화된 부분을 다시 그리기위한 윈도우 부분을 준비하기 위해 메시지가 전송된다.

즉, 윈도우를 배경을 지워야 하는경우, 다시그려야하는경우 전송되는 메시지이다.

WPARAM은 지울 배경의 DC핸들을 나타내며, LPARAM은 사용되지 않는다.

결과값으로는 지우면 0이 아닌 값, 지우지 않았다면 0을 반환한다.

WM_ERASEBKGND 의 리턴 값?

Remark에 보면 다음과 같은 말이 나온다.

메시지를 처리하여 배경을 지웠다면, 0이 아닌 값을 반환해야 한다. 이것은 더이상 지울 필요가 없다는 것을 나타낸다.
만약 0을 리턴했다면 윈도우는 지워야 한다고 표시한 것이다. (일반 적으로 PAINTSTRUCT 구조체의 fErase가 TRUE 상황이 된다.)

즉, 개발자가, 애플리케이션이 배경을 분명 지웠다면 다시 또 지울 필요가 없다고 표시를 해야하는데 이것이 바로 리턴값의 의미라는 것이다.

보통 WM_ERASEBKGND 처리후 WM_PAINT로 넘어가 PAINT작업을 하게된다. 여기서 PAINTSTRUCT 구조체를 다루게 되는데 이 구조체의 멤버중 하나인 fErase에서 WM_ERASEBKGND의 처리 상태를 알 수 있다는 의미가 된다.

다시 말해 WM_ERASEBKGND의 리턴값은 내가 배경을 지웠다! 의 의미로 해석 할 수 있다.

Demo

예제 코드를 작성해서 눈으로 확인해보자. 예제는 WM_ERASEBKND에 따라 WM_PAINT에서 PAINTSTRUCT.fErase 플래그를 확인해 배경을 지워야 하는지 , 안지워도 되는지를 나타내는 코드이다.

1) WM_ERASEBKGND에서 0(false)을 리턴한 경우

case WM_PAINT:
        {  
            TCHAR msgErase[] = _T("배경을 지웠음. 안지워도됨 ");
            TCHAR msgNotErase[] = _T("배경을 안지웠음. 배경을 지워야함.");
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            SetBkMode(hdc, TRANSPARENT);

            if (ps.fErase == true)
            {
                SetTextColor(hdc, RGB(255, 0, 0));
                TextOut(hdc, 10, 40, msgNotErase, lstrlen(msgNotErase));
            }
            else
            {
                SetTextColor(hdc, RGB(0, 255, 0));
                TextOut(hdc, 10, 10, msgErase, lstrlen(msgErase));

            }
            EndPaint(hWnd, &ps);
        }
        break;
    case WM_ERASEBKGND:
        {
            hdc = (HDC)wParam;
            Brush = CreateSolidBrush( RGB(0, 0, 255));
            GetClientRect(hWnd, &crt);
            FillRect(hdc, &crt, Brush);
            DeleteObject(Brush);
            return false;
        }

이 코드에서는 WM_ERASEBKND에서 false를 리턴하고 있다. 그러자 WM_PAINT의 PAINTSTRUCT.fErase에는 true값이 들어와 배경을 지우라고 알려주고 있다.

2) WM_ERASEBKND에서 1(true)을 리턴 한 경우

    case WM_PAINT:
        {  
            TCHAR msgErase[] = _T("배경을 지웠음. 안지워도됨 ");
            TCHAR msgNotErase[] = _T("배경을 안지웠음. 배경을 지워야함.");
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            SetBkMode(hdc, TRANSPARENT);

            if (ps.fErase == true)
            {
                SetTextColor(hdc, RGB(255, 0, 0));
                TextOut(hdc, 10, 40, msgNotErase, lstrlen(msgNotErase));
            }
            else
            {
                SetTextColor(hdc, RGB(0, 255, 0));
                TextOut(hdc, 10, 10, msgErase, lstrlen(msgErase));

            }
            EndPaint(hWnd, &ps);
        }
        break;
    case WM_ERASEBKGND:
        {
            hdc = (HDC)wParam;
            Brush = CreateSolidBrush( RGB(0, 0, 255));
            GetClientRect(hWnd, &crt);
            FillRect(hdc, &crt, Brush);
            DeleteObject(Brush);
            return true;
        }

결과 PAINTSTRUCT.fErase값이 false이므로

배경을 지웠다고 안지워도 된다는 메시지를 확인 할 수 있다.

3) WM_ERASEBKND를 구현하지 않은 경우?

그렇다면 WM_ERASEBKND를 구현하지 않은 경우는 어떻게 될까?

    case WM_PAINT:
        {  
            TCHAR msgErase[] = _T("배경을 지웠음. 안지워도됨 ");
            TCHAR msgNotErase[] = _T("배경을 안지웠음. 배경을 지워야함.");
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            SetBkMode(hdc, TRANSPARENT);

            if (ps.fErase == true)
            {
                SetTextColor(hdc, RGB(255, 0, 0));
                TextOut(hdc, 10, 40, msgNotErase, lstrlen(msgNotErase));
            }
            else
            {
                SetTextColor(hdc, RGB(0, 255, 0));
                TextOut(hdc, 10, 10, msgErase, lstrlen(msgErase));

            }
            EndPaint(hWnd, &ps);
        }
        break;
  /*  case WM_ERASEBKGND:
        {
            hdc = (HDC)wParam;
            Brush = CreateSolidBrush( RGB(0, 0, 255));
            GetClientRect(hWnd, &crt);
            FillRect(hdc, &crt, Brush);
            DeleteObject(Brush);
            return true;
        }*/

위 코드와 같이 WM_ERASEBKND를 주석 처리하고 실행해 보았다.

배경은 윈도우의 기본 DefWindowProc에 의해 대신 하얀색으로 지워 졌으며 WM_PAINT에서 PAINTSTRUCT.fErase값은 false로 배경을 안지워도 된다는 메시지를 볼 수 있다.

결론

  • WM_ERASEBKGND의 리턴값 1은 배경을 지웠다! 의 의미를 나타낸다. (안지웠으면 0)
  • WM_ERASEBKGND 리턴 1: WM_PAINT의 PAINTSTRUCT.fErase == false
  • WM_ERASEBKGND 리턴 0: WM_PAINT의 PAINTSTRUCT.fErase == true
  • WM_ERASEBKGND를 구현하지 않으면 윈도우 기본 DefWindowProc이 WM_ERASEBKGND를 처리하므로 항상 PAINTSTRUCT.fErase는 false이다.

참고

 

728x90
반응형

댓글