IT_Programming/MFC · API

[API] 서브 클래싱( SUB CLASSING )

JJun ™ 2009. 7. 29. 15:26

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

 

[인스턴스 서브 클래싱]

- 윈도우가 만들어진 후 그 윈도우 하나에 대해서만 윈도우 프로시저를 교체하는 것이다.

  따라서 이후에 만들어지는 윈도우는 이 서브클래싱의 영향을 전혀 받지 않는다.

  특정 윈도우 하나에 대해서만 윈도우 프로시저를 교체하는 것이므로 SetWindowLongPtr 함수

  사용된다.

 

[전역(클래스) 서브 클래싱]

- 윈도우 클래스에 대해 서브클래싱을 하는 것이다.

- 윈도우 클래스의 WNDCLASS 구조체를 직접 변경하며, 이때는 SetClassLongPtr 함수가 사용된다.

- 이미 만들어진 윈도우에 대해서는 전혀 효과가 없으며 앞으로 만들어질 윈도우만 영향을 받는다. 

 

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

 

즉, 윈도우 클래스에 윈도우 프로시저가 등록되어 있고, 이 윈도우 클래스로부터 윈도우를 만들 때마다

윈도우 프로시저의 번지가 각 윈도우로 복사되는데, 원본(전역)과 사본(인스턴스) 중 어디를 변경하는가에

따라 서브 클래싱의 범위가 달라진다.

 

주의! 서브 클래싱은 같은 프로세스 내에서만 효력을 발휘한다!

- 전역의 경우에도 윈도우 클래스는 본질적으로 각 프로세스에 대해 지역적이기 때문이다.

  ( 서로 다른 프로세스 영역에서 시작되는 프로그램에 영향을 주지 않는다. )

  시스템 전역 클래스에 대한 정보는 시스템이 가지고 있지만 각 프로세스가 참조하는 클래스 정보는

  응용 프로그램이 시작될 때 전달 받는 복사본일 뿐이다.

 

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

 

[ 내용참조 및 예제참조 출처 - 윈도우즈 API 정복 1권]

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

 

[실행화면 - 인스턴스 서브클래싱]

 

- 초기 화면

 

- (인스턴스 서브클래싱이 적용된) 첫번째 에디트 윈도우를 클릭 했을 경우 전체 블록을 잡는다.

 

- 탭키로 인스턴스 서브클래싱이 적용되지 않은 두번째 에디트 윈도우로 포커스를 옮긴다.

 

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

 

[실행화면 - 전역(클래스) 서브클래싱]

 

- 초기화면

 

- 전역(클래스) 서브클래싱이 적용되지 않은 에디트 윈도우는 엔터를 눌러도 반응이 없다.

 

-  전역(클래스) 서브클래싱이 적용된 두번째 에디트 윈도우는 엔터를 입력할 경우

   부모 윈도우의 캡션에 그 내용을 출력한다.

 

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

 

[소스코드 - 인스턴스 서브 클래싱]

 #include <Windows.h>

// #include <> - 시스템 폴더 경로
// #include "" - 상대 경로 (현재 응용 프로그램의 경로)

 

#define ID_EDIT1 100
#define ID_EDIT2 200

 

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

 

HWND  hEdit1, hEdit2;
WNDPROC  OldEditProc;
HINSTANCE g_hInst;

 

