Language/WinAPI2011.08.22 17:29
WinApi 에서 윈도우를 만드는 과정을 간단하게 살펴보자.



위와 같은 순서로 윈도우를 만든다. 위의 과정과 같이 소스를 통해 알아보자.

 #include <windows.h>

WinApi 를 사용하기위해서는 위의 헤더파일을 추가해야한다. 윈도우 프로그램에서 필요한 테이터 타입, 함수 원형, 매크로 상수 등이 정의되어 있다. 콘솔 프로그램을 작성할 때 stdio.h 헤더 파일을 추가하는 것과 같다. 메인 함수를 살펴보자.

 
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance
					 , LPSTR lpszCmdParam, int nCmdShow)
{
	                    ...
}

프로그램 시작점인 엔트리 포인트가 기존의 콘솔 프로그램의 main 함수가 아닌 WinMain 함수이다. 이 함수 앞에 사용한 APIENTRY 는 윈도우의 표준 호출 규약인 __stdcall 을 사용한다는 의미이다. 그리고 다음 4 개의 인자를 사용한다. 살펴보자.

hInstance
프로그램의 인스턴스 핸들, 프로그램 자체를 일컫는 정수값을 의미한다. 즉 자기 자신을 가리키기 위해 사용한다.

hPrevInstance
바로 앞에 실행된 현재 프로그램 인스턴스 핸들을 의미하며 없는 경우는 NULL 을 사용한다. (Win32 는 항상 NULL)

lpszCmdParam
명령행으로 입력된 프로그램 인수이다. (도스의 argv 인수와 동일)

nCmdShow
프로그램이 실행될 형태를 의미한다. (최소화, 숨김 등)

윈도우 클래스에 대해 알아보자. 윈도우 클래스는 구조체이며 다음과 같이 선언되어 있다.

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

각각의 구조체 멤버에 대해 알아보자.

style
윈도우가 어떤 형태를 가질 것인가를 지정하는 멤버이다. 자주 사용하는 CS_HREDRAW 와 CS_VREDRAW 사용하며 이러한 값들을 OR 연산자로 연결하여 사용할 수 있다.

lpfnWndProc
윈도우의 메시지 처리 함수를 지정한다. 메시지가 발생할 때마다 이 멤버가 지정하는 함수가 호출되며 이 함수가 모든 메시지를 처리한다.

cbClsExtra, cbWndExtra
아주 특수한 목적에 사용하는 여분의 공간으로 사용하지 않을 경우는 0으로 지정한다.

hInstance
윈도우 클래스를 등록하는 프로그램의 번호이다.

hIcon, hCursor
윈도우가 사용할 마우스 커서와 아이콘을 지정한다.

hbrBackground
윈도우의 배경 색상을 지정한다. 윈도우 배경 색상을 채삭할 브러시를 지정하는 멤버이다. GetStockObject 함수를 이용하여 윈도우에서 기본적으로 제공하는 브러시를 지정하거나 COLOR_WINDOW 같은 시스템 색상을 지정할 수 있다.

lpszMenuName
프로그램이 사용할 메뉴를 지정한다. 사용하지 않을 경우 NULL 을 대입한다.

lpszClassName
윈도우 클래스의 이름을 문자열로 정의한다.

위의 윈도우 클래스의 구조체의 멤버변수에 값을 설정하여 어떠한 윈도우를 만들것인지 정의를 한다. 다음과 같이 설정할 수 있다.

// 예약 영역 (특수한 목적에 사용) 사용하지 않으면 0
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 = lpszClass;
// 프로그램이 사용할 메뉴 지정
WndClass.lpszMenuName = NULL;
// 윈도우 스타일 정의 윈도우의 형태를 OR 연산자를 사용하여 지정
WndClass.style = CS_HREDRAW | CS_VREDRAW;

윈도우 클래스의 정의가 끝이 나면 이 윈도우를 등록을 해야한다. 등록을 하기 위해서는 다음 함수를 호출한다.

/*** 클래스 등록 ***/
RegisterClass(&WndClass);	// WndClass 구조체의 번지를 전달

등록을 한 후에 비로서 윈도우를 생성한다. 다음과 같이 생성을하고 있다.

