황동준님의 윈도우 32비트 프로그래밍 01~05

  윈도우 32비트 프로그래밍 01

안녕하세요…돌팔이 황동준입니다.

그냥 제가 알고 있는 윈도우즈 프로그램에 대해서 앞으로 자유롭게 강좌할 생각입니다. 최대한
쉽게 설명할 것이기 때문에 이해하기는 상당히 쉬울 겁니다. 내용이 많아서 아마 장기간 동안 강좌할 것 같은데요 여러분들이 많이 도와주시기
바랍니다.

저도 잘 모르는 부분이 있기 때문에 설명이 틀릴 수도 있으니 이런 부분 과감히 지적해
주세요.

컴파일러는 비주얼씨 4.1을 기준으로 하고 방식은 API입니다. MFC를 익히려는 분들이
많은데 API를 익히고 익히면 더 쉽거든요.

1. 윈도우즈 프로그램의 개요

윈도우즈 프로그래밍을 배울려고 하는 여러분들은 적어도 한가지 책은 가지고 있을 겁니다. 물론
프로그래밍에 관한 책이겠죠. 공통적으로 제일 앞 부분에 보면 윈도우즈 프로그래밍에 관한 개요가 전부 있을 겁니다. 그런 책에 너무 잘 설명되어
있기 때문에 자세히는 언급하지 않겠습니다. 사실 자세히는 잘 모름[혼자말]

윈도우즈는 도스와는 달리 특수한 방법으로 모든게 처리가 됩니다. 왜 도스와는 다른 방법으로
처리가 될까요? 바로 멀티 태스킹이 가능하기 때문입니다. 도스에 있는 인터럽트라는 개념이 윈도우즈에서는 사실상 없습니다. 전부 메시지라고 하는
특수한 것에 의해서 처리가 되죠. 따라서 프로그램을 작성할 때도 이 메시지를 가지고 작업하는 것입니다.

1.1 첫번째 알아야 할 사실

프로그램을 예를 들어 봅시다. 이 프로그램은 작업영역에 마우스가 클릭되면 마우스가
클릭되었다고 출력합니다. 어떤 식으로 프로그램이 작성되어 있을까요?

간단합니다. 마우스가 클릭되면 윈도우즈는 마우스가 클릭되었다는 메시지를 메시지 큐라는 곳에
보냅니다. 프로그램은 어떤 기능을 하면 될까요? 이 메시지 큐에서 메시지를 가져오는 루틴이 있으면 되겠죠? 그래서 그 메시지가 마우스가 눌렸다는
메시지이면 눌렸다고 표시해 주면 되는 것입니다. 아주 간단하죠.

한가지 배웠습니다. 바로 프로그램에는 항상 메시지 큐에서 메시지를 가져오는 루틴이 있어야
한다는 것입니다. 물론 루프문으로 매번 메시지 큐를 체크해야겠죠.

1.2 두번째 알아야 할 사실

자 그러면 두번째를 배워봅시다. 이런 경우는 어떻겠습니까? 위와 같은 프로그램 이 데스크탑
위에 구동되어 있고 이것과 아주 똑같은 기능을 가진 다른 프로그램이 하나 더 구동되어 있는 상태에서 한쪽 프로그램에 마우스를 클릭하면 클릭된
프로그램에서 마우스가 클릭되었다고 출력하겠죠. 클릭 안된 프로그램에서는 출력이 되지 않을 겁니다. 당연하죠. 이 두 프로그램을 윈도우즈는 어떻게
구분할까요? 아주 간단합니다. 프로그램(윈도우)이 생성될 때 윈도우즈는 그 프로그램(윈도우)에 일종의 번호를 부여합니다. 그래서 각
프로그램(윈도우)을 구분하죠. 이 번호를 윈도우즈 용어로 핸들이라고 합니다. 이제 한가지 더 알아야 할 것이 바로 이 핸들인데 윈도우즈에서
사용되는 모든 API함수들은 이 핸들을 아구먼트로 요구한다는 것입니다. 이제 하나씩 배워 나가면 그것을 확인하게 될것입니다.

1.3 윈도우즈 프로그램의 구조

도스에서 프로그램을 짜면 항상 메인함수가 있어야 합니다.

void main(void)
{
}

위와 같은 형태로 프로그램을 작성했을 겁니다. 그런데 윈도우즈에서는 위 메인 함수를 사용하지
않고 WinMain()이라는 윈도우즈 메인함수를 사용해서프로그램을 작성합니다. 도스에서 메인 함수가 필수듯이 윈도우즈에서도 윈 메인함수는
필수입니다. 여기서 한 가지 더 알아야 할 사실은 도스에서는 메인 함수 하나만 있으면 됐지만 윈도우즈에서는 함수가 하나 더 필수적으로 있어야
한다는 것입니다. 보통 이 함수를 그냥 윈도우 함수라고 합니다.

