2004. 4. 25. 12:11

2D테트리스만들기 1. 실전 윈도우 기본구조

####################################################
   2D테트리스만들기

             1. 실전 윈도우 기본구조

                  1) InitApp

                  2) InitInstance

                  3) WinMain

                  4) WndProc

                  5) OnCreate

                  6) OnPaint;

                  7) OnKey

                  8) OnDestroy;


####################################################


앞으로 강의 할 윈도우의 기본 구조를 분석해보자.  지금까진 워밍업이었다. 이제부터

실전에 들어가보자.  구조가 약간은 달라졌을 것이다. 그러나 중요 기능별로 함수를

만든것에 불과하다.   당부하고 싶은 말은 절대로 첨 보는 함수가 있더라도 어려워 하지

말고 모든 기능을 다 외운다는 생각은 버려야 할 것이다.  모르는 것이 있다면 그냥 이런

기능이 있구나 하구 넘어 가길 바랍니다.  나무에 너무 집착하면 넓은 숲을 못보기 때문입니다.

한번 차근히 검토해 보자.


//------------실전 예 시작------------------


#include <windows.h>
#include <windowsx.h>
// HANDLE_MSG, GlobalFreePtr을 만들려면 해더에 windowsx.h를 선언해 주어야 한다.

/************** 전역변수 선언 ***************/
HWND        hMainWnd ;
HINSTANCE        hMainInst;
char                szAppName[] = "윈도우이름" ;
/*****************************************/


/************** 함수선언 ***************/
BOOL InitApp(HINSTANCE hInstance);
// 윈도우의 기본적인 스타일과 아이콘, 커서모양등을 설정
BOOL InitInstance(HINSTANCE hInstance, int iCmdShow);
// 윈도우켑션이름과 위치나 크기등을 정한다.
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
// 윈도우에 실제 명령을 수행하게됨
BOOL OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct);
// 윈도우를 생성한다.
void OnPaint(HWND hwnd);
// 화면을 그린다.
void OnKey(HWND hwnd, UINT vk, bool fdown, int cRepeat, UINT flags);
// 키명령을 수행한다.
void OnDestroy(HWND hwnd);
// 윈도우를 닫는다.
/*****************************************/


BOOL InitApp(HINSTANCE hInstance)
{
    WNDCLASSEX  wndclass ;


    wndclass.cbSize        = sizeof (wndclass) ;//구조체의크기
    wndclass.style         = CS_HREDRAW | CS_VREDRAW ; //윈도우의 수평,수직 크기가
                                                       //변경될 때 다시 그려준다.
    wndclass.lpfnWndProc   = WndProc ; // 윈프록 함수 이름선언
    wndclass.cbClsExtra    = 0 ;
    wndclass.cbWndExtra    = 0 ;
    wndclass.hInstance     = hInstance ; //현재 winMain()에서 사용되는 Handle값은
                                             // 무엇이냐 OS가 Handle값을 전달 해 준다.
    wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
                              //IDI_APPLICATION는 ICON을 default로 쓰겠다는 의미.
    wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
                              //IDC_ARROW은 화살표 모양 커서를 나타냄.
    wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
                               //WHITE_BRUSH은 바탕색을 흰색으로 쓰겠다
    wndclass.lpszMenuName  = ""; // NULL;로해도된다.
                        // 만약 메뉴 이름이 ""로 안되어있고 그냥 이름으로 되어있다면
                        // MAKEINTRESOURCE(IDR_MENU1); 함수를 사용한다.
    wndclass.lpszClassName = szAppName ;
                     //클래스 이름. SzAppName는 클래스 이름을 담는 문자열 변수이름이다.
    wndclass.hIconSm       = LoadIcon (NULL, IDI_APPLICATION) ;

    return(RegisterClassEx (&wndclass)) ;
}

