질문과 답변

윈도우즈 api정복 1권 379~380p 예제가 이해가 안됩니다. 날짜:2023-4-16 9:39:44 조회수:270
작성자 : 황금가물치
포인트 : 49
가입일 : 2022-05-06 15:33:18
방문횟수 : 45
글 7개, 댓글 7개
소개 : 안녕하세요. 윈도우즈api를 공부하기위하여 가입했습니다. 잘 부탁드립니다.
작성글 보기
쪽지 보내기
윈도우즈 api정복 1권 379~380p 예제가 이해가 안됩니다.
SetWindowLongPtr과 GetWindowLongPtr이 이해가 잘 안됩니다.

LRESULT CALLBACK ChildProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) {
    HDC hdc = { 0 };
    PAINTSTRUCT ps = { 0 };

    switch (iMessage) {
    case WM_CREATE:
        SetWindowLongPtr(hWnd, 0, TRUE);
        return(0);
    case WM_LBUTTONDOWN:
        SetWindowLongPtr(hWnd, 0, !GetWindowLongPtr(hWnd,0));
        InvalidateRect(hWnd, NULL,TRUE);
        return(0);
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        if (GetWindowLongPtr(hWnd,0)) {
            Ellipse(hdc, 10, 10, 90, 90);
        }
        else{
            MoveToEx(hdc, 10, 10, NULL);
            LineTo(hdc, 90, 90);
            MoveToEx(hdc, 10, 90, NULL);
            LineTo(hdc, 90, 10);
        }
        EndPaint(hWnd, &ps);
        return(0);
    }
    return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}

밑줄그은 부분이 이해가 잘 안됩니다. GetWindowLongPtr의 윈도우 정보를 얻어오는 함수인데 왼쪽 마우스 버튼을 누르면 반대값이 되면서 SetWindowLongPtr에서 윈도우 속성을 !GetWindowLongPtr으로 변경하고 WM_PAINT로 내려보내는 것으로 이해하면 되는 지 궁금합니다. 또한 비주얼스튜디오 2022에서 !GetWindowLongPtr하면 경고가 뜨는데 경고를 뜨지않게 작성방법이 없을까요? ~GetWindowLongPtr+0x00000001으로 해봤는데 안 됩니다. 답변 부탁드립니다.




김민수

목록보기 삭제 수정 신고 스크랩

작가K 4월17일 5:44:05  

GetWindowLongPtr, SetWindowLongPtr 함수는 윈도우의 여분 메모리를 읽고 쓰는 함수입니다. 관련 이론은 예제 바로 위에 설명되어 있고요 이 두 함수가 동작하기 위해 여분 메모리양을 먼저 지정해 둡니다.

WndClass.cbWndExtra=4;

이러면 각 윈도우벌로 4바이트씩의 메모리를 가지게 되고 이 위치인 0번 인덱스가 곧 4바이트 변수가 됩니다. 두 함수는 이 값을 읽고 씀으로써 윈도우 고유의 정보를 저장합니다. GetWindowLongPtr(hWnd,0) 호출문은 0번 여유 메모리의 값을 읽는다는 뜻이고 그 값이 0 아니면 타원을 0이면 사각형을 그림으로써 윈도우의 상태를 변경합니다.
SetWindowLongPtr(hWnd, 0, !GetWindowLongPtr(hWnd,0)); 호출문은 현재 상태를 읽어 반대로 뒤집어 다시 저장한다는 뜻이며 변수로 치환해서 생각하면 a = !a로 생각할 수 있습니다. 이때 ! 연산자는 여분 메모리에 넣어둔값을 우리가 논리적은 진위형으로 취급하기 때문인데 반대로 뒤집는다는 점에 별 문제는 없습니다. 다만 윈도우 운영체제는 타입에 엄격하지 않은데 비해 C++ 언어는 타입 체크를 엄격하게 하기 때문에 정수에 ! 연산이 적절히 않다고 경고하는 것입니다. ~를 쓰는 것은 정확하지도 않을 뿐더러 동작하지도 않습니다. 정확하게 기술하려면 다음과 같이 해야 합니다.

SetWindowLong(hWnd, 0, GetWindowLong(hWnd, 0)==0 ?1:0);

여분 메모리는 임의의 메모리 영역이며 타입이 따로 없습니다. 넣고 쓰는 사람이 알아서 읽고 쓸 뿐이며 운영체제는 요청한만큼 메모리를 준비해 줄 뿐입니다. 위 예제의 경우는 여분 메모리를 진위형으로 취급하는데 컴파일러는 WORD로 타입을 체크하기 때문에 경고가 발생합니다.
읽고 쓰는 값을 다 정수로 바꾸면 되지만 API 자체가 C 수준의 라이브러리인데 C++ 경고 제거를 위해 굳이 이렇게까지 할 필요는 없는거죠. 경고는 제안일 뿐이라 무시하면 됩니다. 상태를 변경한 후 WM_PAINT 메시지가 날라가는 이유는 바로 아랫줄에 InvalidateRect(hWnd, NULL,TRUE); 호출문이 있기 때문이며 자동으로 날라가지는 않습니다.

황금가물치 4월18일 10:14:03  

자세한 설명으로 이해했습니다. 한가지 더 궁금한건 프로그램을 처음 실행해서 타원이 9개가 나오는 이유는
LRESULT CALLBACK ChildProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) {
    HDC hdc = { 0 };
    PAINTSTRUCT ps = { 0 };

    switch (iMessage) {
    case WM_CREATE:
        SetWindowLongPtr(hWnd, 0, TRUE);
        return(0);
    case WM_LBUTTONDOWN:
        SetWindowLongPtr(hWnd, 0, GetWindowLongPtr(hWnd, 0)==0?1:0);
        InvalidateRect(hWnd, NULL,TRUE);
        return(0);
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        if (GetWindowLongPtr(hWnd,0)) {
            Ellipse(hdc, 10, 10, 90, 90);
        }
        else{
            MoveToEx(hdc, 10, 10, NULL);
            LineTo(hdc, 90, 90);
            MoveToEx(hdc, 10, 90, NULL);
            LineTo(hdc, 90, 10);
        }
        EndPaint(hWnd, &ps);
        return(0);
    }
    return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}
에서 최초 WM_CREATE에서 SetWindowLongPtr함수로 각 9개 "ChildCls"클래스 윈도우가 TRUE값이 저장되고 WM_PAINT에서 GetWindowLongPtr함수가 저장된 TRUE값을 읽어 9개의 타원이 생성되며, 마우스 오른쪽 버튼을 누르면 TRUE값과 0이 일치 안해 SetWindowLongPtr함수는 선택된 "ChildCls"클래스 윈도우에 0값을 세팅하면서 x값이 나오는 걸로 이해하는게 맞는지 궁금합니다. 잘 이해했는지요?


로그인하셔야 댓글을 달 수 있습니다.