1.윈도우즈 프로그래밍

본장은 윈도우 프로그래밍은 어떤 방식으로 하는가에 대한 전반적인 설명을 합니다. 윈도우 프로그래밍에서 알아야할 것은 두가지입니다. 첫 번째로 어떤 형태로 윈도우즈 프로그래밍을 하는가에 대한 이론 (왜 그렇게 되었는가?라는 Why의 개념) 그리고 어떤 함수를 사용해야 하는가?(How의 개념) 이두가지입니다. 본장에서 목표를 하고자 하는 것은 Why의 개념입니다. 본장을 통해서 윈도우 프로그래밍을 어떻게 하는가를 이해하시게 될것입니다.

1.윈도우즈 프로그래밍 형식

이제 C언어를 이해하고 윈도우 프로그래밍에 첫발을 디디게 되었습니다. 본편에서는 윈도우 프로그램을 C언어와 Win32 SDK라이브러리를 이용해서 프로그래밍하는 방법을 설명합니다. SDK이란 Standard Developer Kit의 약자입니다. 해석을 한다면 표준 개발 킷트라는 의미입니다. 즉 윈도우에서 프로그래밍을 하고자 할 경우 표준으로 사용해야할 라이브러리라는 의미입니다. 저편에서 C에서 기본적으로 제공하는 표준 라이브러리와 같이 윈도우에서 프로그래밍을 할려면 SDK를 이용해야 한다는 의미입니다. C++로 프로그래밍을 할 경우 SDK와 함께 편리하게 이용할수 있는 라이브러리가 있는데 이것이 MFC(Micorsoft Foundation Class)입니다. 이것또한 SDK를 기본으로 클래스를 구성한것입니다. 따라서 지금부터는 SDK에서 제공하는 함수를 어떻게 사용하는가와 프로그램형식을 어떻게 하는가를 배우게 되는 것입니다.

윈도우 모드 프로그래밍

윈도우95이후부터는 32비트 OS이며 프로텍트 모드용  OS입니다. 또한 멀티테스킹이 가능한 OS입니다. Preemptive multitasking를 지원합니다. 이모든 것이 무슨 의미인지 C언어를 배우고 난 우리에게는 생소한 이야기 일것입니다. 필자는 윈도우즈 프로그래밍을 유부남이라고 하고 일반 프로그래밍 (보통 리얼모드나 텍스트상태에서 프로그래밍)을 총각이라고 합니다.

총각때는 자기가 돈을 벌면 자기 마음대로 쓸수 있습니다. 또한 어디 놀러가고자 한다면 누구 허락도 없이 마음대로 놀러갈수 있으며 시간이 허락하는한 재정이 허락하는한 자기가 가지고 있는 자원을 자신의 뜻대로 마음대로 사용할수 있다는 것입니다. 그러나 반면에 유부남은 월급을 타더라도 자기 마음대로 사용할수 없습니다. 고스란히 통장으로 입금되어 마누라님 손으로 들어가고 매일 매일 마누라님이 주는 용돈에 의거해서 생활해야 합니다. 유부남은 어디 놀러가고 싶다고 마음대로 몇날 몇일을 놀러갈수 없습니다. 만약 유부남이 자기 마음대로 놀러가고 자기 마음대로 월급을 횡령(?) 한다면 마누라님이 가만히 있을까요? 공포의 고무장갑이 유부남에게 날라올것입니다. (그것도 물넣어서 냉장고에 꽁꽁 얼린 고무장갑이요)

유부남은 모든 일에 있어서 꼭 마누라님에게 허가를 받아야 하고 그렇기 때문에 행동이 부자유 스럽습니다. 왜냐 마누라님 때문에..

그러나 유부남에게도 좋은점은 있습니다. 예를 들어서 밤새도록 프로그래밍을 한다고 합시다. 총각은 밤새우며 프로그래밍 하다가 라면을 먹고 싶다든지 또는 커피를 마시고 싶다면 프로그래밍 하는 것을 멈추고 물을 끓이는 작업을 해야합니다. 즉 한번에 한가지일을 해야 한다는 것입니다. 또한 총각은 자신의 행동을 자신이 절제를 해야 합니다. 무리하여 돈을 쓰고 놀러가고 하였다가는 잘못하면 재정상태가 마이너스가 될경우가 많습니다. (카드값이 엄청나오는 경우 말입니다)

유부남의 경우에서는, 밤새 프로그래밍하다가 커피를 마시고 싶거나 라면을 먹고 싶다고 하였을 경우 □색시야! 커피!□ 또는 □색시야! 라면!□ 이라는 말만하고 다시 프로그래밍에 열중할수 있습니다.그러면 마누라님은 바로 커피를 대령하고 라면을 끓여줍니다. 즉 멀티 테스킹이 가능하다는 말입니다. (물론 이렇게 되기 위해서는 □자기 오늘따라 너무 아름답다!□ 라는 아부성 발언이 필요합니다만) 유부남은 받은 용돈을 탕진해도 제정상태에 문제점이 없습니다. 메인 재정은 마누라님 손에 있기 때문입니다. 따라서 유부남은 자신에게 주어진 자원을 자기 마음대로 마구 써도 됩니다.(그 자원이 많다면요..)