BOOL InitInstance(HINSTANCE hInstance, int iCmdShow)
{
    hMainWnd = CreateWindow (szAppName,     //윈도우 클래스 이름
                            "윈도우켑션",   //윈도우 켑션
                    WS_OVERLAPPEDWINDOW,//윈도우 유형
                    CW_USEDEFAULT,      //초기 x위치
                    CW_USEDEFAULT,      //초기 y위치
                    CW_USEDEFAULT,      //초기 x크기
                    CW_USEDEFAULT,      //초기 y크기
                    NULL,               //부모 윈도우 핸들
                    NULL,               //윈도우 메뉴 핸들
                    hInstance,          //프로그램 인스턴스 핸들
                    NULL) ;           //생성 매개 변수

    if (!hMainWnd) return FALSE;

    hMainInst = hInstance;

    ShowWindow (hMainWnd, iCmdShow) ;
        //크기나 모양.. 을 눈에 보이게 하는 함수.
        // iCmdShow(속성표현-맨 하단의 아이콘 표현 유무)
    UpdateWindow (hMainWnd) ;
        //크기나 모양.. 을 눈에 보이게 하는 함수.
    return TRUE;
}

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PSTR szCmdLine, int iCmdShow)
{
    MSG         msg ;

        if ((!hPrevInstance) & (!InitApp(hInstance)))
            return FALSE;

        if (!InitInstance(hInstance, iCmdShow)) return FALSE;

    while (GetMessage (&msg, NULL, 0, 0))//들어 온 메시지를 받아냄.
                        //MFC의 LButton과 Rbutton과 같음.

    {
      TranslateMessage (&msg) ; //알 수 있는 메시지 형태로 변환.
                        // (키보드에서 메세지를 받아온다.)
      DispatchMessage (&msg) ; //Msg값을 4부분으로 쪼개서 winProc에 인
                //로 보내고 나서 winProc함수에 WN_DESTRO
                //가 올 때 까지 무한루프를 돌린다.
                // (메세지를 윈도우 프로시저에 전달)

    return msg.wParam ;
}

LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg,
                                    WPARAM wParam, LPARAM lParam)
{
    switch (iMsg)
    {
        HANDLE_MSG(hwnd, WM_CREATE, OnCreate);
                // HANDLE_MSG는 메세지를 받아 사용자 함수에 전달하는 역활을 한다.
        HANDLE_MSG(hwnd, WM_PAINT, OnPaint);
        HANDLE_MSG(hwnd, WM_KEYDOWN, OnKey);
        HANDLE_MSG(hwnd, WM_KEYUP, OnKey);
        HANDLE_MSG(hwnd, WM_DESTROY, OnDestroy);
    }

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

BOOL OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct)
{
        return 1; // True;를 사용해도 된다.
}

void OnPaint(HWND hwnd)
{
    HDC hdc; // 디바이스컨텍스트라는 핸들을 생성한다.(페인트에필수)
    PAINTSTRUCT ps; //(페인트에필수)

    hdc = BeginPaint(hwnd, &ps);
        EndPaint(hwnd, &ps);
}

void OnKey(HWND hwnd, UINT vk, bool fdown, int cRepeat, UINT flags)
{
        switch (vk)
        {
                case VK_ESCAPE :
                        PostQuitMessage(0);
                        // 윈도우핸들에 WM_CLOSE메시지를 보낸다.
                        //SendMessage (hMainWnd, WM_DESTROY, 0, 0L);
                        break;
        }
}

void OnDestroy(HWND hwnd)
{
    PostQuitMessage(0);
}



//------------실전 예 끝------------------


1) BOOL InitApp(HINSTANCE hInstance);

첨보는 함수일 것이다.  내가 만든 함수니깐...ㅎㅎ

이 함수는 윈도우 클래스 등록하는 부분을 따로 분리한 것이다. 모든 함수는 최대한 가볍게 가져가야

한다는 기본 원리를 따른 것입니다.  이해를 돕기 위해 이렇게 한다구 하는군여.

    wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;

여기서 (HBRUSH)는 클레스 형이 hbr이기때문에 형을 변환하기 위해 써준것이다. GetStockObject는

pens, brushes, fonts, or palettes의 핸들을 받아오는 기능을 한다.  많이 사용되니 알아두기 바랍

