Your First Windows Program

이제 코딩할 차례이다. 아주 간단한 윈도우 프로그램의 코드를 보면서, 도스 시절의 프로그램과 비교해보자. 이 방법은 개발환경을 사용하고, 프로그램을 컴파일하는데에 도움이 될 것이다.

A Character-Mode Model

프로그래머들 사이에서 유명한 책 The C Programming Language (Prentice Hall, 1978 and 1988) by Brian W. Kernighan and Dennis M. Ritchie 이 있다. 이 책의 챕터1 에는 "hello, world" 라는 간단한 프로그램이 있다.
[code]
main ()
{
     printf ("hello, world\n") ;
}
[/code]

그렇다. 예전엔 C 프로그래머는 printf() 와 같은 함수들은 선언없이 사용했다. 그러나 90 년대에 들어서면서, flag 에러가 발생하기 시작하였다. 자, 다음은 개정판에서의 소스 코드이다.
[code]
#include
main ()
{
     printf ("hello, world\n") ;
}
[/code]

여전히 프로그램은 짧아보인다. 이 코드는 정확히 컴파일되고 실행되지만, 요즘의 많은 프로그래머들은 함수의 리턴값을 명시해주는 걸 선호한다. ANSI C 에서는 함수는 항상 값을 리턴해야된다고 되어 있다.
[code]
#include

int main ()
{
     printf ("hello, world\n") ;

     return 0 ;
}
[/code]

우리는 main 에 몇몇의 매개변수를 포함해서 더 길게 늘릴 수도 있지만, 그냥 내버려 두자.


The Windows Equivalent

윈도우에서도 "hello, world"와 같은 간단한 예를 보여주는 코드가 있다. 역시 include 문이 있고, 엔트리포인트(main 과 같은)가 있으며, 함수호출과 리턴문도 있다. 다음을 보자.
[code]
/*--------------------------------------------------------------
   HelloMsg.c -- Displays "Hello, Windows 98!" in a message box
                 (c) Charles Petzold, 1998
  --------------------------------------------------------------*/

#include

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PSTR szCmdLine, int iCmdShow)
{
     MessageBox (NULL, TEXT ("Hello, Windows 98!"), TEXT ("HelloMsg"), 0) ;

     return 0 ;
}
[/code]

코드를 살펴보기 전에, Visual C++ Developer Studio를 이용해서 프로그램을 실행해보자.

File 메뉴에서 New를 선택하라. 그리고 대화상자가 나오면, Projects 탭을 선택하라. 그리고 Win32 Application을 고른다. Location 필드에서 서브디렉토리를 고르고, Project Name 필드에서 project 의 이름을 써준다. 이름은 HelloMsg 라고 하자. 자 그럼, Location 필드에 적어준 디렉토리에 HelloMsg 라는 이름의 서브디렉토리가 생성된다. Create New Workspace 체크박스는 체크되어 있어야 한다. Platform 선택은 Win32 이다. OK를 누르자.

Win32 Application 이라고 쓰인 대화상자가 나온다. Empty Project를 생성한다고 고른 후 Finish 버튼을 누른다.

File 메뉴에서 New를 다시 선택한다. 대화상자가 나오면 Files 탭을 선택한 후 C++ Source File을 선택하자. Add To Project box 는 체크되어 있어야 한다. File Name 필드에 HelloMsg.c 라고 친 후 OK를 누르자.

이제부터는 HELLOMSG.C 파일에 코딩할 준비가 됐다.

구조적으로, HELLOMSG.C 는 위에서 본, "hello, world" 프로그램과 동일하다. STDIO.H 의 헤더는 WINDOWS.H 로 달라졌고, main 이라는 entry point 는 WinMain 으로 바뀌었다. C 런타임 라이브러리의 printf 함수는 윈도우 API 인 MessageBox 로 바뀌었다. 그러나 이들을 제외하고도 많이 다르게 보인다.