리얼모드 프로그램은 (보통 DOS프로그램) 총각시절을 닮았습니다. 시스템의 메모리 키보드 모니터등을 프로그램자체가 점유하고 프로그램이 마음대로 사용할수 있습니다. 그렇기 때문에 자유스러운 프로그램일수도 있지만 한편으로는 시스템의 모든 것을 관리해야 하기 때문에 프로그램이 매우 복잡하고 불편할때도 있습니다. 모니터에 그래픽으로 한 라인을 그리고자 할려면 시스템을 모드 핸들링하여야 하기 때문에 적어도 5000줄 정도가 되는 그래픽 기반의 라이브러리를 먼저 구축을 해야합니다. 또한 사운드 카드를 이용하여 음성을 출력하고자 한다면 사운드 카드를 일일이 직접 제어를 해야합니다.

윈도우에서의 프로그램은 유부남을 닮았습니다. 커널이 마누라님이라고 보고 프로그램이 유부남이라고 보면 됩니다. 프로그램은 언제나 마누라님에게 보고를 해야하며 허가를 받아야만 시스템의 메모리 키보드 모니터 등의 자원을 사용할수 있습니다. 멋대로 사용하다가는 윈도우 95에서는 공포의 파란화면을 목격하시게 될것입니다.(이것이 공포의 고무장갑과 대응됩니다) 그러나 윈도우에서는 라인을 그릴 경우 단 한줄의 프로그래밍으로 가능합니다. 또한 사운드 카드에 음성을 출력하고자 할경우도 간단하게 출력할수 있습니다. 그이유는 커널이라는 마누라님에게 요청만하면 하드웨어의 핸들링은 커널이 알아서 해주기 때문입니다. 커널이 시스템의 자우원을 점유하고 있고 이것을 컨트롤하는 기능마저 가지고 있기 때문입니다. 이런 의미로 윈도우 프로그래밍은 리얼모드에서의 프로그래밍보다 쉽습니다. 그러나 어렵게 느껴지는 것은 마누라님에게 허가를 받고 또한 마누라님과의 상호대화하는 방법을 모르기 때문입니다. 위에서 예를 들었듯이 □색시야! 커피!□ 한다고 바로 커피가 오지 않습니다. 미리 □자기 오늘따라 자기가 무척 아름답다!□ 라는 아부성 발언으로 초기화를 해야 합니다. 초기화 하지 않고 바로 □색시야! 커피!□ 하면 공포의 고무장갑이 날아올것입니다.

윈도우 프로그래밍 기본 구조

윈도우 프로그래밍은 간단하게 보면 두가지로 볼수 있습니다. 초기화하는 부분과 다음은 커널에게 허가를 받기위한 또는 커널이 점유하고 있는 시스템의 상황을 얻기 위한 컨널과의 통신 부분입니다. 그림1은 윈도우 프로그래밍의 기본구조를 그림으로 도시한것입니다. 윈도우 프로그래밍은 쉽게 보면 먼저 초기화를 하는 부분과 그다음은 커널과 통신을 하기위한 메시지 교환 부분으로 볼수 있습니다.

(그림 1) 윈도우즈 프로그래밍 형태

이렇게 하는 이유는 모든 시스템 자원을 커널이 보유하고 있기 때문입니다 결국 어플리케이션 프로그램은 커널의 허가가 없이는 아무것도 할수 없다는 의미입니다. (전에 설명한 유부남을 상기하시기 바랍니다)

이렇게 보면 윈도우 프로그램은 다음과 같은 두 개의 함수가 필요할것입니다.

void Init()

{

//커널에 초기화 하는 부분

}

void massagetranslation()

{

//메시지를 주고 받는 부분

}

실제 프로그램에서도 위의 두 개의 형태로 구성됩니다. 커널에 초기화 하는 부분이 WinMain이며 메시지를 처리하는 부분이 WndProc입니다.

int WINAPI WinMain (......)

{

초기화 부분

}

LRESULT CALLBACK WndProc (....)

{

메시지 처리 루틴

}

유부남이 초기할 때 □자기 오늘따라 자기가 무척 아름다워□ 라는 몇자 안되는 이야기를 했듯이 WinMain부분은 매우 작습니다. 초기화는 매우 간단하기 때문입니다. 실제로 프로그램은 메시지 처리루틴에서 작성됩니다. 유부남이 밤새도록 작업을 한다고 할 경우 초기화에 의해서 커피이야기를 하지 않았는데도 자동적으로 갔다주고 때때로 간식도 말하지 않았는데도 배달이 됩니다. 마찬 가지로 WinMain에서 초기화를 시키면 커널은 원하던 원하지 않던 여러 부분의 내용을 메시지로 전송합니다. □지금 키보드 눌렸음□ ,□마우스가 움직이고 있음□,□모니터에서 현재 윈도우 위치가 변하고있으□ 등등 수많은 메시지가 날아옵니다. 실제 프로그램은 이메시지를 처리하는 것이 대부분입니다. 예를 들어 에디터 프로그램은 키보드를 입력 메시지를 받고 그값에 의거한 데이터를 저장하고 그것을 화면에 출력해 주면 됩니다. 이런작업을 하고자 한다면 이것 또한 WndProc에 해야합니다. 다음과 같은 루틴이 될것입니다.