그러면 어떤식으로 되는지 전체적으로 흐름을 알아봅시다.

먼저 윈 메인 함수에서는 윈도우를 생성하고 메시지 큐에서 메시지를 가져오는 역할을 합니다.
그래서 가져온 메시지를 윈도우 함수로 보내는데 실제로 메시지를 보고 판단하여 해당 작업을 수행하는 부분이 바로  윈도우 함수가 되는거죠. 왜
윈도우 함수가 필요한지 알겠죠?

또 윈 메인함수도 내부적으로 크게 세 부분으로 나눌 수 있습니다.

WinMain()
{
    첫번째 파트
    두번째
파트
    세번째 파트
}

첫번째 파트에서 프로그래머가 해주어야 할 일은 생성할 윈도우의 속성을 지정해주는 것입니다.
뭐 바탕색은 어떤 색깔로 하고 메시지를 받아서 처리할 윈도우 함수이름도 지정해 주어야 하고 커서, 아이콘등도 지정해 주는 작업을 여기서
하죠.

두번째 파트에서는 실제로 윈도우를 생성해서 화면에 보이게하는 작업이고 마지막 세번째
파트에서는 메시지 큐에서 메시지를 꺼내서 윈도우 함수에 보내주는 작업을 해주면 됩니다.

자 이제 이해가 갑니까? 이해가 가면 벌써 절반은 끝난거나 다름없습니다. 쉽죠.

다음 시간에는 실제로 윈도우를 생성하는 프로그램을 만들어 보겠습니다.

그럼 안뇽…~~~~~~~~~~

 

  윈도우 32비트 프로그래밍 02

안녕하세요… 돌팔이 황동준입니다.

이번에는 실제적으로 윈도우를 생성하는 예제에 대해서 알아 보도록하겠습니다.

2. 윈도우의 생성과 다루기

2.1 데스크탑위에 윈도우 생성하기

윈도우즈 프로그래밍을 작성하기 위해서는 항상  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로 컴파일하는 방법과 프로그램 분석을 해
보도록 하겠습니다.

그럼 이만….

 

  윈도우 32비트 프로그래밍 03

안녕하세요… 돌팔이 황동준입니다.

저번 시간에 이어서 계속 강좌를 하겠습니다. 먼저 저번시간에 코딩한 소스를 컴파일 해보도록
하죠. 먼저 비주얼C 4.1을 구동하세요.

비주얼C 4.1의 통합환경은 Microsoft Developer Studio라고 써있는
아이콘을 더블클릭하면 됩니다.

자 구동이 됐습니까? 그러면 먼저 File 메뉴의 New를 선택하시기 바랍니다. 그러면 어떤
것을 작성할 것인지 물어오는데 이때 두번째의 Project Workspace를 선택하면 또 다른 대화상자가 생성될 것입니다.

우리는 API함수를 이용해서 프로그램을 만들기 때문에 왼쪽의 타입을 Application에
맞추어 놓고 실행 파일 생성 디렉토리를 Brower버튼을 눌러 지정하면 됩니다. 지정이 됐으면 Name부분에 생성될 실행 파일이름을 지정하면
됩니다. 이때 물론 확장자는 생략하구요.

자 여기까지 됐으면 다 된거나 다름없습니다. 여기까지 작업이 프로젝트 파일 구성 준비
단계입니다. 이번에는 어떤 작업이 필요할까요?

당연히 컴파일할 파일을 이 프로젝트에 포함시켜 주어야겠죠. 우리가 만든 프로그램 소스는
하나만 있으면 되니 위에서 코딩한 파일을 Insert메뉴의 Files into Project를 선텍해서 포함시켜 주면 됩니다. 그렇게 어렵지
않죠?

다 됐으면 이제 Build메뉴의 Build를 선택해서 컴파일하면 됩니다. 만약에 에러가
발생되면 F4키를 눌러 어디서 에러가 발생됐는지 알아낼 수도 있습니다.

에러가 없으면 Build 메뉴의 Excute를 선택하면 그 프로그램이 실행됩니다. 여기까지
잘 됩니까? 그러면 생성된 윈도우를 조작해 보세요. 아주 간단한 프로그램이지만 기본적인 것은 다 되죠. 그러면 구체적으로 그 소스에 대해 알아
보겠습니다. 아차~~ 알아보기 전에 나중에 이 프로젝트 파일을 다시 불러와서 사용하고 싶으면 File 메뉴의 Open Workspace를
선택해서 열면 됩니다.

