관리 메뉴

드럼치는 프로그래머

[API/MFC] 윈도우 클래스 ( WNDCLASS, CreateWindow ) 본문

★─Programing/☆─API | MFC

[API/MFC] 윈도우 클래스 ( WNDCLASS, CreateWindow )

드럼치는한동이 2009. 2. 25. 06:33

WinMain 함수에서 하는 가장 중요한 일은 윈도우를 만드는 일이다. 윈도우가 있어야 사용자로부터 입력을 받을 수 있고 출력을 보여줄 수도 있기 때문이다. 윈도우를 만드려면 윈도우 클래스를 먼저 등록한 후 CreateWindow 함수를 호출해야 한다. 모든 윈도우는 윈도우 클래스를 기반으로 하여 만들어지며 윈도우 클래스는 만들어질 윈도우의 여러가지 특성을 정의한다. 윈도우 클래스는 windows.h에 다음과 같이 정의되어 있는 구조체이다.


typedef struct tagWNDCLASS { UINT style; WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra; HINSTANCE hInstance; HICON hIcon; HCURSOR hCursor; HBRUSH hbrBackground; LPCSTR lpszMenuName; LPCSTR lpszClassName; } WNDCLASS;


10개나 되는 멤버를 가지고 있는데 WndClass의 각 멤버 의미는 다음과 같다. 현재 단계에서 이 내용을 일부러 암기할 필요까지는 없지만 대충 어떤 역할을 하는 멤버인지 읽어두도록 하자.


þ
style


윈도우의 스타일을 정의한다. 즉 윈도우가 어떤 형태를 가질 것인가를 지정하는 멤버이다. 이 멤버가 가질 수 있는 값은 무지하게 많지만 가장 많이 사용하는 값이 CS_HREDRAW와 CS_VREDRAW이다. 이 두 값을 OR 연산자(|)로 연결하여 사용한다. 이 값들의 의미는 윈도우의 수직(또는 수평) 크기가 변할 경우 윈도우를 다시 그린다는 뜻이다. 이밖에도 많은 값이 올 수 있다.


þ
lpfnWndProc


이 멤버는 윈도우의 메시지 처리 함수를 지정한다. 메시지가 발생할 때마다 여기서 지정한 함수가 호출되며 이 함수가 모든 메시지를 처리한다. 메시지 처리 함수의 이름은 물론 마음대로 정할 수 있지만 거의 WndProc으로 정해져 있는 편이다.


참고


WinMain 함수는 프로그램의 시작점이므로 도스에서 시작점이 반드시 main이어야 하는 것과 마찬가지로 이름이 고정되어 있다. 하지만 메시지 처리 함수인 WndProc는 사용자가 임의로 이름을 정할 수 있는 함수이다. 그러나 관습에 의해 이 함수의 이름은 WndProc으로 고정되어 있으므로 이 이름을 그대로 사용하는 것이 좋다. 관습이란 오랜 세월동안 많은 똑똑한 사람에 의해 가장 말썽없는 형태로 굳어진 것이므로 어겨봐야 득보다 실이 더 많다.


þ
cbClsExtra, cbWndExtra


일종의 예약 영역이다. 윈도우즈가 내부적으로 사용하며 아주 특수한 목적에 사용되는 여분의 공간이다. 예약 영역을 사용하지 않을 경우는 0으로 지정한다.


þ
hInstance


이 윈도우 클래스를 사용하는 프로그램의 번호이며 이 값은 WinMain의 인수로 전달된 hInstance값을 그대로 대입해주면 된다.


þ
hIcon, hCursor


이 윈도우가 사용할 마우스 커서와 최소화되었을 경우 출력될 아이콘을 지정한다. LoadCursor 함수와 LoadIcon 함수를 사용하여 지정한다. 사용자가 직접 아이콘과 커서를 만들어 사용할 수도 있지만 여기서는 윈도우즈가 디폴트로 제공하는 아이콘과 커서를 사용하고 있다. 커서는 좌측으로 기울어진 화살표 모양이며 아이콘은
와 같은 모양을 가진다.


þ
hbrBackground


윈도우의 배경 색상을 지정한다. 좀 더 정확하게 표현하면 윈도우의 배경 색상을 채색할 브러시를 지정하는 멤버이다. GetStockObject라는 함수를 사용하여 윈도우에서 기본적으로 제공하는 브러시를 지정한다. 지정할 수 있는 브러시에는 여러 가지 종류가 있지만 가장 일반적인 흰색 배경(WHITE_BRUSH)이 많이 사용된다.


þ
lpszMenuName


이 프로그램이 사용할 메뉴를 지정한다. 메뉴는 프로그램 코드에서 만드는 것이 아니라 리소스 에디터에 의해 별도로 만들어진 후 링크시에 같이 합쳐진다. 메뉴를 사용하지 않을 경우 이 멤버에 NULL을 대입해 주면 된다.


þ
lpszClassName


