[Win32] 2. Window Class, Windows 메시지 시스템

김성엽님의 블로그를 따라하고 있습니다.

Window Class

Windows OS에는 멀티태스킹을 지원함(창 여러개 띄우는거)
프로그램이 엄청나게 중복될텐데 얘네를 어떻게 중복처리를 해서 간소화시킬까 라는 생각에서 나온 개념

  • Window 단위로 프로그램 작업을 처리함

Window Procedure

  • 어떤 메시지가 Window에 발생한 경우,
    그 메시지를 어떻게 처리할 것인지 정의한 함수
  • 예)
    어떤 프로그램에 버튼이 필요해서 다른윈도우에서 버튼을 만들어서 사용할 때(자식윈도우), 메시지를 처리하기위한 Window Procedure를 추가해야함.
    • 버튼 여러개 만들면 Window Procedure가 무조건 '중복' 된다.
    • 그럼 Procedure를 공유하면 됨

Window Procedure 공유하기

  • 그 많은 Procedure를 공유하는 방법이 Window Class

Window Class를 구성하는 정보들

Window Procedure를 공유하기 위한 정보 + 다양한 정보들이 있음.

매번 작업을 할때 마다 초기화를 해줘야한다.
여러 동일한 작업을 하는 Window라면 기본적으로 초기값이 동일함.

WNDCLASS 구조체에서 관리된다.

typedef struct tagWNDCLASS{
	UINT 		style;
    WNDPROC		lpfnWndProc;
    int			cbClsExtra;
    int			cbWndExtra;
    HINSTANCE	hInstance;
    HICON		hIcon;
    HCURSOR		hCursor;
    HBRUSH		hbrBackground;
    LPCWSTR		lpszMenuName;
    LPCWSTR		lpszClassName;
} WNDCLASS;
  • Class Name - lpszClassName
    • Window Class를 구분하기 위해 이름을 유니코드 문자열 형식으로 저장
    • typedef const wchar_t *LPCWSTR : LPCWSTR 자료형은 유니코드 문자열을 사용하기위한 포인터 형식의 자료형이다.
    • const wchar_t *lpszClassName; . 결국 이런모양이다.
// 직접 문자열을 대입하는 경우
WNDCLASS wc;
wc.lpszClassName = L"Tipsware";