자 그러면 구체적으로 그 소스를 분석해 볼까요?

#include <windows.h>

먼저 항상 windows.h라는 헤더파일을 포함시켜 주어야 한다고 위에서 언급했을 겁니다.
이것이 없으면 윈도우즈 프로그램을 만들지 못하죠.

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM,
LPARAM);

윈도우 함수가 메인 함수의 뒤에 위치하기 때문에 함수원형을 선언 해준겁니다.

int WINAPI WinMain
(HINSTANCE hInstance,
HINSTANCE hPrevInstance, LPSTR lpszArg, int nCmdShow)
{

        HWND hWnd;

윈도우의 핸들 자료형이 HWND입니다. 이것은 CreateWindow() 함수를 사용할때
리턴되는 자료형이 HWND 이기 때문에 이 자료형이 필요하죠.

        MSG 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);

아이콘은 기본적으로 시스템에서 제공하는 것을 사용하겠다는 뜻입니다. 생소한 함수가 하나
있죠? LoadIcon()이라는 함수인데 보통 기본적으로 첫번째 파라미터를 NULL로 지정하고 두번째 파라미터는 지정된 예약어를 가지고
윈도우에서 제공하는 아이콘을 사용할 수 있습니다. 다음은 이 함수 원형과 두번째 파라미터에 들어갈 수 있는 예약어에 대한 설명입니다.

HICON LoadIcon(
    HINSTANCE
 hinst,
    LPCTSTR  lpszIcon
   );   

IDI_APPLICATION 기본적인 프로그램 아이콘
IDI_ASTERISK
   아스테리크 아이콘
IDI_EXCLAMATION 느낌표 모양의 아키콘
IDI_HAND        손 모양의
아이콘
IDI_QUESTION    물름표 아이콘

만약에 유저가 그린 그림을 가지고 아이콘으로 사용하려면 첫번째 파라미터에는 프로그램의
인스턴스 핸들을 그리고 두번째 파라미터에는 그 그림의 핸들을 지정해주어서 사용하면 됩니다. 실제적으로 유저가 그린 그림을 가지고 사용하는 방법은
뒤에 가서 다시 설명드리겠습니다.

        WndClass.hCursor = LoadCursor(NULL,
IDC_ARROW);

역시 커서 모양을 정의하는 부분인데 윈도우즈에 말하는 커서는 도스에서 말하는 커서와는 의미가
다릅니다. 도스에서 커서의 의미는 유저로부터 입력을 받기 위해서 깜빡거리는 것을 의미하지만 윈도우즈에서의 커서는 마우스 포인트를 의미하는
것입니다. 그러면 윈도우즈에서 유저로부터 입력을 받기 위해서 깜빡거리는 것을 뭐라고 할까요? 좀 생소할지는 몰라도 이것을 캐럿이라고 합니다. 이
부분은 나중에 다시 설명 드리겠습니다. 하여간 마우스 포인트를 의미하는 커서의 모양을 위 LoadCursor()이라는 함수를 가지고 할수
있습니다. 역시 첫번째 파라미터를 NULL로 하고 두번째 파라미터에 예약어를 지정하여 기본적으로 제공하는 커서를 사용할 수 있습니다. 아래는
함수 원형과 예약어에 대한 설명입니다.

HCURSOR LoadCursor(
    HINSTANCE
 hinst,
    LPCTSTR  lpszCursor
   );   

IDC_ARROW       화살표 모양의 커서
IDC_CROSS       십자가
모양의 커서
IDC_IBEAM       I자 모양의 커서
IDC_WAIT        모래 시계 모양의 커서

        WndClass.hbrBackground =
GetStockObject(WHITE_BRUSH);

윈도우의 백경색을 흰색으로 한다는 의미입니다.

HGDIOBJ GetStockObject(
    int
 fnObject
   );

위에 GetStockObject()함수의 원형이 나와 있는데 이 함수는 앞으로도 많이
나올겁니다. 이 함수가 하는 역활은 펜, 브러쉬 또는 폰트등의 핸들을 넘기는 것인데 구체적으로는 파라미터에 따라 그 내용이 달라집니다. 들어갈
파라미터와 의미는 다음과 같습니다.

BLACK_BRUSH         검정색 브러쉬
DKGRAY_BRUSH
       진한 회색 브러쉬
GRAY_BRUSH          회색 브러쉬
HOLLOW_BRUSH        빈
브러쉬
LTGRAY_BRUSH        여린 회색 브러쉬
NULL_BRUSH          빈 브러쉬
WHITE_BRUSH
        흰색 브러쉬