/*** 메모리상에 윈도우 생성 ***/
hWnd = CreateWindow( lpszClass, lpszClass, WS_OVERLAPPEDWINDOW,
			CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
			NULL, (HMENU)NULL, hInstance, NULL);

인자값이 상당히 많다. 하나 하나씩 살펴보자.

HWND CreateWindow ( lpClassName, lpWindowName, dwStyle, x, y,
                                nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam )

lpClassName
생성하고자 하는 윈도우의 클래스를 지정하는 문자열을 의미한다.

lpWindowName
윈도우의 타이틀 바에 나타날 문자열을 의미한다.

dwStyle
윈도우의 형태를 지정하는 인수이다. 비트 필드값으로 OR 연산자로 연결할 수 있다.

x, y
윈도우의 위치 지정을 설정한다. CW_USEDEFAULT 는 운영체제가 자동으로 설정해 준다.

nWidth, nHeight
윈도우의 크기 지정을 설정한다. CW_USEDEFAULT 는 운영체제가 자동으로 설정해 준다.

hWndParent
부모 윈도우의 핸들을 지정한다. (없는 경우 NULL)

hMenu
메뉴 핸들을 지정한다. (사용하지 않으면 NULL)

hInstance
윈도우를 만드는 주체인 프로그램 핸들을 지정한다.

lpParam
CREATESTRUCT 구조체의 주소 여러개의 윈도우를 만들 때 윈도우 고유의 파라미터를 전달하는 특수한 목적에 사용한다. (보통은 NULL)

CreateWindow 의 리턴값은 윈도우를 대표하는 번호인 윈도우 핸들을 리턴한다.

윈도우를 생성하였다면 이제는 윈도우가 화면에 출력을 해주는 함수를 호출한다.

ShowWindow ( hWnd, nCmdShow );

hWnd 는 윈도우의 핸들이며, nCmdShow 는 윈도우 화면에 출력하는 방법을 지정하는 인자이다. 이 인자는 매크로 상수로 정의 되어 있으며 다음과 같다.

SW_HIDE   
윈도우 숨김

SW_MINIMIZE  
윈도우 최소화 및 비활성화

SW_RESTORE  
윈도우 활성화

SW_SHOW   
윈도우 활성화하여 보여줌

SW_SHOWNORMAL 
윈도우 활성화하여 보여줌

이렇게 윈도우를 정의하고 등록하여 메모리상에 윈도우를 만들고 이를 출력하도록 하였다. 이제는 사용자로부터의 메시지를 처리하는 부분을 살펴보자.

메시지는 사용자가 시스템의 내부적인 동작에 의해 발생된 일체의 변화에 대한 정보를 말한다. 순서를 따르지 않고 주어진 메시지에 대한 반응을 정의하는 방식을 이용하여 프로그램이 실행된다.  이렇게 윈도우 프로그램에서 메시지를 처리하는 부분을 메시지 루프라고 하며 WinMain 함수의 끝에 보통 존재한다. 그 형태는 다음과 같다.

/*** 메시지 루프 - WndProc 에 메시지를 보내줌 ***/
// 메시지 큐에서 메시지 읽어옴 WM_QUIT 면 FALSE 리턴
while( GetMessage(&Message, NULL, 0 , 0) )
{
	TranslateMessage(&Message);	// 키보드 입력 처리 
	DispatchMessage(&Message);	// 큐에서 꺼낸 메시지를 WndProc 함수의 iMessage 로 전달
}

3 개의 함수를 통하여 메시지를 처리한다. 하나씩 살펴보자.

BOOL GetMessage ( LPMSG lpMsg, HWND hWnd, UINT wMsgFiterMin, UINT wMsgFiterMax );

GetMessage 함수는 메시지 큐에서 메시지를 읽어들이는 역할을 한다. 메시지 큐는 시스템이나 사용자로 부터 발생된 메시지가 잠시 대기하는 일종의 메시지 임시 저장 영역이다. 메시지 큐에서 읽어왔을 때 WM_QUIT 이면 FALSE 를 리턴하고 그외에는 TRUE 를 리턴한다.

BOOL TranslateMessage ( CONST MSG *lpMsg );

키보드의 눌림 메시지가 발생할 때 문자가 입력되었다는 메시지를 만드는 역할을 한다. 즉 키보드의 A 키를 누르면 A 문자가 입력되었다는 메시지를 만든다.