// 배열에 저장된값을 사용하는 경우
WNDCLASS wc;
wchar_t class_name[] = L"Tipsware";
wc.lpszClassName = class_name;
    • 참고로 Window Class 는 대,소문자 구분이 없다.
  • Window Procedure Address - lpfnWndProc

    • 특정 Window 의 메시지를 처리하는 Procedure 함수주소.
    • 근데 개발 초기에 없는 응용프로그램의 Window Procedure 를 어떻게 지정해?
      • 함수의 포인터로 구현하자.
        typedef LREUSLT (CALLBACK*WNDPROC)(HWND, UINT, WPARAM, LPARAM);
      • 이 형식, 반환형식을 유지하면서 Window Procedure 함수를 만들어야 한다.
      • MyProc 이라는 Window Procedure 함수를 만들어보자
      LRESULT CALLBACK MyProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam){
      	if(uMsg == WM_LBUTTONDOWN){
          	// 왼쪽 마우스버튼이 클릭되었을 때 처리하고싶은 작업을 적으면 됨
          }
          /*
          DefWindowProc 함수는 Window OS가 발생하는 많은 메시지를
          프로그래머가 전부 처리할수 없기때문에,
          메시별 기본적인 상황을 처리할수 있도록
          제공하는 API 함수이다.
          */
          return DefWindowProc(hWnd, uMsg, wParam, lParam);
      }
      • 이 함수 주소를 Window Class에 지정할 땐 아래와 같이 적으면 됨
      WNDCLASS wc;
      wc.lpfnWndProc = MyProc; //  MyProc 함수의 주소를 대입
      • 이렇게 Window Class에 저장하면, 해당 메시지 발생시 해당 함수가 호출 됨
  • Instance Handle - hInstance

    • 인스턴스는 Windows OS에서 실행된 응용프로그램을 구별하기 위한 고유값이다.
    • 응용프로그램에 포함된 리소스(아이콘, 커서, 비트맵 등)를 사용하는 API함수들은 Instance를 매개변수로 사용한다.
      • WinMain에서 첫번째 인자로 Instance값이 전달된다. 그값을 그대로 이항목에 넣으면됨
      int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
      {
      	WNDCLASS wc;
          wc.hInstance = hInstance; // 첫 매개변수 값을 대입
          ...
      }
  • Cursor - hCursor

    • 마우스 커서 위치값을 핸들값에 저장한다.
    WNDCLASS wc;
    wc.hCursor = LoadCursor(NULL,IDC_ARROW);
  • Logo Icon - hIcon

    • 로고이미지로 사용할 아이콘 핸들값을 저장
    WNDCLASS wc;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  • Background Brush - hbrBackground

    • Window 배경에 대한 핸들값을 저장
    • CreateSolidBrush() 함수를 이용하여 'Brush Object'를 만들어서 핸들값에 저장하여 사용
      • Window Class에 전달된 Brush Object는 OS에서 관리를 안해서 주의가 필요함
      • 프로그램 종료시 Brush Object가 자동으로 제거가 안되기 때문에 직접 제거를 해야함.
      • 이런 Brush Object 핸들값을 잘 관리해줘야한다.
    WNDCLASS wc;
    HBRUSH h_burch = CreateSolidBrush(RGB(0,255,255));
    wc.hbrBackground = h_bursh;
    • GetStockObject() : 그냥 평범하게 기본색상을 사용하고 싶을때 사용하는 함수
    WNDCLASS wc;
    wc.hbrBackground = ::GetStockObject(WHITE_BRUSH); // 흰색채우기 속성 사용
    • 색상들은 상수값으로 돌아감
  • 더 간단히 색을 지정할 수 있다

  • GetSysColorBrush()를 사용하여 동작하기 때문에 다음과 같은 방법도 사용됨

    WNDCLASS wc;
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); // 흰색채우기 속성 사용
  • Menu - lpszMenuName

    • 메뉴이름을 저장한다.
    • 메뉴 리소스를 먼저 추가해서 사용해야함
    WNDCLASS wc;
    wc.lpszMenuName = L"MainMenu";
    • 메뉴가 필요없으면 NULL 넣어주면됨
    wc.lpszMenuName = NULL;
  • ClassStyle - style

    • Window의 영역 일부가 커지거나 이동하는 등 여러 스타일을 지정할 수 있음.
    • 비트 OR 연산자를 통해 여러 스타일을 동시에 지정할 수 있음
    WNDCLASS wc;
    wc.style = CS_HREDRAW | CS_VREDRAW;
  • Extra Class Memory - cbClsExtra

    • 동일한 WindowClass를 사용하는 Window들이 공유할 수 있는 메모리크기를 설정함.
    • 쓸때없이 추가 메모리공간을 만들면 보안에 취약해질 수 있고, 그냥 사용을 추천하지 않음. 0으로 설정 ㄱㄱ
    WNDCLASS wc;
    wc.cbClsExtra = 0;
  • Extra Window Memory - cbWndExtra

    • 특별한 경우가 아니면 그냥 0으로 ㄱㄱ
    WNDCLASS wc;
    wc.cbWndExtra = 0;

Window Class 구성하고 시스템 등록하기

WNDCLASS wc;		// Window Class 등록을 위한 구조체
wc.cbClsExtra = 0;	// 추가 메모리 사용 안함
wc.cbWndExtra = 0;	// 추가 메모리 사용 안함
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); //메인 윈도우 배경색 지정
wc.hCursor = LoadCursor(NULL, IDC_ARROW); // 화살표 커서 사용
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); // 제목 표시줄의 LOGO ICON 사용
wc.hInstance = mh_instance; // 실행 인스턴스 핸들값 지정
wc.lpfnWndProc = MyProc; // 기본 메시지 처리 함수 지정
wc.lpszClassname = L"Tipsware"; // Window Class 이름 지정
wc.lpszMenuName = NULL; // 메뉴사용 안함
wc.style = CS_HREDRAW | CS_VREDRAW; // Window 그리기 특성 설정

