IT_Programming/MFC · API

[API] 여분의 메모리를 사용하는 이유와 방법

JJun ™ 2009. 7. 24. 09:19

==================================================================================================

 

서론   :      각 차일드 윈도우를 구별할 경우 특정 차일드의 정보를 저장하는 배열 첨자를 찾을 수 없는데,

                 이는 핸들이 운영체제가 발급하는 것이라서 그렇다.

                 ID를 이용하면 될 것 같지만 이것도 차일드 윈도우 같은 경우

                 필요에 따라서 동적으로 생성, 소멸을 하기 때문에 ID의 연속성을 확보하기 어렵다.

 

해결책 :     차일드별로 다른 정보를 저장할 수 있는 모종의 장치가 필요 → 여분의 메모리를 사용한다.

             ( ※ 윈도우 프로퍼티 : 여분의 메모리와 같은 용도 - 여분의 메모리보다 유연하고 편리한 방법 )

 

                 cbClsExtra  - 윈도우 클래스를 등록할 때 여분의 메모리 할당 (byte)

                 cbWndExtra - 윈도우를 만들때 여분의 메모리 할당 (byte)

                 여분의 메모리가 일반적인 전역변수나 정적변수와 다른 점은 윈도우별로 할당되고 관리되며,

     핸들이 다르면 실제로 엑세스되는 대상도 달라진다는 점이다.

 

사용 ( ..Window..() - 윈도우별, ..Class..() - 윈도우 클래스별 )

          GetWindow(Class)LongPtr(HWND hwnd, int nIndex)

          SetWindow(Class)LongPtr(HWND hwnd, int nIndex, LONG dwnewlong)

      ( nIndex = 여분의 메모리 시작주소 오프셋.  nIndex = 0 : 첫번째 여분의 메모리를 사용한다는 의미 )

        

단점 및 제약사항 :  기존에 존재하는 윈도우를 서브 클래싱한 경우는 여분의 메모리를 함부로 사용할 수

                             없다. 다른 동료 개발자나 오랜 시간이 흐른 뒤에 장문(?)의 소스코드를 봤을 때

                             기존의 윈도우가 여분 메모리를 어떤 사용 용도로 사용하고 있으며 남은 용량이

                             얼마인지 알아내기 어렵기 때문이다. ( OOP에서 private member와 유사 )

                             → 프로퍼티 사용 !!

 

------------------------------------------------------------------------------------

 

[예제 : 윈도우즈 API 정복 1권]

==================================================================================================

 

[실행화면]

 

- 초기 화면

 

- 마우스 왼쪽 버튼을 클릭할 경우

 

- 크기를 줄일 경우

 

- 크기를 다시 크게 할 경우

 

 

==================================================================================================

 

[소스코드]

 

#include <windows.h>

 

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK ChildProc(HWND, UINT, WPARAM, LPARAM);

 

HINSTANCE g_hInstance;

 

int APIENTRY WinMain( HINSTANCE hInstance,

                                   HINSTANCE hPrevInstance,

                                   LPSTR lpCmdLine,

                                   int nShowCmd)
{
      HWND  hwnd;
      MSG   message;
      WNDCLASS wndclass;
      static char lpszMainClass[] = TEXT("MainWindow");
 
      g_hInstance = hInstance;

 

      wndclass.cbClsExtra  = 0;
      wndclass.cbWndExtra  = 0;
      wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
      wndclass.hCursor  = LoadCursor(NULL, IDC_ARROW);
      wndclass.hIcon   = LoadIcon(NULL, IDI_APPLICATION);
      wndclass.hInstance  = hInstance;
      wndclass.lpfnWndProc = WndProc;
      wndclass.lpszClassName = lpszMainClass;
      wndclass.lpszMenuName = NULL;
      wndclass.style   = CS_HREDRAW | CS_HREDRAW;
      RegisterClass(&wndclass);

     

      wndclass.cbWndExtra = 4; // 여분의 메모리 지정 (최대 40Byte) : 각 차일드를 구별하는 용도

                                            // - 여기서는 ㅇor x 구별 용도
     
 wndclass.hbrBackground = (HBRUSH)GetStockObject(LTGRAY_BRUSH);
      wndclass.hCursor = LoadCursor(NULL, IDC_CROSS);
      wndclass.lpfnWndProc = ChildProc;
      wndclass.lpszClassName = TEXT("ChildsCls");

      RegisterClass(&wndclass);

 

     hwnd = CreateWindow (
                                          lpszMainClass,
                                          lpszMainClass,
                                          WS_OVERLAPPEDWINDOW,
                                          CW_USEDEFAULT,
                                          CW_USEDEFAULT,
                                          CW_USEDEFAULT,
                                          CW_USEDEFAULT,
                                          NULL,
                                          (HMENU) 0,
                                          hInstance,
                                          NULL
                                    );

 

      ShowWindow(hwnd, nShowCmd);

 

      while(GetMessage(&message, NULL, 0, 0)) // 메시지 루프
      {
            TranslateMessage(&message);
            DispatchMessage(&message);
      }

 

      return (int)message.wParam;
}

 

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp)
{
      int x, y;
 
      switch(message)
      {
            case WM_CREATE : for(x=0; x<300; x+=100) // x 좌표
                                          {
                                                for(y=0; y<300; y+=100) // y 좌표
                                                {
                                                      CreateWindow (
                                                                               TEXT("ChildsCls"), 
                                                                               NULL, 
                                                                               WS_CHILD | WS_VISIBLE, 
                                                                               x,
                                                                               y,
                                                                               100,
                                                                               100,
                                                                               hwnd,
// 부모 윈도우 핸들 등록

                                                                   (HMENU) NULL,
                                                                   g_hInstance,
                                                                   NULL
                                                               );
                                                }
                                          }

                                                      return 0;

 

       case WM_DESTROY : PostQuitMessage(0);
                                       return 0;
 }

 

 return DefWindowProc(hwnd, message, wp, lp);
}

 

LRESULT CALLBACK ChildProc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp)
{
      HDC   hdc;
      PAINTSTRUCT ps;
 
      switch(message)
      {
            case WM_CREATE :  SetWindowLong(hwnd, 0, TRUE);
                                           return 0;

 

            case WM_LBUTTONDOWN : // !GetWindowLong(hwnd, 0) : TRUE → FALSE , FALSE → TRUE
                                                     SetWindowLong(hwnd, 0, !GetWindowLong(hwnd, 0)); 

                                                     // 강제로 WM_PAINT 메시지를 발생시킴
                                                     InvalidateRect(hwnd, NULL, TRUE);
                                                     return 0;

 

            case WM_PAINT :   hdc = BeginPaint(hwnd, &ps);
        
                                         if(GetWindowLong(hwnd, 0)) // TRUE
                                         {
                                                 // ㅇ(동그라미) 그리기
                                                 Ellipse(hdc, 10, 10, 90, 90);
                                         }
                                         else // FALSE
                                         {
                                                 // X(엑스)자 그리기
                                                 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, message, wp, lp);
}

 

==================================================================================================