BLACK_PEN           검정색 펜
NULL_PEN            빈

WHITE_PEN           흰색 펜
ANSI_FIXED_FONT     안시 고정 폰트
ANSI_VAR_FONT
      안시 가변 폰트
DEVICE_DEFAULT_FONT 장치 기본 폰트
OEM_FIXED_FONT      장치 지원
폰트
SYSTEM_FONT         시스템 폰트
SYSTEM_FIXED_FONT   시스템 고정
폰트
DEFAULT_PALETTE     기본 팔레트

        WndClass.lpszMenuName = NULL;

생성될 윈도우에 메뉴가 없기때문에 NULL로 지정하였습니다.

        WndClass.lpszClassName = szAppName;

클래스 이름은 타이틀바 제목과 같이 했죠. 다르게 하기 귀찮아서요.

        if(!RegisterClass(&WndClass)) return
NULL;

이렇게 등록한 WNDCLASS구조체를 실제로 등록하는 과정입니다. 위에서 이미 다 설명드렸을
겁니다.

        hWnd =
CreateWindow(
                szAppName,
                szAppName,
                WS_OVERLAPPEDWINDOW,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                NULL,
                NULL,
                hInstance,
                NULL
        );

각 파라미터의 의미는 이미 알고 있을 겁니다. 여기서 세번째 파라미터에 보면 윈도우 생성
모양을 지정해 주게 되어 있는데 잘 모르는 게 들어있군요. 여기에 들어갈 수 있는 예약어와 의미는 다음과 같습니다.

WS_BORDER           윈도우의 테두리를 그려 줍니다.
WS_CAPTION
         타이틀바를 가진 윈도우를 생성합니다.
WS_HSCROLL          윈도우에 수평 스크롤바를 만들어
줍니다.
WS_ICONIC           윈도우를 아이콘화합니다.
WS_MAXMIZE          윈도우를
최대화합니다.
WS_MINIMIZE         윈도우를 아이콘화합니다.
WS_OVERLAPPED       윈도우에 최대, 최소,
종료 버튼을 생성하고 WS_CAPTION|
                    WS_BORDER를 한것과 같은 효과를
봅니다.
WS_OVERLAPPEDWINDOW WS_OVERLAPPED|WS_SYSMENU|WS_THICKFRAME과 같은
효과를 봅니다.
WS_SYSMENU          시스템 메뉴를 갖는 윈도우를 생성합니다.
WS_THICKFRAME       굵은
경계선을 가진 윈도우를 생성합니다.
WS_VSCROLL          윈도우에 수직 스크롤바를 만들어 줍니다.

네번째에서 일곱번째 파라미터를 보면 CW_USEDEFAULT라고 되어 있는데 이것은
그냥 기본적으로 윈도우즈가 정해준 값으로 하겠다는 의미입니다.
실제로 이 부분에 유저가 숫자를 지정해주면 그 위치, 그 크기대로 윈도우가
생성됩니다.

        ShowWindow(hWnd,
nCmdShow);
        UpdateWindow(hWnd);

실제로 윈도우를 보여주는 과정이죠. 보여지는 형식은 기본적으로 윈도우즈에서 제공하는 형태를
띕니다. nCmdShow는 윈 메인 함수의 파라미터 값중 하나라는 것을 알고 있겠죠?

        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 case문이 맣죠. 메시지를 구분하여 처리하기 때문에 이 방식을 많이 취하는 것입니다.

    switch(mesg)
    {
        case
WM_DESTROY :

            PostQuitMessage(0);
            return
FALSE;

유저가 프로그램을 종료하려고 하면 WM_DESTROY 메시지가 발생되므로 이때 종료시켜야
프로그램이 종료됩니다.

VOID PostQuitMessage(
    int
 nExitCode
   );

파라미터로 0을 지정하면 프로그램이 종료됩니다.

    }
    return DefWindowProc(hWnd, mesg,
wParam, lParam);
}

그 외의 메시지는 디폴트로 처리하기 때문에 위 DefWindowProc() 함수를
사용합니다.

LRESULT DefWindowProc(
    HWND
 hwnd,
    UINT  uMsg,
    WPARAM  wParam,
    LPARAM  lParam
   );
  

오늘은 여기까지입니다. 쉽죠…

그럼 안녕히….

 

  윈도우 32비트 프로그래밍 04

안녕하세요……..돌팔이 황동준입니다….

저번시간에 간단하게 윈도우를 생성하는 예제를 해 보았는데 어때요? 기능에 비해서 너무 소스가
길다구요?