Window Class 선언했으면 RegisterClass() 를 이용하여 OS 에 등록하자
RegisterClass(&wc); // wc에 저장도니 정보를 기준으로 'Window Class'등록

Window Class 사용

이렇게 등록한 WindowClass를 우리 프로그램에 생성하려면
CreateWindow()의 첫 매개변수를 WindowClass 이름을 넣어주면됨
HWND h_nwd = CreateWindow(L"Tipware", ...

이렇게 사용하면 되는데, 남이 만들었거나 윈도우OS에서 사용중엔 Window Class 또한 호출할 수 있다.

HWND h_nwd = CreateWidnow(L"Button", ...

컨트롤 관련 WindowClass 이름은
Button, ComboBox, Edit, ListBox, MDIClient, Static 등 이 있음
그외 특수한 목적으로 만들어진 WindowClass도 있으니 참고.


Windows OS 메시지 시스템

Windows OS 는 Window를 사용한다!

  • 멀티태스킹할때 Window 쓴다는 말 위에도 다 적어뒀다.

시스템 자원을 어떻게 공유할까?

  • APi 함수를 호출하여 해당 자원의 권한(핸들값) 획득 후 간접적으로 사용해야함

새로운 프로그램 진행방식 사용

응용프로그램 방식

  • 키보드, 마우스는 OS에서 관리하는 시스템자원이기 때문에 CLI환경에서 사용하듯 이용하기 어려움
  • 특정 Window에 마우스를 클릭하거나 키보드를 입력했다면,
    Windows OS가 해당 Window를 소유한 응용프로그램에게 그사실을 알려야지만 해당 응용프로그램이 사용자에 대한 상황을 처리할 수 있다.
  • 그래서 동기화 기술도 함께 사용됨

Window 메시지 사용

  • 일반적인 동기화기수로 하면 굉장히 복잡해진다.
  • Window메시지를 이용하여 간단하게 도기화를 진행해보자

Message ID

  • OS가 Window에 발생한 각종 상태 변화를 알려주기위해 사용되는게 메시지이다.
  • 이런 각 상태를 의미하는 상수값이 Message ID 이다.
  • 메시지 아이디는 WM_XXXX 형식으로 값이 치환되어 있다.
    WM_LBUTTONDOWN // Window Message Left Button Down 줄임말
    WM_RBUTTONUP // Window Message Right Button UP 줄임말
    이런식

사용자가 정의해서 사용가능한데, 기존 Window 메시지와 중복되면 버그가 나서 보통 이렇게 사용함
#define WM_MY_MESAGE (WM_USER + 1)

Window 메시지 구성

메시지는 상태변화 뿐만 아니라 구체적인 상황까지 알려줌

  • 메시지 아이디, 두개의 32비트 메모리인 wParam,lParam 가지 추가 제공함.(Word Parmeter, Long Parameter)
  • 각 wParam, lParam 에 저장되는 메시지아이디가 다르기 때문에 도움말을 꼭 참조해서 작업해야함.

응용 프로그램에서 어떻게 메시지 체크를?

  • R메시지 큐를 사용한다.(무슨 OS던 다 사용되네 ㅎㅎ)

Window 메시지는 운영체제만 전송가능한가?

  • 어떤 응용프로그램이던 Window메시지를 전송할 수 있음
    • SendMessage() : 메시지를 수신한 쪽에서 처리할때까지 기다리는 대기 (동기화)
    • PostMessage() : 메시지를 전달하면 대기없이 바로 종료 (비동기)

콘솔 프로그램과 응용 프로그램 진행 방식 차이

  • 콘솔프로그램은 main함수를 호출하면서 진행
  • Window 데스크톱 응용프로그램은 대부분 작업이
    메시지큐에 저장된 Window메시지 기준으로 처리됨.
    • 그래서 복잡해보이고 감도 안잡히는거임.
    • WinAPI 이해없이 MFC를 하면 안되는 이유이기도 함.

좋은 웹페이지 즐겨찾기