윈도우 클래스의 이름을 정의한다. 여기서 지정한 이름은 CreateWindow 함수에 전달되어지며 CreateWindow 함수는 윈도우 클래스에서 정의한 특성값을 참조하여 윈도우를 만든다. 윈도우 클래스의 이름은 보통 실행 파일의 이름과 일치시켜 작성하며 이 예제의 경우 lpszClass 문자열에 "First"를 대입한 후 이 문자열을 윈도우 클래스 이름으로 사용하였다.


멤버의 수가 너무 많아 한번에 다 익히기 힘들겠지만 이 중에 제일 중요한 멤버는 윈도우 클래스의 이름을 정의하는 lpszClassName과 메시지 처리 함수를 지정하는 lpfnWndProc이다. 윈도우 클래스를 정의한 후 RegisterClass 함수를 호출하여 윈도우 클래스를 등록한다.

 


ATOM RegisterClass( CONST WNDCLASS *lpWndClass);



RegisterClass 함수의 인수로 WndClass 구조체의 번지를 넘겨주면 된다. 이런 이런 특성을 가진 윈도우를 앞으로 사용하겠다는 등록 과정이다. 소스의 앞부분을 보면 WNDCLASS 구조체의 각 멤버에 값을 대입하고 윈도우 클래스를 등록하는 코드가 있다. 단순한 대입문의 연속인데 어떤 값들이 대입되었는지 잘 살펴보자.


WNDCLASS WndClass; 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)WndProc; WndClass.lpszClassName=lpszClass; WndClass.lpszMenuName=NULL; WndClass.style=CS_HREDRAW | CS_VREDRAW; RegisterClass(&WndClass);



윈도우 클래스를 등록한 후에는 등록한 윈도우 클래스를 기본으로 윈도우를 실제 생성해야 한다. 윈도우를 생성할 때는 CreateWindow 함수를 사용한다.


HWND CreateWindow(lpszClassName, lpszWindowName, dwStyle, x, y, nWidth, nHeight, hwndParent, hmenu, hinst, lpvParam)


보다시피 인수가 무척 많은 편이다. 윈도우즈 API 함수들은 대체로 인수가 많다. 각 인수의 의미를 알아보자.


þ
lpszClassName


생성하고자 하는 윈도우의 클래스를 지정하는 문자열이다. 앞에서 정의한 WndClass구조체의 lpszClassName 멤버의 이름을 여기에 기입해 준다. 우리의 예제에서는 lpszClass 문자열에 윈도우 클래스 이름을 기억시켜 두었으므로 이 문자열을 그대로 넘겨주면 된다.


þ
lpszWindowName


윈도우의 타이틀 바에 나타날 문자열이다. 여기서 지정한 문자열이 윈도우의 타이틀 바에 나타난다. 프로그래머가 마음대로 지정할 수 있는데 이 책에서는 예제의 프로젝트명(lpszClass 전역 문자열)을 타이틀 바에 나타내고 있다.


þ
dwStyle


만들고자 하는 윈도우의 형태를 지정하는 인수이다. 일종의 비트 필드값이며 거의 수십개를 헤아리는 매크로 상수들이 정의되어 있고 이 상수들을 OR연산자로 연결하여 윈도우의 다양한 형태를 지정한다. 윈도우가 경계선을 가질 것인가, 타이틀 바를 가질 것인가 또는 스크롤 바의 유무 등등을 세세하게 지정해 줄 수 있다. 가능한 스타일값에 관한 자세한 내용은 레퍼런스를 참조하되 WS_OVERLAPPEDWINDOW를 사용하면 가장 무난한 윈도우 설정 상태가 된다. 즉 시스템 메뉴, 최대 최소 버튼, 타이틀 바, 경계선을 가진 윈도우를 만들어 준다.


þ
X, Y, nWidth, nHeight


인수의 이름이 의미하듯이 윈도우의 크기와 위치를 지정하며 픽셀 단위를 사용한다. x, y좌표는 메인 윈도우의 경우는 전체 화면을 기준으로 하며 차일드 윈도우는 부모 윈도우의 좌상단을 기준으로 한다. 정수값을 바로 지정해도 되며 CW_USEDEFAULT를 사용하면 윈도우즈가 알아서 적당한 크기와 위치를 설정해 준다. 예제에서는 모두 CW_USEDEFAULT를 사용하였다.


þ
hWndParent


부모 윈도우가 있을 경우 부모 윈도우의 핸들을 지정해 준다. MDI 프로그램이나 팝업 윈도우는 윈도우끼리 수직적인 상하관계를 가져 부자(parent-child) 관계가 성립되는데 이 관계를 지정해 주는 인수이다. 부모 윈도우가 없을 경우는 이 값을 NULL로 지정하면 된다.


þ
hmenu