LRESULT CALLBACK WndProc (....)

{

switch(메시지)

{

case 키보드 눌렸음:

버퍼에 킷값을 저장함

break;

case 화면출력해도 됨:

버퍼 내용을 화면에 출력한다.

break;

}

}

커널에서 들어온 메시지에 의해서 필요한 부분들을 switch문을 이용하여 처리하여 주는 루틴이 되는 것입니다. 커널은 필요한 모든 메시지를 초기화한 윈도에 날려줍니다. 여기에서 이메시지에 의거한 모든 내용을 전부 프로그램이 처리해야 할까요? 그럴 필요는 없습니다. 지금까지 C언어를 배우기 위해서 하나의 윈도우를 띄었습니다. 이 윈도우는 마우스를 이용하여 이동을 시킬수 있습니다. 마우스로 이동시킬 때 커널은 □현재 윈도우가 이동되는 상태임!□ 이라는 메시지를 프로그램에 알려줍니다. 만일 모든 메시지를 프로그램이 처리해주아야 한다면 이메시지가 날아올때마다 위치를 확인하고 윈도우를 모두 다시 그려주어야 합니다. 이것은 유부남 형태가 아닙니다. 유부남은 커피가 오면 마시고 싶으면 마시고 그렇지 않으면 거부하면 그커피는 마누라님이 알아서 처리합니다. 마찬가지로 필요한 부분만 프로그램이 처리하고 나머지는 모두 커널에게 맏기는 것입니다. 따라서 마우스를 이용하여 윈도우를 이동시키는 것은  또는 윈도우 좌측 상단에 있는 시스템 메뉴를 클릭하여 윈도우 크기를 조절하는것등등 잡스러운것들 모두 커널에게 의뢰를 할수 있습니다.

이런 형태로 WndProc는 다음과 같이 변할수 있습니다.

LRESULT CALLBACK WndProc (....)

{

//필요한 메시지만 처리하고

switch(메시지)

{

case 키보드 눌렸음:

버퍼에 킷값을 저장함

break;

case 화면출력해도 됨:

버퍼 내용을 화면에 출력한다.

break;

}

CallKernel();//나머지는 커널에게 다시 의뢰를 한다.

}

CallKernel이라는 함수가 있다는 것이 아니라 위와 같은 형식이라는 것입니다.

결론으로 윈도우 프로그래밍의 기본구조는 WinMain이라는 초기화 함수와 메시지를 처리하는 WndProc라는 두 개의 함수로 나누어져 있다는것입니다. 그리고 실제 프로그램은 WndProc를 이용하여 하고 WinMain에서는 단순히 초기화만 할뿐이라는 것입니다.

WinMain 함수

윈도우 프로그래밍에서 초기화하는 부분을 WinMain이라고 하였습니다. 리얼모드 프로그램에서 main()으로 시작하듯 윈도우에서는 WinMain으로 프로그램이 시작됩니다. 프로그램이 컴파일될 때 프로그램의 시작점을 바로 WinMain에서부터 찾기 때문입니다. WinMain을 찾아야만 초기화를 하고 그다음부터 메시지 처리를 할수 있기 때문입니다. 이 WinMain은 다음과 같은 기본형태로 설정해주어야 합니다.

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,

PSTR szCmdLine, int iCmdShow)

WINAPI라는 형은 윈도우 어플리케이션 이라는 의미입니다. 어플리케이션이란 윈도우상에서 구동하는 일반적인 모든 프로그램을 의미합니다. hInstance 란 프로그램 인스턴스라고 이야기 하는데 쉽게 보면 어플리케이션 프로그램의 ID입니다. 어플리케이션 프로그램이 로드되면 커널은 그 윈도우에게 해당하는 ID를 부여해 줍니다. 쉽게 이해할수 있기 위해서 예를 들어본다면 극장에 들어가고자 할 경우 매표소에서 표를 구입하면 좌석번호를 부여 해주듯이 윈도우 시스템에서  프로그램이 구동되고자 하면 커널은 우선적으로 번호를 부여해줍니다. 이것을 인스턴스라고 합니다. 이 인스턴스튼 프로그램이 실행되고 있는 어떤 공간을 가르키는 포인터라고 보시면 됩니다.  hPrevInstance는 같은 프로그램이 이전에 구동되었을 때 설정되는 인스턴이스인데 윈도우95이후부터는 사용을하지 않으므로 그냥 써준다고 생각하고 무시하시기 바랍니다. szCmdLine는 프로그램과 함께 들어오는 인자들입니다. 도스모드에서 dir *.c 라고 구동을 해보셨을것입니다. 이때 dir는 *.c라는 인자를 받아서 확장자가 c인 파일을 모두 화면에 보여 줍니다. 이때  *.c라는 것이 윈도우에서는 szCmdLine로 들어옵니다. 예를 들어서 test라는 프로그램을 만들었을 때 도스모스에서 test listdata.test 라고 test다음에 listdata.test라고 써주면 이값이 szCmdLine에 설정되는 것입니다. iCmdShow에는 윈도우가 처음 출력될때는 값이 설정됩니다. 보통 iCmdShow는 화면에 보여지는 값으로 설정됩니다. 그냥 화면에 출력되는 형태가 어떤형태가 좋은지를 알려주는값이라고 생각하고 이해가 안가면 □음 그냥 써야 하나보다□ 라고 생각을 하시기 바랍니다. WinMain의 초기 인자에서 중요한 것은 hInstance입니다. 이것은 프로그램이 구동할수 있는 좌석표이기 때문입니다. 나머지 인자들은 그냥 써야 되나보다 라고 생각하시기 바랍니다.