int APIENTRY WinMain( HINSTANCE hInstance,

                                   HINSTANCE hPrevInstance,

                                   LPSTR lpCmdLine,

                                   int nShowCmd)
{
      static char MainClassName[] = TEXT("WinMain");
      HWND  hwnd;
      MSG   msg;
      WNDCLASS wndclass;

 

      g_hInst = 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 = MainClassName;
      wndclass.lpszMenuName = NULL;
      wndclass.style   = CS_HREDRAW | CS_VREDRAW;

 

      RegisterClass(&wndclass);

     

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

 

      ShowWindow(hwnd, nShowCmd);

     

      while(GetMessage(&msg, NULL, 0, 0))
      {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
      }

     

      return (int)msg.wParam;
}

 

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp)
{
      PAINTSTRUCT ps;
      HDC   hdc;
      TCHAR  *Mes = TEXT("에디트의 Enter키 입력을 검출합니다!");
  
      switch(message)
      {
            case WM_CREATE : MoveWindow(hwnd, 350, 250, 500, 300, TRUE);
                                          hEdit1 = CreateWindow (
                                                                               TEXT("edit"),
                                                                               TEXT("서브클래싱"),
                                            WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL,
                                                                               10,
                                                                               10,
                                                                               200,
                                                                               25,
                                                                               hwnd,
                                                                               (HMENU)ID_EDIT1,
                                                                               g_hInst,
                                                                               NULL     
                                                                         );

 

                                     hEdit2 = CreateWindow (
                                                                          TEXT("edit"),
                                                                          TEXT("원래클래스"),
                                            WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL,
                                                                          10,
                                                                          50,
                                                                          200,
                                                                          25,
                                                                          hwnd,
                                                                          (HMENU)ID_EDIT2,
                                                                          g_hInst,
                                                                          NULL     
                                                                    );

                                   SetFocus(hEdit1);

                         // ↓ 이전의 윈도우 프로시저의 번지를 기억! (3번째 인자 - 서브클래스 프로시저)
                                   OldEditProc = (WNDPROC)SetWindowLongPtr(hEdit1, GWLP_WNDPROC,

                                                                                                       (LONG_PTR)SubEditProc);

                                   return 0;

 

       case WM_PAINT :     hdc = BeginPaint(hwnd, &ps);
                                     TextOut(hdc, 10, 100, Mes, lstrlen(Mes));
                                     EndPaint(hwnd, &ps);
                                     return 0;

 

                                        // ↓ 이전의 윈도우 프로시저의 번지로 돌린다!

       case WM_DESTROY :  SetWindowLongPtr(hEdit1, GWLP_WNDPROC,

                                                                              (LONG_PTR)OldEditProc);
                                        PostQuitMessage(0);
                                        return 0;
 }

 

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

 

// 서브클래스 윈도우 프로시저 - hEdit1만 서브 클래싱이 적용되어 있다!
LRESULT CALLBACK SubEditProc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp)

{
      static BOOL INIT_ERASE_FLAG = TRUE;

     

      switch(message)
      {
            case WM_KEYDOWN :  if(INIT_ERASE_FLAG == TRUE)
                                              {
                                                      SetWindowText(hEdit1, "");
                                                      SetWindowText(hEdit2, ""); 
                                              }
                                              INIT_ERASE_FLAG = FALSE;
 
                                              if(wp == VK_RETURN)
                                              {
                                                    MessageBox(hwnd, TEXT("Enter is pressed!"), TEXT("Edit"),

                                                                           MB_OK);
                                                    SetFocus(hwnd);
                                              }

                                              

                                              if(wp == VK_TAB)
                                                   SetFocus(hEdit2);
        
                                             if(wp == VK_LEFT)
                                                   wp = VK_RIGHT;
                                             else if(wp == VK_RIGHT)
                                                   wp = VK_LEFT;
                
                                             break;

 

             case WM_LBUTTONDOWN :     SetFocus(hEdit1);
                                                          SendMessage(hwnd, EM_SETSEL, 0, -1);
                                                          return 0;
      }

 

  // CallWindowProc 함수 : 관심을 가지는 메시지 이외에는 원래의 에디트 윈도우 방식으로 처리!

      return (CallWindowProc(OldEditProc, hwnd, message, wp, lp));
}

 

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

 

[소스코드 - 전역(클래스) 서브 클래싱]

#include <Windows.h>

 

#define BUF_SIZE 256
#define ID_EDIT1 100
#define ID_EDIT2 200

 

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

 