윈도우에서 사용할 메뉴의 핸들을 지정한다. WndClass에도 메뉴를 지정하는 멤버가 있는데 윈도우 클래스의 메뉴는 그 윈도우 클래스를 기반으로 하는 모든 윈도우에서 사용되는 반면 이 인수로 지정된 메뉴는 현재 CreateWindow 함수로 만들어지는 윈도우에서만 사용된다. 만약 WndClass에서 지정한 메뉴를 그대로 사용하려면 이 인수를 NULL로 지정하면 되며 WndClass에서 지정한 메뉴 대신 다른 메뉴를 사용하려면 이 인수에 원하는 메뉴 핸들을 주면 된다. First 예제의 경우 WndClass에도 메뉴가 지정되어 있지 않고 CreateWindow 함수에서도 메뉴를 지정하지 않았으므로 메뉴없는 프로그램이 만들어진다.


þ
hinst


윈도우를 만드는 주체, 즉 프로그램의 핸들을 지정한다. WinMain의 인수로 전달된 hInstance를 대입해 주면 된다.


þ
lpvParam


CREATESTRUCT라는 구조체의 번지이며 특수한 목적에 사용된다. 보통은 NULL값을 사용한다.


지금 당장 여기서 CreateWindow의 모든 인수에 대해 다 외우려고 할 필요까지는 없고 예제에서 어떤 값이 사용되었는가와 그 의미가 무엇인가만 대충 보고 가도록 하자. 여기서 잠깐 필자가 잔소리를 좀 하자면 API 공부를 할 때는 무시할 건 과감하게 무시하고 지나가는 요령이 필요하다. CreateWindow 함수는 API를 처음 배우는 사람에게 무척 중요하기는 하지만 그렇다고 처음부터 11개나 되는 인수들의 정확한 의미까지 속속들이 이해할 필요까지는 없다. x,y,lpszWindowName 등 쉽게 이해되는 인수들만 대충 봐두면 되지 lpvParam같은 전문적이고 어려운 인수의 의미나 dwStyle인수의 모든 스타일값에 대해 반드시 알아야 하는 것은 아니다. 물론 완벽하게 이해한다고 나쁠 것은 없겠지만 그렇게 하다가는 제풀에 지쳐 금새 흥미를 잃고 만다. 중요한 것은 이론적으로 의미가 있는 큰 줄기를 먼저 파악하는데 정성을 쏟으라는 것이다.


CreateWindow 함수는 윈도우에 관한 모든 정보를 메모리에 만든 후 윈도우 핸들을 리턴값으로 넘겨준다. 넘겨지는 윈도우 핸들은 hWnd라는 지역 변수에 저장되었다가 윈도우를 참조하는 모든 함수의 인수로 사용된다.


CreateWindow 함수로 만든 윈도우는 어디까지나 메모리상에서만 있을 뿐이며 아직까지 화면에 출력되지는 않았다. 메모리에 만들어진 윈도우를 화면으로 보이게 하려면 다음 함수를 사용해야 한다.


BOOL ShowWindow(hWnd, nCmdShow);


hWnd 인수는 화면으로 출력하고자 하는 윈도우의 핸들이며 CreateWindow 함수가 리턴한 핸들을 그대로 넘겨주면 된다. nCmdShow는 윈도우를 화면에 출력하는 방법을 지정하며 다음과 같은 매크로 상수들이 정의되어 있다.


매크로 상수 의미
SW_HIDE 윈도우를 숨긴다.
SW_MINIMIZE 윈도우를 최소화시키고 활성화시키지 않는다.
SW_RESTORE 윈도우를 활성화시킨다.
SW_SHOW 윈도우를 활성화시켜 보여준다.
SW_SHOWNORMAL 윈도우를 활성화시켜 보여준다.


nCmdShow 인수에 어떤 값을 넘겨줄 것인가는 전혀 고민할 필요가 없으며 WinMain 함수의 인수로 전달된 nCmdShow를 그대로 넘겨주기만 하면 된다. 그래서 ShowWindow(hWnd,nCmdShow);와 같이 거의 호출 형식이 정해져 있는 셈이다. 설명이 좀 길어지기는 했지만 윈도우를 만들고 화면에 나타내는 코드는 다음 두 줄이다.

	

        hWnd=CreateWindow(lpszClass,lpszClass,WS_OVERLAPPEDWINDOW,
		  CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
		  NULL,(HMENU)NULL,hInstance,NULL);
	ShowWindow(hWnd,nCmdShow);


여기까지 실행하면 화면에 윈도우가 출력된다. 이 후부터는 메시지 루프가 시작되며 프로그램이 사용자와 윈도우즈, 그리고 다른 프로그램과 상호 정보를 교환하며 실행된다. 여기까지 윈도우를 만드는 과정을 간단하게 정리해 보도록 하자.



이 과정은 거의 정형화된 과정이므로 계속 윈도우즈 프로그래밍을 공부할 생각이 있다면 암기할 만도 하다. 이 과정이 WinMain에서 반드시 해 주어야 할 과정이며 그 이외의 처리는 해 주어야 할 필요가 거의 없다.



written by http://www.winapi.co.kr

Comments