초기화 방법

윈도우에서 초기화방법은 두가지입니다. 첫 번째는 윈도우 클래스를 등록하는 것이고 두 번째는 메인 윈도우를 만드는것입니다. 윈도우 안에 여러 윈도우들이 있습니다. 그림 2는 Visual C++의 형태로 화면에 여러 부수적인 윈도우들이 보이는 형태입니다.

(그림 2) Visual C++의 화면

그림 2에서 보면 Exam17.c와 Exam02.c가 가 출력된 윈도우가 있으며 이거을 감싸고 있는 메인 윈도우가 있습니다. 그림2에서 Exam17.c가 출력되는 윈도우와 Exam02.c가 출력되는 윈도우는 스타일이 같습니다. 즉 같은 속성을 가진 윈도우라는 의미입니다. 그러나 외곽의 윈도우 다시말하면 메인 윈도우는 Exam02.c나 Exam17.c와 같은 스타일이 아닌 다른 스타일입니다. 위의 그림형태만 보면 그림2에서 보이는 윈도우 클래스는 2개입니다. 즉 에디터기능이있는 윈도우 클래스와 메뉴를 가지고 있는 윈도우 클래스 두 개인것입니다.

커널에 초기화 할 때는 본프로그램이 사용하는 클래스를 등록하는 것이 첫 번째입니다. 이때 사용하는 함수가 RegisterClassEx입니다.

두 번째로 메인 윈도우를 만든다고 했는데 메인윈도우를 함수는 CreateWindow입니다. CreateWindow함수는 메인윈도우만 만드는 것이 아니라 윈도우를 만들 때 사용하는 대표적인 함수입니다. 이렇게 보면 WinMain은 다음과 같이 설정할수 있습니다.

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,

PSTR szCmdLine, int iCmdShow)

{

RegisterClassEx () ;//클래스를 등록하고

CreateWindow ()//윈도우를 만든다.

}

그림2에서 처럼 클래스가 두 개일경우에는 RegisterClassEx함수를 이용해서 또하나의 클래스를 등록합니다.

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,

PSTR szCmdLine, int iCmdShow)

{

RegisterClassEx (메인윈도우스타일) ;//클래스를 등록하고

RegisterClassEx (차일드 윈도우 스타일) ;//클래스를 등록하고

CreateWindow ()//메인 윈도우를 만든다.

}

프로시저

윈도우는 Message Driven방식이라고 합니다. 즉 모든 내용을 메시지를 이용하여 통신을 하는 것이 대부분인 프로그램 방식이라는 것입니다. 이런 메시지를 처리하는 함수를 프로시저라고 합니다. 보통 함수명다음에 Proc를 붙은 함수를 만들면 이것이 프로시저라고 보시면 될것입니다.

FuncProc

프로시저는 한 개의 함수일수가 없습니다. 그림2에서 보면 Exam02.c를 감싸고 있는 윈도우는 키보드 입력 메시지에 따라 화면 출력을 해야 하며 메인 윈도우는 마우스가 클릭되는 데에 따라서 메뉴가 실행되어야 합니다. 즉 해당특성이 다른다는것입니다. 쉽게 보면 윈도우 클래스마다 수행되는 프로시저가 다릅니다. 뒤에서 계속 배우겠지만 프로시저는 윈도우 하나하나마다 다르게 설정할수 있습니다. 또한 그렇게 설정해야 합니다. 윈도우에서 프로그램을 만드는과정은 여러개의 프로시저를 만드는 과정이라고 보아되 되기 때문입니다. 이렇게 보면 WinMain함수는 단 한 개이지만 WndProc로 설정한 하나의 프로시저를 가지고는 사용할수 없기 때문에 윈도우 클래스나 윈도우에 프로시저를 따로따로 설정해주어야 합니다.

WinMain함수가 다음과 같은 형식으로 바꾸어져야 한다는 것입니다.

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,

PSTR szCmdLine, int iCmdShow)

{

//메인 윈도우 클래스

RegisterClassEx (메인윈도우스타일,프로시저 -> WndProc) ;

//차일드 윈도우 클래스

RegisterClassEx (차일드 윈도우 스타일,프로서저 -> ChildProc) ;

CreateWindow ()//메인 윈도우를 만든다.

}

LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)

{

switch(iMsg)

{

메뉴 메시지가 발생되었을 때 처리

}

나머지는 커널에 의뢰

}

LRESULT CALLBACK ChildProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)

{

switch(iMsg)

{

키보드 입력이 있었을 때 처리

}

나머지는 커널에 의뢰

}

위의 예를 본다면 메인 윈도우 클래스를 등록하면 이 윈도우에 해당하는 프로시저 함수를 설정해주어야 하고 또한 차일드 윈도우를 등록한다면 이에 해당하는 프로시저함수를 등록해주면 각각의 프로시저를 새로 만들어서 해당되는 내용을 코딩하는 것입니다.

Exam02.c의 WinMain분석

이제 윈도우가 어떤 형태로 프로그래밍이 되는가에 대한 기본을 이해하셨을것입니다. 지금까지 C언어를 배우기 위해서 윈도우를 하나 출력하기 위해 아무것도 모르면서 Exam02.c를 프로젝트에 설정하고 사용하였습니다. 이제 Exam02.c가 어떤 형태인가를 알아보겠습니다.

WinMain함수는 다음과 같을것입니다.

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,

PSTR szCmdLine, int iCmdShow)

{

이것은 이해가 될것입니다. 이제 두 번째로 초기화의 첫 번째 순서인 윈도우 클래스를 등록합니다. 지금까지 사용한 윈도우는 단지 한 개의 클래스 였습니다. 그렇기 때문에 RegisterClassEx 함수는 단한번 사용할것입니다. 실제 RegisterClassEx 함수는 다음과 같습니다.

ATOM RegisterClassEx(

CONST WNDCLASSEX *lpwcx //윈도우 클래스 정보

);

lpwcx는 구조체입니다. 따라서 RegisterClassEx를 수행하기 위해서는 WNDCLASSEX 구조체의 변수를 만들고 이 구조체의 맴버에 값을 넣어주고 이것을 RegisterClassEx 에 넘겨주어야 합니다. 먼저 WNDCLASSEX 구조체의 형태를 알아보겠습니다. WNDCLASSEX 는 다음과 같습니다.

typedef struct _WNDCLASSEX {

UINT    cbSize; //본 구조체의 크기

UINT    style; //출력 스타일

WNDPROC lpfnWndProc; //프로시저 함수

int     cbClsExtra; //클래스 여분 메모리

int     cbWndExtra; //윈도우 여분 메모리

HANDLE  hInstance; //윈도우 인스턴스

HICON   hIcon; //아이콘

HCURSOR hCursor; //커서

HBRUSH  hbrBackground;//배경색

LPCTSTR lpszMenuName; // 메뉴이름

LPCTSTR lpszClassName; //클래스 이름

HICON   hIconSm; //작은 아이콘

} WNDCLASSEX;

style는 출력스타일입니다. 이값은 windows.h에 정의된 CS_계열의 플러그를 사용할수 있는데 보통 CS_HREDRAW | CS_VREDRAW  값을 씁니다. 나머지는 본 윈도우 프로그램밍 편을 다읽고 조사해 보시기 바랍니다. CS_HREDRAW | CS_VREDRAW  는 가로로 윈도우가 변하거나 세로로 윈도우가 변할 때 다시 출력하라는 의미입니다. lpfnWndProc 에는 프로시저 함수 이름을 설정해 줍니다. hInstance는 WinMain의 첫 번째 인자로 받은 인스턴스이며 hbrBackground는 윈도우 배경색을  설정할 브러쉬입니다. 나머지 인자들은 책을 공부하면서 계속적으로 배우시게 될것입니다.

이제 클래스를 하나 등록해 보겟습니다.

WNDCLASSEX  wndclass ;

wndclass.cbSize        = sizeof (wndclass) ;

wndclass.style         = CS_HREDRAW | CS_VREDRAW ;

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 = (HBRUSH) GetStockObject (WHITE_BRUSH) ;

wndclass.lpszMenuName  = NULL ;

wndclass.lpszClassName = szAppName ;

wndclass.hIconSm       = LoadIcon (NULL, IDI_APPLICATION) ;

RegisterClassEx (&wndclass) ;

자세히 알지는 못한다 하더라도 클래스를 등록할경우에는 위와 같이 해야 한다고 기억하시기 바랍니다. hIcon과 hCursor그리고 hbrBackground lpszMenuName,hIconSm등은 후에 계속적으로 설명하면서 사용할것입니다. lpszClassName란 이클래스의 이름을 의미합니다. 이것은 여러분이 원하는대로 문자열을 설정하여 등록해 주시면 됩니다.

이제 두 번째로 메인 윈도우를 만드는 작업이 있습니다. 이것은 다음과 같습니다.

hwnd = CreateWindow (szAppName,

"Exam02",

WS_OVERLAPPEDWINDOW,

CW_USEDEFAULT,

CW_USEDEFAULT,

CW_USEDEFAULT,

CW_USEDEFAULT,

NULL,

NULL,

hInstance,

NULL) ;

ShowWindow (hwnd, iCmdShow) ;

UpdateWindow (hwnd) ;

CrateWindow의 첫 번째 인자는 클래스이름입니다. 클래스 이름을 보고 커널은 해당 윈도우클래스에 등록되어 있는 프로시저 함수로 메시지를 전송합니다. CreateWindow는 위에 RegisterClassEx를 이용하여 등록한 wndclass 스타일이므로 wndclass의 맴버인 lpszClassName 명과 같은 이름을 써주어야 합니다. 두 번째 이자는 윈도우이름으로 이이름은 윈도우가 나타나면 타이틀 바에 출력됩니다. 지금까지 C언어를 배우면서 작성한 프로그램의 모든 윈도우 타이틀에 "Exam02"만 출력된 이유가 바로 여기에 있는것입니다. 3번째는 윈도우 스타일 플러그입니다. WS_OVERLAPPEDWINDOW 는 일반적으로 시스템 메뉴와 확대,축소 종료 버튼이 있는 형태입니다. 이외에 여러 스타일이 있는데 이것또한 본책에서 계속설명합니다. 다음 4개의 인자는 화면에 출력할 x,y위치와 윈도우 가로크기와 세로 크기입니다. CW_USEDEFAULT를 사용하면 커널이 임의적으로 출력시킵니다. 한번 이4개의 인자에 수를 넣어보면서 출력을 해보시기 바랍니다. 그다음 인자는 부모 윈도우 핸들입니다. 그리고 그다음 인자는 메뉴이름이나 또는 차일드 윈도우일 경우 자신의 ID번호입니다. 이부분또한 계속 설명해 나갈것입니다. CreaeteWindow의 10번재 인자에는 프로그램 인스턴스를 설정합니다.

이렇게 하고 ShowWindow를 하면 윈도우가 나타나고 UpdateWindow를 하면 이윈도우가 업데이트 됩니다. 지금까지 한 것이 첫 번째로 윈도우 클래스를 등록하고 메인 윈도우를 출력한것입니다. 헌데 지금까지 설명하지 않았던 다음 내용들이 있습니다. 이것은 다음과 같습니다.

while (GetMessage (&msg, NULL, 0, 0))

{

TranslateMessage (&msg) ;

DispatchMessage (&msg) ;

}

함수이름만 보면 메시지를 받아서 이값이 0이 아니면 메시지를 전환시키고 이것을 처리한다는 의미입니다. 이루프는 바로 프로시저와 연결되는 루프입니다. 프로시저는 메시지를 처리할때마다 수행된다고 했습니다. 결국 이프로시절를 호출하는 어떤 루프가 필요하고 이루프가 바로 MainWnd에 설정된것입니다. 이루프가 돌면서

메인 윈도에 설정한 WndProc함수가 계속적으로 호출이 됩니다.

Exam02.c의 WndProc 분석

WndProc는 다음과 같은 형입니다.

LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)