위에서부터 보자.


The Header Files

HELLOMSG.C 는 전처리기 로 시작되는데, 이는 어떠한 윈도우 C 프로그램을 봐도 있을 것이다.
[code]
#include
[/code]

WINDOWS.H 는 다른 헤더파일들을 포함하고있는 윈도우의 중심적인 헤더파일이다. 윈도우의 가장 중요한 대부분의 기초헤더가 여기에 포함되어 있다.

[code]
WINDEF.H   Basic type definitions.
WINNT.H   Type definitions for Unicode support.
WINBASE.H   Kernel functions.
WINUSER.H  User interface functions.
WINGDI.H  Graphics device interface functions.
[/code]

이러한 헤더들은 윈도우 데이터타입, 함수, 데이터구조체, 상수들을 정의한다. 이것들은 윈도우의 아주 중요한 부분들이다. 비주얼 스튜디오의 Edit 메뉴에서 파일찾기 기능을 이용하면 이들 파일들을 찾을 수 있다. 이 헤더파일들을 스튜디오상에서 열어볼 수 있으며 직접 고쳐서 시험해볼 수도 있을 것이다.


Program Entry Point

C 프로그램의 엔트리 포인트 main처럼, 윈도우 프로그램에서는 WinMain 이다. 이것은 다음과 같이 나타낸다.

[code]
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PSTR szCmdLine, int iCmdShow)
[/code]

이 엔트리 포인트(진입점 or 시작점)는 MSDN에서 /Platform SDK/User Interface Services/Windowing/Windows/Window Reference/Window Functions 에 설명되어 있다. 이것은 WINBASE.H 에 다음과 같이 선언되어 있다.

[code]
int
WINAPI
WinMain(
    HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPSTR lpCmdLine,
    int nShowCmd
    );
[/code]

내가 HELLOMSG.C에서 소스의 일부분을 다르게 쓴 것을 알 수 있다. 세 번째 파라메터가 그것인데, WINBASE.H 에는 LPSTR 이라고 선언되어 있고, 내 소스에서는 PSTR 이라고 되어 있다. 이 두가지 데이터 타입은 WINNT.H 에 문자열을 가리키는 포인터로서 동일하게 선언되어 있다. LP 라 함은 "long pointer"을 나타내는데, 이것은 16비트 윈도우의 유품이다.

나는 또 WinMain 선언에서 두가지 파라메터 이름을 바꿨다. 많은 윈도우 프로그램은 "헝가리안 표기법"이라고 불리는 변수명명법을 사용한다. 이 방법은 변수이름 앞에 데이터타입이 뭔지 나타내는 간단한 접두어를 붙인다. 이 방법에 관해서는 챕터3에서 다시 보기로 하자. 지금은 내가 변수명 앞에 붙인 sz 가 "string terminated with a zero"를 나타낸다는 것만 알고 넘어가자.

WinMain 함수의 리턴값이 int 로 명시되어 있다. WINAPI 라고 쓴 것은 WINDEF.H 에 다음과 같이 정의되어 있다.

[code]
#define WINAPI __stdcall
[/code]

이 정의문은 기계어코드가 어떤 식으로 함수의 매개변수들을 스택에 위치시킬 것인가에 대한 하나의 관습적인 문장이다.

WinMain 의 첫 번째 파라메터는 "instance handle" 이라고 불리는 것이다. 윈도우 프로그래밍에서, 핸들은 애플리케이션이 어떤 것을 알아볼 수 있게 하기위한 간단한 숫자 이다. 이 경우에서, 핸들은 프로그램에서 고유의 숫자를 가진다. 핸들은 다른 윈도우 함수호출을 위해 필요하기도 하다. 윈도우 초기버전에서는 같은 프로그램을 두 번이상 동시에 실행시켰을 때 프로그램의 인스턴드가 중복되어 생성된다. 같은 프로그램의 모든 인스턴스는 코드를 공유하고, read-only memory 상태가 된다.(메뉴와 다이얼로그 박스와 같은) 프로그램은 hPrevInstance 파라메터를 체크함으로써, 그 자신과 다른 인스턴스를 구별할 수가 있다. 이것은 몇가지의 잡다한 작업을 줄여주고, 같은 프로그램에서 서로 다른 인스턴스끼리 서로 다른 고유의 데이터 영역을 가질 수 있게 해준다.