그렇지 않습니다. 도스에서 그 정도의 기능을 가진 윈도우를 만들려면 소스양이 더 길어집니다.
물론 라이브러리를 이용하지 않구요.

자 이번에는 다른 예제를 보도록 하죠. 이번 예제도 저번 예제와 크게 다른 점은 없습니다.
단지 윈도우를 생성할 때 모양을 다르게 생성하는 것인데 저번 시간에 잘 이해했다면 굳이 이번 예제를 따로 이해할 필요는 없습니다.

이번 예제는 생성되는 윈도우의 모양이 타이틀바만 가지고 있는 예제입니다.

hWnd =
CreateWindow(
    szAppName,
    szAppName,
    WS_VISIBLE|WS_BORDER,
    CW_USEDEFAULT,
    CW_USEDEFAULT,
    CW_USEDEFAULT,
    CW_USEDEFAULT,
    NULL,
    NULL,
    hInstance,
    NULL
);

생성되는 윈도우가 타이틀바만 가지려면 우리가 처음에 알아본
WS_OVERLAPPEDWINDOW 대신에 위와 같은 속성을 주면 됩니다.

간단하네요. 아래는 전체 소스입니다. 한번 컴파일해서 실행시켜 보십시요.

#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_VISIBLE|WS_BORDER,
                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_CHAR :
        case WM_KEYDOWN :

            SendMessage(hWnd, WM_DESTROY,
(WPARAM)0, (LPARAM)0);
            return FALSE;

        case WM_DESTROY :

            PostQuitMessage(0);
            return
FALSE;
    }
    return DefWindowProc(hWnd, mesg, wParam,
lParam);
}

자 이번에는 타이틀바를 다뤄보도록 하겠습니다. 타이틀바가 뭔지는 알죠?

CreateWindow()라는 함수를 이용해서 윈도우를 생성할때 파라미터중 타이틀바 제목을
지정하게 되어 있는데 현재 생성된 윈도우의 타이틀바가 무엇인지 알 수 있게 해주는 함수가 있습니다.

int GetWindowText(
    HWND  hwnd,
    LPTSTR
 lpsz,
    int  cch
   );   

위 함수를 이용해서 현재 타이틀바의 제목을 알아낼 수 있습니다. 첫번째 파라미터는 타이틀바를
알아낼 윈도우의 핸들을 의미하고 두번째 파라미터는 알아낸 타이틀바의 제목을 저장할 버퍼를 의미합니다. 마지막 세번째 파라미터는 버퍼의 최대
크기를 의미하죠.

타이틀바의 제목을 알아내는 함수가 있으니 지정하는 함수도 당연히 있겠죠?

BOOL SetWindowText(
    HWND
 hwnd,
    LPCTSTR  lpsz
   );

위 함수를 이용해서 타이틀바의 제목을 새로 지정할 수 있습니다. 두번째 파라미터에 새로
지정할 윈도우 타이틀바 제목을 써주면 됩니다. 자 그러면 이 두 함수를 이용한 간단한 예제를 보도록 하겠습니다. 이번 예제는 윈도우가 생성될 때
타이틀바의 제목을 바꿔주고 유저가 Enter키를 치면 현재 타이틀바를 출력해주는 예제입니다. 그럼 예제를 볼까요?

#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)
{
    char szTitle[] = “Title is changed
!!!”;
    char szGetTitle[80];

    switch(mesg)
    {
        case WM_CREATE
:

            SetWindowText(hWnd,
szTitle);
            return FALSE;

        case WM_KEYDOWN :

            if(LOWORD(wParam) ==
VK_RETURN)
            {
                GetWindowText(hWnd, szGetTitle,
80);
                MessageBox(hWnd, szGetTitle, “”,
MB_OK);
            }
            return FALSE;

        case WM_DESTROY :

            PostQuitMessage(0);
            return
FALSE;
    }
    return DefWindowProc(hWnd, mesg, wParam,
lParam);
}

자 그러면 구체적으로 알아볼까요? 못보던 것들도 보이는군요. 그런데 잘 보면 알겠지만 윈
메인 함수부분은 예전 예제랑 다른 점이 하나도 없습니다. 그렇죠? 그러면 윈도우 함수 부분만 봅시다.