LRESULT는 결과값을 저장하는 32비트 변수입니다. CALLBACK는 콜백함수라는 의미입니다. WinMain의 while루프에 의해서 뒤에 감추어진 형태로 구동이 되는 것이 WndProc입니다. 이렇게 뒤에서 어떤 메시지에 의해서 구동되는 함수를 콜백함수라고 하고 콜백함수를 선언하고자 할경우에는 함수 앞에 CALLBACK를 설정합니다. WndProc 의 첫 번째 인자는 윈도우 핸들입니다. 두 번째 인자가 바로 메시지입니다. 세 번째와 네 번째는 메시지와 함께 필요한 정보를 넣는 인자들입니다.

예를들어서 키가 눌린 메시지가 WndProc에 설정될때는 iMsg에는 WM_KEYDOWN이란 값이 들어옵니다. 여기에서 우리는 어떤키가 눌려졌는지 궁금하며 키상태가 어떤지 궁금합니다. 이런 정보들이 wParam과 lParam에 설정됩니다. 즉 wParam과 lParam은 메시지와 함께 설정되는 여러 정보를 보관하는 변수라고 보시면 됩니다.

다음은 WndProc에서 메시지를 처리하는 스위치문과 나머지 부분입니다.

LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)

{

HDC         hdc ;

PAINTSTRUCT ps ;

switch (iMsg)

{

case WM_CREATE ://윈도우가 처음 만들어질때

return 0 ;

case WM_PAINT ://화면이 출력될때

hdc = BeginPaint (hwnd, &ps) ;

main(hdc);

EndPaint (hwnd, &ps) ;

return 0 ;

case WM_DESTROY ://윈도우가 사라질때

PostQuitMessage (0) ;

return 0 ;

}

//나머지는 알아서 처리하도록....

return DefWindowProc (hwnd, iMsg, wParam, lParam) ;

}