HWND  hEdit1, hEdit2;
WNDPROC  OldEditProc;
HINSTANCE g_hInst;

 

int APIENTRY WinMain( HINSTANCE hInstance,

                                  HINSTANCE hPrevInstance,

                                  LPSTR lpCmdLine,

                                  int nShowCmd )
{
      static char MainClassName[] = TEXT("WinMain");
      HWND  hwnd;
      MSG   msg;
      WNDCLASS wndclass;

     

      g_hInst = 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 = MainClassName;
      wndclass.lpszMenuName = NULL;
      wndclass.style   = CS_HREDRAW | CS_VREDRAW;

     

      RegisterClass(&wndclass);

     

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

 

      ShowWindow(hwnd, nShowCmd);

     

      while(GetMessage(&msg, NULL, 0, 0))
      {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
      }

      return (int)msg.wParam;
}

 

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp)
{
      PAINTSTRUCT ps;
      HDC   hdc;
      TCHAR  *Mes = TEXT("에디트의 Enter키 입력을 검출합니다!");

     

      switch(message)
      {
            case WM_CREATE : MoveWindow(hwnd, 350, 250, 500, 300, TRUE);

                                          hEdit1 = CreateWindow (
                                                                               TEXT("edit"),
                                                                               NULL,
                                                 WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL,
                                                                               10,
                                                                               10,
                                                                               200,
                                                                               25,
                                                                               hwnd,
                                                                               (HMENU)ID_EDIT1,
                                                                               g_hInst,
                                                                               NULL     
                                                                         );
           // ↓ 위치 주의! -
hEdit1이 속한 edit 윈도우 클래스가 서브클래싱의 대상이 된다.

           // 즉, edit 윈도우의 클래스를 서브클래싱 적용한 이후부터 적용되게 된다.

           // 전역 서브클래싱을 적용하려는 윈도우 바로 위에서 이전 윈도우 프로시저의 번지 기억!

           // 3번째 인자 - 서브 클래스 프로시저명
                                     OldEditProc = (WNDPROC)SetClassLongPtr(hEdit1, GCLP_WNDPROC,

                                                                                                          (LONG_PTR)SubEditProc);

             // hEdit2 윈도우에 전역 서브클래싱이 적용된다.

                                     hEdit2 = CreateWindow (
                                                                          TEXT("edit"),
                                                                          NULL,
                                            WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL,
                                                                          10,
                                                                          50,
                                                                          200,
                                                                          25,
                                                                          hwnd,
                                                                          (HMENU)ID_EDIT2,
                                                                          g_hInst,
                                                                          NULL     
                                                                    );

                                     SetFocus(hEdit1);
                                     return 0;

 

         case WM_PAINT :  hdc = BeginPaint(hwnd, &ps);
                                     TextOut(hdc, 10, 100, Mes, lstrlen(Mes));
                                     EndPaint(hwnd, &ps);
                                     return 0;

 

         case WM_DESTROY : SetClassLongPtr(hEdit1, GCLP_WNDPROC,

                                                                        (LONG_PTR)OldEditProc);
                                         PostQuitMessage(0);
                                         return 0;
      }

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

 

// 서브클래스 윈도우 프로시저 - hEdit2 (이후)만 서브 클래싱이 적용되어 있다!

LRESULT CALLBACK SubEditProc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp)

{
      TCHAR str[BUF_SIZE];

 

      switch(message)
      {
            case WM_KEYDOWN : if(wp == VK_RETURN)
            {
                  GetWindowText(hwnd, str, BUF_SIZE);
                  SetWindowText(GetParent(hwnd), str);
            }
            break;
      }

 

  // CallWindowProc 함수 : 관심을 가지는 메시지 이외에는 원래의 에디트 윈도우 방식으로 처리!

      return (CallWindowProc(OldEditProc, hwnd, message, wp, lp));

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