LRESULT CALLBACK WndProc(HWND hWnd, UINT mesg,
WPARAM wParam, LPARAM lParam)
{
    char szTitle[] = “Title is changed
!!!”;

새 타이틀바 제목을 위 문자열로하기 위해서 정의해 준겁니다.

    char szGetTitle[80];

현재 타이틀바 제목을 읽어와서 저장하기 위해서 위 변수를 선언했습니다.

    switch(mesg)
    {
        case WM_CREATE
:

            SetWindowText(hWnd,
szTitle);
            return FALSE;

못보던 메시지가 있네요. WM_CREATE라는 메시지는 윈도우가 처음 생성될 때 발생하는
메시지입니다. 대부분 변수를 초기화 하는 작업은 이 부분에서 해주죠. 위 구문을 해석하자면 윈도우가 생성될 때 바로 윈도우의 제목을 바꾼다는
의미가 되겠습니다. 별로 어렵지 않죠?

        case WM_KEYDOWN :

            if(LOWORD(wParam) ==
VK_RETURN)
            {
                GetWindowText(hWnd, szGetTitle,
80);
                MessageBox(hWnd, szGetTitle, “”,
MB_OK);
            }
            return FALSE;

역시 못보던 메시지로 WM_KEYDOWN 이라는 메시지가 나왔습니다. 이 메시지는 특수키가
입력되었을때 발생하는 메시지입니다. 특수키는 어떤 키를 의미할까요?

F1-F12, Shift, Ctrl, Alt, Delete키등과 같은 키를 의미합니다. 어떤
것인지 알겠죠? 이러한 특수키가 눌리면 WM_KEYDOWN이라는 메시지가 발생됩니다. 그러면 구체적으로 어떤 키인지 어떻게 알 수 있을까요?
물론 아는 방법이 있죠.

바로 부수적인 메시지가 들어간다는 wParam에 그 구체적인 값이 들어가는 것입니다.
wParam의 하위워드에 그 구체적인 값이 들어가기 때문에 위 구문과 같이 사용합니다. 하여간 이 부분은 입력부분에서 다시 자세히 설명 드리겠고
단지 Enter키가 눌리면 위 조건문이 참이 된다는 사실만 알고 있으면 됩니다. 조건문을 만족하면 현재 타이틀바 제목을 읽어올테고 그 다음은 또
모르는 함수네요.

int MessageBox(
    HWND
 hwndOwner,
    LPCTSTR  lpszText,
    LPCTSTR  lpszTitle,
    UINT
 fuStyle
   );

MessageBox() 함수는 유저에게 어떤 정보를 보여주기 위한 간단한 대화 상자입니다.
구체적인 사용방법은 대화상자 부분에서 다시 설명드리겠습니다. 단지 두번째 파라미터에 써준 문자열을 주제로 대화상자를 출력한다는 것입니다. 세번째
파라미터에 들어가는 문자열은 대화상자의 타이틀바 제목이 됩니다. 네번째는 버튼의 형식을 지정할 수 있는데 MB_OK이라는 것은 무조건 OK버튼
하나만 생성한다는 뜻입니다. 이번 예제에서 배우려고 하는 것이 MessageBox() 함수가 아니니 이정도로만 설명을 드리죠. 하여간 현재
윈도우의 타이틀바 제목을 가지고 와서 유저에게 그 제목을 보여주는 것이죠. 직접 컴파일해서 확인해 보기 바랍니다. 그러면 MessageBox()
함수가 구체적으로 어떤것인지 알 수 있을 겁니다.

        case WM_DESTROY :

            PostQuitMessage(0);
            return
FALSE;

윈도우를 종료하는 메시지가 오면 종료되겠죠.

    }
    return DefWindowProc(hWnd, mesg,
wParam, lParam);
}

자 오늘 강좌는
끝~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 

  윈도우 32비트 프로그래밍 05

안녕하세요…….돌팔이 황동준입니다.

저번시간에 이어서 계속 강좌를 해 보겠습니다. 이번에도 역시 윈도우를 다루는 예제인데 예제는
두 개입니다. 휴~~~ 언제 다 끝나나 이거 원…..

그럼 시작해 보죠.

우리는 윈 메인 함수의 제일 앞 부분에서 어떤 작업을 하는지에 대해 이미 알고 있습니다.
어떤 작업을 합니까? 클래스 등록을 하였죠? 이중에 아마 배경색을 지정해준 부분이 있을 겁니다. 이 배경색을 프로그램상에서 바꿀 수 있는 방법이
있습니다.

DWORD SetClassLong(
    HWND  hwnd,
    int
 nIndex,
    LONG  lNewVal
   );

위 함수를 이용해서 윈도우의 배경색을 바꿀 수 있는데 사실 위 함수로 배경색 뿐만 아니라
커서, 아이콘, 윈도우 함수, 스타일등 윈도우 클래스에 등록한것을 전부 바꿀 수 있습니다.