위의 함수에서보면 iMsg에 의해서 윈도우가만들어 질때와 사라질 때 그리고 화면 출력될 때 3개만 설정했습니다. 그리고 만들어질때는 아무런작업도 하지 않았으며 종료할때는 PostQuitMessage(0);을 실행하였습니다. PostQuitMessage(0);은  메지시 큐에 종료 메시지를 설정하는 것입니다. 이렇게 설정하면 WinMain에서 while루프의 GetMessage (&msg, NULL, 0, 0) 함수는 0을 리턴하고 따라서 while루프가 종료 되면 프로그램도 종료되는 것입니다.

WM_PAINT는 바로 화면 출력 메시지입니다. 이메시지에서 main함수를 호출하고 main함수에서 지금까지 프로그램을 공부했던 것입니다.

지금까지 C언어를 공부할 때 우리가 필요했던 것은 결과값의 화면출력이 었습니다. 화면출력또한 메시지에 의거해서 처리해야 하고 이메시지가 WM_PAINT이기 때문에 이안에 main함수를 등록한것입니다.

윈도우의 프로그래밍 기본형식

SDK로 윈도우 프로그래밍을 할때 기본 형식은 다음과 같습니다.

본편에서 윈도우 프로그래밍 이라고 한다면 Win32 SDK를 이용한 윈도우 프로그래밍이라는 것으로 생각해 주시기 바랍니다.

□핸들을 받아서 그핸들을 사용하거나 핸들을 만들어서 설정하여 사용한다□

이것은 필자가 생각해도 명언인 것 같습니다. C언어로 프로그래밍할 때 기본은 이것입니다. 예를들어 라인을 그릴려고 한다면 □그릴 화면 핸들을 받고 라인을 그릴 펜이라는 핸들을 만들고 펜이라는 핸들을 이용하여 그릴 화면 핸들에 라인을 그린다□라고 할수 있습니다.

윈도우 프로그램밍에서는 모든 것을 객체화 했습니다. 객체화 했기 때문에 이객체를 컨트롤 하기 위해서는 핸들이라는 것이 필요합니다. 예를 든다면 자동차라는 객체가 있다면 이자동차를 핸들링하기 위해서는 자동차 핸들이 필요하며 자전거를 움직이고자 한다면 자전거 핸들이 필요한것과 마찬가지입니다.

SDK에서는 필요한 모든 것을 객체화 하였고 이객체를 이용하고자 할 경우 H가 앞에 붙은 핸들형을 사용합니다.

프로시저 함수 첫 번째에  HWND hwnd 라는 것을 보셨을것입니다. 이것은 윈도우 핸들입니다. 윈도우를 컨트롤 하기 위해서 만든 핸들 변수가 HWND 인것입니다. 또 WinMain에서 첫 번째 인자는 HINSTANCE hInstance입니다. hInstance는 바로 인스턴스 핸들입니다. 핸들을 얻을 경우 사용되는 함수가 보통 Get계열 함수이며 핸들을 만들때는 Create계열이고 핸들을 설정할때는 Set 계열입니다.

결국 프로그램밍을 할 때는 무엇인지 몰라도 H자가 붙은 핸들을 이용하여 얻고 이것을 이용하는것입니다. 그냥 추측으로 라인을 그린다고 한다면 다음과 같은 형식으로 할수 있습니다.

H자붙은 펜핸들 hPen;

H자붙는 화면핸들 hScreen;

hScreen=Get계열화면얻는함수;//Get계열 화면 얻는함수를 사용해서 핸들을 얻고

hPen=Create게열 펜만드는함수//펜을 만들고

: hScreen에 hPen을 이용하여 라인을 그린다.

실제 본편에서 설명하는 대부분이 어떻게 핸들을 얻으며 만들고 이핸들을 어떻게 사용하는가입니다.

두 번째로 윈도우프로그래밍의 기본형식은

□구조체로 시작해서 구조체로 끝난다!□

이것 또한 명언인 것 같습니다. 윈도우 프로그램 소스를 C언어만 배운사람이 보면 무슨 내용인지 하나도 모릅니다. 표준 형은 하나도 없고 전혀 이상한 글자만 써져 있기 때문입니다. 그것은 모두 구조체를  사용했기 때문입니다. H자가 붙지 않은 이상한것들은 전부 구조체입니다. 그럼 이것이 어떤 구조체인가를 알고자 한다면 간단하게 그구조체에 커서를 넣고 F1키를 누르시면 됩니다. WndProc에 보면 다음과 같은 문장이 있을것입니다.

PAINTSTRUCT ps ;

H자가 붙지 않았으므로 구조체일것입니다. 어떤것인지 모르니가 커서를 PAINTSTRUCT에 넣고 F1키를 눌러보세요. 그럼 다음과 같이 나올것입니다.

PAINTSTRUCT Structure

The PAINTSTRUCT structure has the following form:

typedef struct tagPAINTSTRUCT {

HDC  hdc;

BOOL fErase;

RECT rcPaint;

BOOL fRestore;

BOOL fIncUpdate;

BYTE rgbReserved[16];

} PAINTSTRUCT;

The PAINTSTRUCT structure contains information that can be used to paint the client area of a window.

Members

hdc dentifies the display context to be used for painting.