32비트 윈도우에서는 이러한 개념이 쓰이지 않는다. WinMain 의 두 번째 파라메터는 언제나 NULL(0이라고 정의된)이다.

WinMain 의 세 번째 파라메터는 프로그램이 실행될 때 쓰이는 명령어 입력 파라메터이다. 어떤 윈도우 애플리케이션은 이것을 메모리로 파일을 로드할 때 쓰기도 한다.(프로그램이 시작될 때) 네 번째 파라메터는 프로그램이 초기에 어떻게 출력되는지 결정하는 파라메터이다. 보통크기나 최대화, 최소화등의 상태가 그것이다. 챕터3에서 이 파라메터가 어떻게 사용되는지 알아볼 예정이다.


The MessageBox Function

MessageBox 함수는 화면상에 간단한 메시지를 출력하도록 하는 함수이다. MessageBox 가 출력하는 윈도우는 대화상자처럼 생겼으나, 많은 기능이 있지는 않다.

MessageBox 함수의 첫 번째 파라메터는 윈도우의 핸들이다. 우리는 이것이 무엇을 의미하는지 챕터3에서 공부할 것이다. 두 번째 파라메터는 메세지 박스의 중앙에 나올 문자열이고, 세 번째 파라메터는 메시지 박스의 제목표시줄에 나올 캡션이다. HELLOMSG.C 에서는 각각의 문자열들이 TEXT 라는 매크로로 둘러싸여 있다. 모든 문자열을 TEXT 매크로로 묶어줄 필요는 없지만, 문자열들을 유니코드로 바꾸어주고 싶다면 좋은 시도이다. 이것에 관해서는 챕터2에서 좀 더 자세하게 알아보자.

세 번째 파라메터는 WINUSER.H 에 정의되어 있는 여러 가지 상수들의 콤비네이션이다. 다음과 같은 것들을 조합함으로써 대화상자에 적용해볼 수 있을 것이다.

[code]
#define MB_OK                       0x00000000L
#define MB_OKCANCEL                 0x00000001L
#define MB_ABORTRETRYIGNORE         0x00000002L
#define MB_YESNOCANCEL              0x00000003L
#define MB_YESNO                    0x00000004L
#define MB_RETRYCANCEL              0x00000005L
[/code]

네 번째 파라메터를 0 으로 입력해버리면, 단지 OK 버튼만 나타난다. 저것들을 조합하기위해서 OR (|) 문을 사용한다.

[code]
#define MB_DEFBUTTON1               0x00000000L
#define MB_DEFBUTTON2               0x00000100L
#define MB_DEFBUTTON3               0x00000200L
#define MB_DEFBUTTON4               0x00000300L
[/code]

메세지박스에 아이콘 형태도 변화시킬 수가 있다. 다음을 봐라.

[code]
#define MB_ICONHAND                 0x00000010L
#define MB_ICONQUESTION             0x00000020L
#define MB_ICONEXCLAMATION          0x00000030L
#define MB_ICONASTERISK             0x00000040L
[/code]

이러한 아이콘들은 여러 가지 변형이 있다.

[code]
#define MB_ICONWARNING              MB_ICONEXCLAMATION
#define MB_ICONERROR                MB_ICONHAND
#define MB_ICONINFORMATION          MB_ICONASTERISK
#define MB_ICONSTOP                 MB_ICONHAND
[/code]