첫번째 파라미터는 바꿀 윈도우의 핸들을 의미하고 두번째 파라미터는 예약어로서 이 예약어로
어떤 것을 지정하느냐에 따라 해당 윈도우의 속성을 바꿀 수 있습니다.

세번째 파라미터는 바꿀 새로운 값을 의미합니다. 다음은 두번째 파라미터에 들어갈 예약어와 그
의미입니다.

GCL_HBRBACKGROUND   배경생을 바꿀때 사용합니다.
GCL_HCURSOR
        커서를 바꿀때 사용합니다.
GCL_HICON           아이콘을 바꿀때 사용합니다.
GCL_MENUNAME
       메뉴를 바꿀때 사용합니다.
GCL_STYLE           스타일을 바꿀때 사용합니다.
GCL_WNDPROC
        윈도우 함수를 바꿀때 사용합니다.

자 그러면 실제 예제를 보도록 합시다. 아래 예제는 유저가 Enter키를 쳤을 때 배경색을
검정색으로 바꾸는 예제입니다.

#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_KEYDOWN :

            if(LOWORD(wParam) ==
VK_RETURN)
            {
                SetClassLong(hWnd,
GCL_HBRBACKGROUND,
                    (LONG)GetStockObject(BLACK_BRUSH));
                InvalidateRect(hWnd,
NULL, TRUE);
            }
            return FALSE;

        case WM_DESTROY :

            PostQuitMessage(0);
            return
FALSE;
    }
    return DefWindowProc(hWnd, mesg, wParam,
lParam);
}

Enter키를 치면 하얀색이던 배경색이 검정색으로 바뀜을 확인할 수 있을 겁니다. 한번 직접
컴파일해서 실행 시켜 보십시요. 그럼 분석을 해봅시다. 역시 윈 메인 함수는 볼 필요가 없군요. 윈도우 함수 부분만 봅시다.

LRESULT CALLBACK WndProc(HWND hWnd, UINT mesg,
WPARAM wParam, LPARAM lParam)
{
    switch(mesg)
    {
        case
WM_KEYDOWN :

            if(LOWORD(wParam) ==
VK_RETURN)
            {

Enter키가 눌리면 수행이 되도록 위 구문을 사용한 것입니다. 앞 예제와 다른 점이
없죠?

                SetClassLong(hWnd,
GCL_HBRBACKGROUND,
                    (LONG)GetStockObject(BLACK_BRUSH));

배경색을 바꾸기 때문에 두번째 파라미터에 GCL_HBRBACKGROUND를 사용하였고 세번째
파라미터는 검정색을 의미하는 것입니다. 이미 다 알고 있을 겁니다.

                InvalidateRect(hWnd, NULL,
TRUE);

흠 .. 못보던 함수가 하나 있네요. 이 함수는 어떤 역할을 할까요? 이 함수는 윈도우를
다시 그리게 하는 역할을 합니다. 두번째 파라미터에 NULL을 지정했기 때문에 전체 윈도우를 다시 그립니다.

전체적으로 해석해 볼까요? 배경색을 바꾸고 다시 그린다. 뭐 그런 의미가 되겠네요.
InvalidateRect()함수 구문을 빼고 다시 컴파일 해보시기 바랍니다. 될까? 안될까?

BOOL InvalidateRect(
    HWND
 hwnd,
    CONST RECT  *lprc,
    BOOL  fErase
   );

첫번째 파라미터는 이미 다 아실테고 두번째 파라미터는 다시 그릴 윈도우의 사각 영역의 좌표를
의미합니다.

typedef struct _RECT {
    LONG
left;
    LONG top;
    LONG right;
    LONG bottom;
} RECT;

RECT 구조체의 구성이 위에 잘 나와있네요. 각 사각 좌표를 의미하는 것입니다. 특별한
것은 없죠. 만약에 유저가 지정한 사각 부분만 복구하고 싶으면 직접 위 구조체 변수를 선언하여 값을 채워서 지정해 주면 됩니다.

함수의 마지막 파라미터는 지정된 부분을 복구할 때 배경을 지워진 상태로 하겠느냐 아니면
그렇지 않겠느냐 하는 의미입니다. 말이 좀 어렵죠. 나중에 가면 다 이해할 겁니다.

            }
            return FALSE;

        case WM_DESTROY :

            PostQuitMessage(0);
            return
FALSE;
    }
    return DefWindowProc(hWnd, mesg, wParam,
lParam);
}

이상으로 첫번째 예제 설명 끝~~~~~~~~

그러면 두번째 예제를 보도록 합시다. 두번째 예제는 전체 윈도우의 크기와 작업영역(클라이언트
영역)의 크기를 알아내는 예제입니다. 함수 두개의 사용방법만 알면 뭐 간단하죠.