fErase pecifies whether the background needs to be redrawn. It is not 0 if the application should redraw the background. The application is responsible for drawing the background if a Windows window-class is created without a background brush (see the description of the hbrBackground member of the WNDCLASS structure in the Win32 SDK documentation).

rcPaint pecifies the upper-left and lower-right corners of the rectangle in which the painting is requested.

fRestore Reserved member. It is used internally by Windows.

fIncUpdate Reserved member. It is used internally by Windows.

rgbReserved[16] reserved member. A reserved block of memory used internally by Windows.

이도움말을 읽고 구조체가 어떤것인지 이해를 하셔야 합니다. (물론 많은 내용은 c본 책에서 설명을 하겠지만요)

(잔소리)

실제 본책에는 모든 구조체에 대한 설명을 다할 수가 없습니다. 만약 윈도우에서 사용하는 구조체와 핸들에 대한 모든내용을 설명하자면 본책의 3배정도의 두께가 필요하기 때문입니다. 다만 여러분에게 알려줄수 있는 것은 도움말을 이용하라는 것입니다. 프로그래머가 되기 위해서는 도움말 정도는 읽을수 있는 영어실력이 필요합니다. 그렇지 않고서는 발전할 수가 없습니다. 모든 구조체와 핸들에 대한 설명이 완벽한 것은 유일하게 도움말 뿐이기 때문입니다. 도움말을 참조하지 않는다면 결국 책에 나온 예제만 이용할수 있는 수준밖에는 안됩니다.

(잔소리끝)

본편의 나머지에 대해서

본편은 본장을 제외하고 11장입니다. 이 11장에는 필수적으로 필요한 Why의 개념을 설명하였습니다. 그리고 나머지는 How의 방법에 준했습니다.

본편을 통해서 어쩌면 윈도우즈 프로그램의 Why는 다 설명했다고 봅니다. 나머지는 라이브러리함수를 어떻게 사용하는가에 대한 방법이 전부입니다. 다음장부터 공부를 하실때는 먼저 내용을 읽어보시고 단순히 예제를 그대로 작성해서 결과를 얻는 것으로 끝내시기 바랍니다. 꼭 Why가 필요하면 만득이이야기나 또는 어처구니 없는 황당한 이야기를 해가면서 이해할수 있도록 노력했으며 How개념은 단순히 이렇게 하십시오라고 이야기 했습니다.

11장의 모든프로그램을 한번씩은 작성해보고 그것이 안된다면 돌아가는것만 보면서 11장을 공부하시기 바랍니다. 그런다음 후에 어떤 프로그램을 만들 때 □아! 이부분은 어느부분에 있더라!□ 라고 생각을 하고 그부분에서 소스를 How방법에 의해서 설정하시기 바랍니다. 윈도우즈 프로그래밍은 C언어를 가지고 SDK라이브러리를 어떻게 사용하는가가 다이기 때문입니다.

(잔소리)

윈도우즈 프로그램을 할 때 조금 복잡한 함수나 복잡한 형태를 이용해서 단 2~3줄로 프로그래밍할 때 매우 잘하는것이고 필수적인 함수를 이용하여 루프를 돌린다든지 또는 여러 라인을 이용해서 작성을 하면 무식한 방법이라고 이야기 하는 경우가 있습니다. 전 이런 이야기를 하고 싶습니다. 단 2~3줄로 프로그래밍하는 것이 최상이나 그렇다고 필수함수를 이용하여 여러 라인으로 프로그래망한다고 그것이 나쁜 것이 절대 아니라고 말입니다. 실제 2~3줄의 복잡한 함수를 이용하면 그함수 내부에서는 필수 함수로 여러라인을 이용한 무식한 내용이 들어가 있는 것이 대부분이기 때문입니다. 필수 함수가지고 여러 라인의 프로그램을 하면 자신도 모르는사이에 알고리듬이 늘어납니다. 처음에는 아주 무식한 형태가 되겠지만 자꾸자꾸 프로그래밍 하다보면 □아하 이부분은 이런 방식으로 하면 빠르게 되겠구나!□ 라는 것을 알수 있으며 때로는 자신만의 알고리듬을 만들수도 있기 때문입니다. 필수함수를 이용하여 여러 라인을 작성하다보면 윈도우즈가 아닌 다른 OS에서도 프로그래밍을 배울 때 매우 빠르게 숙달합니다. 그이유는 필수함수는 다른 OS에서도 비슷하게 설정되어 있기 때문입니다. 저희 웹서버에 질문이 들어올 때 답변을 하면 □그건 좀 무식한 방법이고 어떻게 지적이고 수준 높은 방법이 있을텐데..□ 라고 하시는 분들이 있습니다. 전 그럴 때 마다 묻고 싶습니다. □SDK 라이브러리 가지고 하라는데로 프로그래밍하는데 떤 수준 높은 방법이 있고 어떤 수준 낮은 방법 있는가?□ 라고 말입니다. 프로그램을 배울때는 무식해야 합니다. 그래야만 자신의 것을 만들수가 있는것입니다.

(잔소리끝)