몇 개의 더 많은 MB_ 상수가 있지만, 도큐먼트의 /Platform SDK/User Interface Services/Windowing/Dialog Boxes/Dialog Box Reference/Dialog Box Functions을 직접 찾아보길 바란다.

이 프로그램상에서, MessageBox 함수는 1을 리턴한다. 그러나 IDOK 라는 값을 리턴하는 것이 더 적당할 듯 싶다. 이것은 WINUSER.H 에 1로 정의되어 있다. 메시지박스는 또한 IDYES, IDNO, IDABORT, IDRETRY, IDIGNORE 라는 값들을 리턴할 수도 있을 것이다.

자 이제 이 코드가 C 의 "hello, world" 코드와 같아보일까? 글쎄, 아마도 당신은 "hello, world" 프로그램에서의 printf 함수가 가진 다양한 형식의 출력방식에 비해 MessageBox 함수가 별로 안 좋게 보일 수 있을 것이다. 그러나 다음 챕터에서 MessageBox 함수를 printf 함수처럼 다양한 포맷출력별로 설명할 예정이다.


Compile, Link, and Run

HELLOMSG.C를 컴파일할 준비가 되었다면, Build 메뉴에서 Build Hellomsg.exe를 선택하던가, F7을 누르거나, 툴바에 있는 Build 아이콘을 선택하자.

또는 빌드메뉴에서, Execute Hellomsg.exe를 선택하거나, Ctrl+F5를 누르거나 툴바에서 Execute 아이콘을 누르자. 원하는 형태대로 메시지 박스가 출력되는 것을 볼 수 있다.

정상적으로 되었다면, 컴파일 과정에서, 컴파일러는 .OBJ(onject) 파일을 C 코드로부터 생성해낸다. 링크 과정에서 링커는 .OBJ 파일을 .LIB(library) 파일과 결합시켜서 .EXE(executable) 파일을 생성한다. 이러한 것들은 Project 탭과 Link 탭을 클릭한 후 Setting을 선택하면 라이브러리 파일을 볼 수 있다. 더 자세하게 보면, 전에 KERNEL32.LIB, USER32.LIB, GDI32.LIB 파일들을 본 적이 있다. 이러한 것들은 윈도우의 서브시스템을 위한 "import librartes" 이다. 이들은 dynamic-link library 이름들을 포함하고 있고, 어떤 주소의 정보들이 .EXE 파일에 결합되는지에 대한 내용을 가지고 있다. 윈도우는 이러한 정보들을 KERNEL32.DLL, USER32.DLL, GDI32.DLL dynamic-link library 로부터 사용하고 있다.

비주얼 C++ 개발 스튜디오에서, 다른 옵션을 줌으로써, 다르게 컴파일하고 링크시킬 수가 있다. 기본적인 옵션으로 이러한 것들 중, Debug 모드와 Release 모드라는 것이 있다. 이러한 이름들의 하위 디렉토리안에 실행파일들이 담겨있다. 디버그 configuration 모드에서는, 디버깅하는 데 필요한 정보와 소스 코드를 tracing 하는 정보들이 .EXE 에 추가되게 된다.

커맨드 라인 상태에서 작업하는 것을 더 선호한다면, CD-ROM 에 포함된 .MAK(make) 파일들을 보자. 그럴려면 비주얼 스튜디오의 BIN 디렉토리에 있는 VCVARS32.BAT 파일이 필요할 것이다. 커맨드 라인에서 make file을 실행하고 싶다면 HELLOMSG 디렉토리로 바꾸로 실행해라.

NMAKE /f HelloMsg.mak CFG="HelloMsg _ Win32 Debug"

or

NMAKE /f HelloMsg.mak CFG="HelloMsg _ Win32 Release"

이렇게 타이핑함으로써, 커맨드라인에서 .EXE 파일을 실행할 수 있다.

DEBUG\HELLOMSG

or

RELEASE\HELLOMSG