LONG DispatchMessage ( CONST MSG *lpmsg );

메시지 큐에서 꺼낸 메시지를 윈도우의 메시지 처리 함수인 WndProc 로 전달한다. 이 때 WndProc 함수를 호출하는 것이 아님을 유의하자. WndProc 함수는 OS 가 호출하는 것이다.

정리를 하자면 메시지 루프에서 하는 일은 메시지 큐에서 메시지를 꺼내 메시지 처리 함수로 보내는 것이다. 이러한 메시지를 보내기 위해 MSG 라는 구조체를 사용한다. 이 구조체는 다음과 같이 정의 되어있다.

typedef struct tagMSG
{
    HWND        hwnd;
    UINT        message;
    WPARAM      wParam;
    LPARAM      lParam;
    DWORD       time;
    POINT       pt;
} MSG;

구조체의 멤버에 대해 알아보자.

hwnd
메시지를 받을 윈도우 핸들이다.

message
어떤 종류의 메시지인가를 나타낸다. 가장 중요한 값이된다.

wParam, lParam
전달된 메시지에 대한 부가적인 정보를 가진다. 메시지별로 의미가 달라진다.

time
메시지가 발생한 시간이다.

pt
메시지가 발생했을 때의 마우스 위치이다.

메시지는 실제로 정수값으로 표현되며 메시지의 종류가 엄청 많다. 보통 WM_ 로 시작되는 것들이 메시지이다. 주로 자주 사용하는 메시지를 살펴보자.

WM_QUIT 
프로그램을 끝낼 때 발생하는 메시지이다.

WM_LBUTTONDOWN 
마우스의 좌측 버튼을 누를 경우 발생한다.

WM_CHAR 
키보드로부터 문자가 입력될 때 발생한다.

WM_PAINT 
화면을 다시 그려야 할 필요가 있을 때 발생한다.

WM_DESTROY 
윈도우가 메모리에서 파괴될 때 발생한다.

WM_CREATE 
윈도우가 처음 만들어질 때 발생한다.


메시지를 WndProc 함수에 전달한다. 이 전달받은 메시지를 OS 에 의해 호출되면 WndProc 함수가 처리를 한다.  이렇게 운영체제에 의해 호출되는 응용 프로그램 내의 함수를 콜백 함수라고 한다. 콜백 함수를 살펴보자.

LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
	/* 고정 형식 */
	switch(iMessage)	//  운영체제로부터 들어온 메시지에 대한 처리
	{
		case WM_DESTROY :
			PostQuitMessage(0);	// WM_QUIT 메시지를 보냄
			return 0;
	}

	// switch 문에서 처리하지 않은 메시지를 처리
	return (DefWindowProc(hWnd, iMessage, wParam, lParam));
}

WndProc 함수의 인자를 살펴보면 wParam 과 lParam 은 iMessage 의 메시지에 따른 부가적인 정보를 가진다. 메시지가 마우스 왼쪽 버튼을 눌렀다면 위의 두 인자는 화면의 어디쯤에서 버튼이 눌러졌는가의 추가 정보를 이 두 인자에 전달된다. 따라서 메시지에 따라서 값이 달라진다.
콜백 함수의 본체에는 기본적인 형태는 다음과 같다.

switch(iMessage)
{
	case Msg1:
		처리1;
		return 0;
	case Msg2:
		처리2;
		return 0;
	case Msg3:
		처리3;
		return 0;
	default:
		return DefWindowProc(...);
}

메시지에 따라 어떻게 처리할 것인지 정의하고 처리가 끝이나면 0 을 리턴시킨다. 제일 끝에 있는 DefWindowProc 함수는 WndProc 에서 처리하지 않은 나머지 메시지에 관한 처리를 한다. 전체적인 흐름도는 다음과 같다.




'Language > WinAPI' 카테고리의 다른 글

RegisterClass 함수 - 20110904  (0) 2011.09.04
WinMain 함수 - 20110902  (0) 2011.09.02
SendMessage 함수 - 20110902  (0) 2011.08.30
Timer (타이머) - 20110830  (0) 2011.08.30
mouse (마우스) - 20110830  (0) 2011.08.30
WinAPI 의 윈도우를 만드는 과정  (0) 2011.08.22
Posted by 꿈을모아서

댓글을 달아 주세요