니다.

    wndclass.lpszMenuName  = ""; // NULL;로해도된다.
        // 만약 메뉴 이름이 ""로 안되어있고 그냥 이름으로 되어있다면
        // MAKEINTRESOURCE(IDR_MENU1); 함수를 사용한다.

  이것은 윈도우에 들어가 메뉴 이름을 넣는 것인데.  아직은 사용하지 않으니 자세한 내용은 다음으로

미루도록 하겠다.

    return(RegisterClassEx (&wndclass)) ;

생성한 클레스를 레지스터에 등록한후 리턴값을 돌려준다.



2) BOOL InitInstance(HINSTANCE hInstance, int iCmdShow);

윈도우를 생성하고 보여주는 함수이다.

    hMainWnd = CreateWindow (...);

hMainWnd는 윈도우 핸들을 전역 변수로 만들어 필요한 함수에서 가져다 쓰게 하려구 선언했다.

    if (!hMainWnd) return FALSE;

윈도 생성이 실패하면 false를 리턴한다.  윈도우 생성이 안되는 것이다.

    hMainInst = hInstance;

인스턴스 핸들을 전역 변수로 만든다.




3) int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                            PSTR szCmdLine, int iCmdShow)

        if ((!hPrevInstance) & (!InitApp(hInstance)))
            return FALSE;
hPrevInstance 몰라도 된다. InitApp함수 리턴값을 알아본다.

        if (!InitInstance(hInstance, iCmdShow)) return FALSE;

InitInstance함수 리턴값을 알아본다.



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

HANDLE_MSG함수로 모든 윈도우 메세지를 특정 함수에 전달한다.  HANDLE_MSG를 사용하지

않구 직접 메세지를 처리해도 상관없으나 WndProc함수 보기 좋게 정리 하기 위해 사용한다.

참고] 함수 원형을 보면

BOOL OnCreate(HWND hWnd, LPCREATESTRUCT lpCreateStruct);
void OnChar(HWND hWnd, TCHAR ch, int cRepeat);
void OnKey(HWND hWnd, UINT vk, BOOL fDown, int cRepeat, UINT flags);
void OnPaint(HWND hWnd);
void OnSetFocus(HWND hWnd, HWND hwndOldFocus);
void OnKillFocus(HWND hWnd, HWND hwndNewFocus);
void OnDestroy(HWND hWnd);
LRESULT OnImeComposition(HWND hWnd, WPARAM wParam, LPARAM lParam);
LRESULT OnImeChar(HWND hWnd, WPARAM wParam, LPARAM lParam);
void OnLButtonDown(HWND hWnd, BOOL fDoubleClick, int x, int y, UINT keyFlags);
void OnMouseMove(HWND hWnd, int x, int y, UINT keyFlags);
void OnLButtonUp(HWND hWnd, int x, int y, UINT keyFlags);
void OnTimer(HWND hWnd, UINT id);
void OnSize(HWND hWnd, UINT state, int cx, int cy);
void OnHScroll(HWND hWnd, HWND hwndCtl, UINT code, int pos);
void OnVScroll(HWND hWnd, HWND hwndCtl, UINT code, int pos);
void OnContextMenu(HWND hWnd, HWND hwndContext, UINT xPos, UINT yPos);
void OnCommand(HWND hWnd, int id, HWND hwndCtl, UINT codeNotify);
BOOL OnSetCursor(HWND hWnd, HWND hwndCursor, UINT codeHitTest, UINT msg);
UINT OnGetDlgCode(HWND hWnd, LPMSG lpmsg);




5) BOOL OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct);
윈도우를 생성한다.



6) void OnPaint(HWND hwnd);
화면을 그린다.



7) void OnKey(HWND hwnd, UINT vk, bool fdown, int cRepeat, UINT flags);

vk 키값이 넘어온다.



8) void OnDestroy(HWND hwnd);

PostQuitMessage 응용 프로그램에 종료 WM_QUIT에 메세지를 전달한다.