BOOL GetClientRect(
    HWND
 hwnd,
    LPRECT  lprc
   );   

위 함수로 윈도우의 작업영역의 크기를 알수 있습니다. 두번째 파라미터는 바로 위에서 알아
보았죠? 현재 윈도우의 작업영역 좌표가 두번째 파라미터에 들어가는 것입니다.

int GetSystemMetrics(
    int
 nIndex
   );

현재 화면의 크기를 알려면 위 함수를 이용해서 할수 있는데 위 함수로 꼭 현재 화면의
해상도만을 알수 있는 것은 아닙니다. 파라미터에 어떤 값이 들어가야 하는지에 따라 여러가지 크기를 얻어낼 수 있습니다. 여기서 현재 화면의
크기라는 것은 유저의 윈도우즈 해상도를 의미하는 것입니다. 물론 컬러수는 빼구요.

SM_CXCURSOR     커서의 넓이를 알수 있게 해줍니다.
SM_CYCURSOR
    커서의 높이를 알수 있게 해줍니다.
SM_CXFULLSCREEN 윈도우가 최대 크개일때 작업영역의 넓이를 알수 있게
해줍니다.
SM_CYFULLSCREEN 윈도우가 최대 크기일때 작업영역의 높이를 알수 있게 해줍니다.
SM_CXICON,
     아이콘의 넓이를 알수 있게 해줍니다.
SM_CYICON       아이콘의 높이를 알수 있게 해줍니다.
SM_CXMIN,
      윈도우의 최소 넓이를 알수 있게 해줍니다.
SM_CYMIN        윈도우의 최소 높이를 알수 있게
해줍니다.
SM_CXSCREEN,    현재 화면의 넓이를 알수 있게 해줍니다.
SM_CYSCREEN     현재 화면의 높이를 알수
있게 해줍니다.

자 그러면 실제적인 전체 소스를 보도록 합시다. 아래 예제는 윈도우가 생성될 때 작업영역과
화면의 크기를 구한 뒤 Enter키가 눌렸을 때 그 값을 출력하는 예제입니다.

#include <windows.h>
#include
<stdio.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)
{
    RECT rect;
    static char
szFullWindowSize[80];
    static char szWindowSize[80];

    switch(mesg)
    {
        case WM_CREATE
:

            GetClientRect(hWnd,
&rect);
            sprintf(szWindowSize, “X : %d, Y : %d”, rect.right,
rect.bottom);
            sprintf(szFullWindowSize, “X : %d, Y :
%d”,
                GetSystemMetrics(SM_CXSCREEN),
GetSystemMetrics(SM_CYSCREEN));
            return FALSE;

        case WM_KEYDOWN :

            if(LOWORD(wParam) ==
VK_RETURN)
            {
                MessageBox(hWnd, szWindowSize,
“Window Size”, MB_OK);
                MessageBox(hWnd, szFullWindowSize,
“Window Full Size”, MB_OK);
            }
            return FALSE;

        case WM_DESTROY :

            PostQuitMessage(0);
            return
FALSE;
    }
    return DefWindowProc(hWnd, mesg, wParam,
lParam);
}

별로 이해하기 어려운 부분은 없는것 같네요. 그렇죠?

case WM_CREATE :

    GetClientRect(hWnd,
&rect);
    sprintf(szWindowSize, “X : %d, Y : %d”, rect.right,
rect.bottom);
    sprintf(szFullWindowSize, “X : %d, Y :
%d”,
        GetSystemMetrics(SM_CXSCREEN),
GetSystemMetrics(SM_CYSCREEN));
    return FALSE;

이미 함수는 다 설명 드렸고 혹시 sprintf()라는 함수를 사용해 본적이 있습니까? 아마
printf()함수를 사용해 본 유저라면 위 함수를 잘 보면 어떤 역할을 하는지 쉽게 짐작할 수 있을 겁니다. sprintf()함수의 리턴값은
첫번째 파라미터에 저장된 문자열의 길이입니다. 상당이 많이 활용하니 이 리턴값을 잘 기억하시기 바랍니다.

case WM_KEYDOWN :

    if(LOWORD(wParam) ==
VK_RETURN)
    {
        MessageBox(hWnd, szWindowSize, “Window Size”,
MB_OK);
        MessageBox(hWnd, szFullWindowSize, “Window Full Size”,
MB_OK);
    }
    return FALSE;

Enter키가 눌리면 그 값을 출력해 주는 것입니다.

오늘은 여기까지 강좌
끝~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

zemna

Programmer/Web/Mobile/Desktop

You may also like...

Leave a Reply