[API]강좌(2)<--윈도우의 생성과 다루기 |
|
실제적으로 윈도우를 생성하는 예제에 대해서 알아 보도록 하겠습니다.
윈도우의 생성과 다루기
데스크탑위에 윈도우 생성하기
윈도우즈 프로그래밍을 작성하기 위해서는 항상 windows.h라는 헤더 파일을 포함시켜 주어야 합니다. API 함수가 정의되어 있거든요.
저번시간에 배운 윈도우즈 프로그래밍 형태를 잘 생각해 봅시다. 먼저 윈 메인 함수가 있어야 한다고 했죠. 그러면 먼저 윈 메인 함수에 대해 알아 봅시다.
int WINAPI WinMain
(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszArg, int nCmdShow)
윈 메인 함수는 위와 같은 형태를 취하는데 잘 보세요. 상당히 복잡하죠.
파라미터가 상당히 길고 리턴값을 표시하는 앞에도 요란하게 많이 붙어 있습니다.
그렇다고 겁먹을 필요는 없습니다. 하나하나 알아보죠.
맨 앞에 있는 int는 여러분들도 알다시피 함수의 리턴값을 의미하는 것입니다. 그러면 WINAPI는 뭘까요? 조금 생소할지 모르겠지만 윈도우가 함수를 부를때 순서가 WINAPI 형식을 취한다는 것입니다. 구체적으로 그 순서가 무엇인지는 알 필요는 없습니다. 그냥 그렇게 사용하는 구나라고 알고 있으면 되죠.
기존에 16비트로 코딩할때에는 아마 파스칼 형태로 했기 때문에 WINAPI 대신에 PASCAL이라고 사용했을 겁니다.
그러면 이번에는 파라미터에 대해 알아봅시다.
첫번째로 인스턴스라는 것에 대해 알아봅시다. 앞에서 프로그램마다 고유의 번호인 핸들을 가지다고 했죠. 이것은 이해를 돕기 위해서 그렇게 설명한것이고 사실 핸들은 정확하게 윈도우를 구분하기 위한 번호를 의미하는 것입니다. 그리고 우리가 배울 인스턴스는 프로그램을 구분하기 위한 번호이구요. 윈도우와 프로그램은 차이가 있습니다. 왜냐하면 하나의 프로그램은 많은 윈도우를 가질수 있기 때문이죠.
첫번째 파라미터는 바로 이 프로그램 번호를 의미하는 것입니다. 그러면 두번째 파라미터는 뭘 의미할까요?
윈도우즈에서는 같은 프로그램을 여러번 실행할수 있는 특징이 있습니다. 동일한 프로그램이 다시 실행되었을때 바로 이전에 실행된 동일한 프로그램의 번호가 두번째 파라미터 값이 되는것입니다. 프로그램이 하나만 실행되었다면 hPrevInstance는 어떤 값을 가지게 될까요? 물론 NULL값을 가지게 됩니다. 우리는 지금 윈도우즈 32비트 프로그래밍을 배우고 있습니다. 대표적인것이 윈도우즈 95인데 윈도우즈 95에 서는 같은 프로그램을 여러번 실행해도 어느정도의 메모리 공유는 있지만 서로 다른 프로그램으로 인식하므로 항상 이 hPrevInstance값이 NULL이 됩니다. 그렇다고 파
라미터에서 이 부분을 제외하면 안 됩니다. 항상 위와 같은 형식을 취해야 합니다.
lpszArg는 명령어행에 대한 포인터를 의미하고 nCmdShow는 생성될 윈도우의 형태를 의미하는것입니다. 윈도우가 생성될때 화면 가득히 최대화되서 생성될수도 있고 아이콘 형태로 생성될수도 있죠. 이것은 유저가 정의해 주지 않으면 윈도우즈가 알아서 지정해주는데 그 지정값이 nCmdShow에 들어가는 것입니다.
자 여기까지 메인 함수원형에 대해 알아보았습니다. 그러면 이번에는 윈 메인 함수를 구분짓는 세 개의 파트에 대해 알아보겠습니다. 앞에서 파트1에서 파트3까지 구분할수 있다고 했죠?
우선 첫번째 파트에서 뭘 한다고 했습니까? 윈도우의 속성을 지정해 준다고 했을 겁니다. 윈도우의 속성을 지정하는 방법은 간단합니다. 어떤 특수한 구조체에 속성값을넣어주고 그 구조체의 주소를 파라미터로 하는 함수를 이용해서 지정해 주면 됩니다.
여기서 사용하는 구조체는 WNDCLASS라는 그 구조체인데 아래는 그 구조체의 원형과 설명 입니다.
typedef struct tagWNDCLASS
{
UINT style; //윈도우의 속성
WNDPROC lpfnWndProc; // 윈도우 함수 지정, 앞에서 윈도우 함수 하나가 꼭
// 필요하다고 했죠?
int cbClsExtra; //잘 쓰이지 않음
int cbWndExtra; //잘 쓰이지 않음
HINSTANCE hInstance; //인스턴스 핸들, 이 부분은 유저가 정의하는 부분이
//아니므로 윈도우즈로 부터 할당된 인스턴스 번호를
//저장해야 합니다. 윈메인 함수의 파라미터로 그 값
//이 넘어온다는 것을 알겁니다. 그 값을 그대로 대
//입시켜 주면 되겠죠.
HICON hIcon; //이 윈도우가 최소화 됐을때 사용할 아이콘
HCURSOR hCursor; //커서 모양
HBRUSH hbrBackground; //윈도우 바탕색
LPCTSTR lpszMenuName; //윈도우가 가질 메뉴 이름
LPCTSTR lpszClassName; //윈도우의 클래스 이름
}
위 구조체의 각 멤버에 값을 대입하여 함수를 이용해서 등록하면 됩니다. 이때 등록하는 함수로 RegisterClass()라는 함수를 사용합니다.
ATOM RegisterClass(const WNDCLASS *lpwc);
자 여기까지 하면 속성을 지정하는 파트1 부분이 나것입니다. 그러면 윈도우를 생성해서 보여주는 파트2 부분을 알아봅시다.
이제는 윈도우를 생성해야 하는데 생성할때 사용하는 함수가 물론 있습니다.
한번 이 함수에 어떤 파라미터가 들어갈 지 예상 해봅시다. 윈도우를 생성하는데 필요한 것이 어떤게 있을까요? 윈도우즈 프로그램에 보면 항상 타이틀바(제목바)가 있죠. 이것을 지정하는 파라미터가 있을거 같고 또 생성 좌표와 크기를 지정하는 파라미터도 필요할거 같죠? 뭐 그정도 필요하겠네요. 그럼 실제로 알아봅시다.
HWND CreateWindow(
LPCTSTR lpszClassName,
LPCTSTR lpszWindowName,
DWORD dwStyle,
int x,
int y,
int nWidth,
int nHeight,
HWND hwndParent,
HMENU hmenu,
HANDLE hinst,
LPVOID lpvParam
);
자 위에보면 함수 원형이 하나 있죠? 바로 이 함수를 이용해서 윈도우를 생성하는 것 입니다.
lpszClassName 우리는 이 함수를 사용하기 전에 생성할 윈도우의 속성을 RegisterClass()함수를 이용해서 등록하였습니다. WNDCLASS 맴버중 에 보면 맨 마지막에 클래스 이름을 등록하는 데가 있을 겁니다.
이 곳에 등록되어 있는 클래스 이름과 이 파라미터의 값이 같아야 그 속성이 생성될 윈도우에 적용됩니다.
lpszWindowName 윈도우를 생성하였을때 타이틀바에 제목을 지정할수 있는데 그 제목이 이곳에 등록된 문자열이 되는 것입니다.
dwStyle 생성될 윈도우의 모양을 정의해 주는 곳입니다. 최소화버튼, 최대화버튼등을 생성하도록 하고 싶거나 그렇지 않은 경우에는 이 파라미터를 이용하면 되겠죠.
x 생성될 윈도우의 X 좌표를 지정하면 됩니다.
y 생성될 윈도우의 Y 자표를 지정하면 됩니다.
nWidth 생성될 윈도우의 넓이를 지정하면 됩니다.
nHeight 생성될 윈도우의 높이를 지정하면 됩니다.
hwndParent 윈도우를 포함하고 있는 부모 윈도우의 핸들을 지정해 주면 됩니다
핸들이 뭔지는 대략 짐작하고 있을 겁니다. 다시 설명 드리면 윈도우마다 가지는 고유의 번호가 핸들인데 어떤 윈도우를 생성할때에는 그 윈도우의 부모 윈도우가 있기 때문에 생성함수를 사용할때에 항상 그 부모 윈도우의 핸들을 지정 해야만 합니다.
hmenu 생성되어지는 윈도우가 메뉴를 가지고 있으면 그 메뉴 핸들을 지정 면 됩니다. 이 부분은 메뉴부분에 가면 이해가 될겁니다.
hinst 현재 프로그램의 인스턴스 핸들을 지정하면 됩니다. 윈 메인함수의 첫번째 파라미터 값이 되겠죠.
lpvParam 보통 잘 쓰이지 않기 때문에 NULL로 많이 지정합니다.
자 이제는 윈도우를 생성했습니다. 그러면 뭘 해야 할까요? 바로 메시지 큐에서 메시지를 가져오는 구문이 필요하죠. 바로 이 부분이 파트3에 해당하는 부분입니다.
그러나 한가지 빼먹은것이 있습니다. 그 부분을 먼저 설명드리고 넘어가죠.
위에서 배운 CreateWindow() 함수를 이용해서 윈도우를 생성했다고 해서 화면에 생성한 윈도우가 보이는것은 아닙니다. 실제로 눈에 보이게 하는 작업이 있어야 하거든요. 물론 이것도 함수를 이용해서 할수 있습니다. 두개의 함수가 필요하죠.
BOOL ShowWindow(
HWND hwnd,
int nCmdShow
);
먼저 첫번째 함수로 위 함수를 사용하는데 첫번째 파라미터를 보기 바랍니다. HWND 라는 처음보는 자료형이 있죠? 이것이 바로 윈도우의 핸들을 의미하는 자료형입니다. 실제로 생성할 윈도우의 보여줄 형태를 지정하는 것이 위 함수인데 어떤 윈도우인지 그 핸들을 첫번째 파라미터로 지정해주는 것입니다. 두번째 파라미터는 보여질 형태죠. 보통 윈 메인함수의 마지막 파라미터 값을 지정해 줍니다.
BOOL UpdateWindow(
HWND hwnd
);
이 함수를 설명 하려면 메시지중 WM_PAINT를 설명 드려야 하는데 일단은 위에서 배운 ShowWindow() 함수와 함께 쓰여서 윈도우가 보여지게 한다는 정도만 알고 있으면 됩니다.
이제는윈도우가 보이겠군요. 그럼 메시지 큐에서 메시지를 가져 오는 부분을 보도록 합시다. 메시지가 언제까지 발생될지 모르기 때문에 메시지를 가져오는 부분은 항상 무한 루프문으로 구성하여야 합니다. 그럼 실제로 메시지를 가져오는
함수에 대해 알아 봅시다.
BOOL GetMessage(
LPMSG lpmsg,
HWND hwnd,
UINT uMsgFilterMin,
UINT uMsgFilterMax
);
무한 루프문의 조건을 위 함수의 리턴값으로 하는 형식을 많이 취합니다. 만약에 프로그램을 종료하려는 메시지가 메시지 큐에 있으면 위 함수는 0을 리턴하기 때문에 프로그램이 종료되는것입니다. 그렇지 않은 경우에는 첫번째 파라미터인 메
세지 자료형에 그 메세지를 저장하게 되죠. 두번째 파라미터는 메시지를 가져와서 처리할 윈도우의 핸들이고 세번째와 네번째는 메시지 필터를 의미합니다. 그렇게 많이 사용하지 않기 때문에 보통 0을 지정하는데 게임 SDK를 이용해서 게임을 제작할때에는 이 부분을 컨트롤 할 필요도 생깁니다.
BOOL TranslateMessage(
CONST MSG *lpmsg
);
LONG DispatchMessage(
CONST MSG *lpmsg
);
메시지를 가지고 왔으면 가져온 메시지를 처리해야 겠죠. 첫벗째 함수인 TranslateMessage() 함수는 키보드에 인해서 발생되는 가상 키값을 윈도우 함수에서 체크할수 있는 메시지 형태로 변환하는 역할을 합니다. 좀 어렵죠. 잘 모
르겠으면 그냥 그렇게 사용하는구나라고 알아두시면 됩니다.
두번째 함수인 DispatchMessage() 함수는 가져온 메시지를 WNDCLASS에서 지정한 윈도우 함수로 그 메시지를 보내는 역할을 합니다.
앞에서 이미 알아보았죠. 실제로 메시지를 가지고 처리하는 부분이 윈도우 함수부분 이라는 것을....
자 여기까지 윈 메인 함수에 대해 알아보았습니다. 그러면 윈도우 함수 부분을 볼까요.
LRESULT CALLBACK
WndProc(HWND hWnd, UINT mesg, WPARAM wParam, LPARAM lParam)
위와 같은 형태로 윈도우 함수를 사용하는데 함수 이름이 WndProc으로 되어 있는 것은 WNDCLSS 클래스에 등록 시킨 윈도우 함수 이름과 같으면 달라도 상관 없습니다.
두번째 파라미터가 바로 메시지 큐에서 가져온 메시지가 들어오는 곳입니다. 결국 이 메시지를 분석해서 프로그램을 작성하죠. 나머지 세번째와 네번째는 두번째 파라미터터에 들어가지 못한 부수적인 메시지가 들어옵니다. 이 부수적인 메시지도 상당히 많이 사용하기 때문에 잘 봐둘 필요가 있습니다.
흠.. 이제 대충 다 알아본거 같군요. 그럼 실제로 윈도우를 띄우는 예제의 소스를 보도록 합시다.
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain
(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszArg, int nCmdShow)
{
HWND hWnd;
MSG msg;
WNDCLASS WndClass;
char szAppName[] ="This program is to create window";
WndClass.style = NULL;
WndClass.lpfnWndProc = WndProc;
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = 0;
WndClass.hInstance = hInstance;
WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
WndClass.hbrBackground = GetStockObject(WHITE_BRUSH);
WndClass.lpszMenuName = NULL;
WndClass.lpszClassName = szAppName;
if(!RegisterClass(&WndClass)) return NULL;
hWnd = CreateWindow(
szAppName,
szAppName,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL
);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT mesg, WPARAM wParam, LPARAM lParam)
{
switch(mesg)
{
case WM_DESTROY :
PostQuitMessage(0);
return FALSE;
}
return DefWindowProc(hWnd, mesg, wParam, lParam);
}
다음 시간에는 이 프로그램을 비주얼C 4.1로 컴파일하는 방법과 프로그램 분석을 해 보도록 하겠습니다.
출처 : http://www.hanpoi.wo.to/