General/C, C++, C#

[API] 기본예제(창 띄우기)

croute 2009. 11. 30. 13:56

#include <windows.h>

LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
HINSTANCE g_hInst;
LPCTSTR lpszClass=TEXT("First");

int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance
    ,LPSTR lpszCmdParam,int nCmdShow)
{
 HWND hWnd;
 MSG Message;
 WNDCLASS WndClass;
 g_hInst=hInstance;
 
 WndClass.cbClsExtra=0;
 WndClass.cbWndExtra=0;
 WndClass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
 WndClass.hCursor=LoadCursor(NULL,IDC_ARROW);
 WndClass.hIcon=LoadIcon(NULL,IDI_APPLICATION);
 WndClass.hInstance=hInstance;
 WndClass.lpfnWndProc=WndProc;
 WndClass.lpszClassName=lpszClass;
 WndClass.lpszMenuName=NULL;
 WndClass.style=CS_HREDRAW | CS_VREDRAW;
 RegisterClass(&WndClass);

 hWnd=CreateWindow(lpszClass,lpszClass,WS_OVERLAPPEDWINDOW,
  CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
  NULL,(HMENU)NULL,hInstance,NULL);
 ShowWindow(hWnd,nCmdShow);
 
 while (GetMessage(&Message,NULL,0,0)) {
  TranslateMessage(&Message);
  DispatchMessage(&Message);
 }
 return (int)Message.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
 switch (iMessage) {
 case WM_DESTROY:
  PostQuitMessage(0);
  return 0;
 }
 return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}

 

 

 

코드분석 ================================================================================

header file - windows.h

 

typedef struct tagWNDCLASS

{

  UINT            style;              // 윈도우의 스타일을 정의 윈도우가 어떤형태를 가질지 결정

  WNDPROC    lpfnWndProc;   // 메시지 처리함수를 지정. 이름을 정할수있지만 관례상 WndProc 

  int                cbClsExtra;    // 일종의 예약 영역.(여분의 공간) 특수목적사용. 기본 0 지정.

  int                cbWndExtra;   //  " 

  HINSTANCE  hInstance;      // 윈도우 클래스를 등록하는 프로그램의 번호.

                                              WinMain의 인수로 전달된 hInstance값을 그대로 대입.

  HICON          hIcon;            // 마우스 커서 지정. LoadCursor 함수 사용

  HCURSOR     hCursor;        // 윈도우 아이콘을 지정. LoadIcon 함수 사용

  HBRUSH       hbrBackground; // 윈도우 배경 색상을 채색할 브러시를 지정하는 멤버.

                                                GetStockObject함수 사용 기본 브러시를 지정하거나

                                                 COLOR_WINDOW같은 시스템 색상을 지정할 수도 있다.

  LPCSTR       lpszMenuName; // 이 프로그램이 사용할 메뉴를 지정.

    // 실행중 만들어지는것이 아니라 리소스 에디터에 의해 별도로 만들어져 링크시에 같이 합쳐진다.

  LPCSTR       lpszClassName; // 윈도우 클래스의 이름을 문자열로 정의.

    // 여기서 지정한 이름은 CreateWindow 함수에 전달되며 CreateWindow 함수는

        윈도우 클래스에서 정의한 특성값을 참조하여 윈도우를 만든다.

} WNDCLASS;

 

 

 

 

코드분석 ================================================================================

 

윈도우 생성함수 - HWND CreateWindow(lpszClassName, lpszWindowName, dwStyle, x, y, nWidth, nHeight, hwndParent, hmenu, hinst, lpvParam)

 

 hWnd = CreateWindow(lpszClass,lpszClass,WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,CW_USEDEFAULT,

CW_USEDEFAULT,CW_USEDEFAULT, NULL,(HMENU)NULL,hInstance,NULL);

lpszClassName - 생성하고자 하는 윈도우의 클래스를 지정하는 문자열

 

lpszWindowName - 윈도우의 타이틀바에 나타날 문자열이다

                            여기서 지정한 문자열이 윈도우의 타이틀 바에 나타난다.

 

dwStyle - 만들고자하는 윈도우의 형태를 지정하는 인수. 일종의 비트 필드값.

              수십개를 헤아리는 매크로 상수들이 정의되어 있고,

              이 상수들은 OR연산자로 연결하여 윈도우의 다양한 형태를 지정한다.

              크기조절이 가능하도록 할 것인가, 타이틀 바를 가질 것인가,

              또는 스크롤바의 유무 등등을 세세하게 지정할 수 있다.

              기본적으로 WS_OVERLAPPEDWINDOW 스타일을 사용하면 가장 무난한 윈도우가 된다.

 

x, y, nWidth, nHeight - 인수의 이름이 의미하듯 윈도우의 크기를 지정. 픽셀단위를 사용.

                                 ex) 100, 100, 640, 480으로 주면 화면의 (100, 100)위치에

                                       폭 640픽셀, 높이 480픽셀 크기로 윈도우가 만들어진다.

                                 CW_USEDEFAULT를 사용하면 운영체제가 화면 크기에 맞게

                                 적당한 크기와 위치를 알아서 설정한다.

 

hWndParent - 부모 윈도우가 있을경우 부모 윈도우의 핸들을 지정한다.

                      MDI 프로그램이나 팝업 윈도우는 윈도우끼리 수직적 상하관계를 가져서

                      부자(parent-chile)관계가 성립되는데 이 관계를 지정하는 인수이다.

                    부모 윈도우가 없을 경우, 즉 자신이 최상위 윈도우일 경우는 이 값을 NULL로 지정,

                    이렇게하면 데스크탑을 부모로 가져 바탕화면 어디나 돌아다닐 수 있는 윈도우가 된다.

 

hmenu - 윈도우에서 사용할 메뉴의 핸들을 지정.

             윈도우클래스에도 메뉴를 지정하는 멤버가 있는데 윈도우 클래스의 메뉴는,

              그 윈도우 클래스를 기반으로 하는 모든 윈도우에서 공통적으로 사용되는 반면

             이 인수로 지정된 메뉴는 현재 CreateWindow 함수로 만들어지는 윈도우에서만 사용.

 

hinst - 윈도우를 만드는 주체, 즉 프로그램의 핸들을 지정한다.

           WinMain의 인수로 전달된 hInstance를 대입하면 된다.

           운영체제는 누가 윈도우를 만들었는지 기억해 두었다가 프로그램이 종료될때,

           파괴되지 않은 윈도우를 자동으로 파괴한다.

 

lpvParam - CREATESTRUCT라는 구조체의 번지이며 여러개의 윈도우를 만들때,

                각 윈도우에 고유의 파라미터를 전달하는 특수한 목적에 사용된다. 보통은 NULL값 사용.

 

CreateWindow함수는 윈도우에 관한 모든 정보를 메모리에 만든 후 윈도우를 대표하는 번호인

윈도우 핸들을 리턴한다. 넘겨지는 윈도우 핸들은 hWnd라는 변수에 저장되었다가,

이 윈도우를 참조하는 모든 함수의 인수로 사용된다.

CreateWindow 함수로 만든 윈도우는 메모리상에만 있을 뿐, 아직 화면에 출력되지는 않았다.

메모리에 만들어진 윈도우를 화면으로 보이게 하려면 아래의 함수를 사용한다.

 

BOOL ShowWindow(hWnd, nCmdShow);

 

hWnd 인수는 화면으로 출력하고자 하는 윈도우의 핸들이며 CreateWindow 함수가 리턴한 핸들을

그대로 넘기면 된다. nCmdShow는 윈도우를 화면에 출력하는 방법을 지정하며 다음과 같은 매크로 상수들이 정의되어 있다.

 

SW_HIDE - 윈도우를 숨긴다.

SW_MINIMIZE - 윈도우를 최소화하고 활성화시키지 않는다.

SW_RESTORE - 윈도우를 활성화 시킨다.

SW_SHOW - 윈도우를 활성화하여 보여준다.

SW_SHOWNORMAL - 윈도우를 활성화하여 보여준다.

 

nCmdShow 인수에 어떤 값을 넘겨줄 것인가는 전혀 고민할 필요가 없다.

WinMain 함수의 인수로 전달된 nCmdShow를 그대로 전달하면 된다.

 

 

 

 

코드분석 ================================================================================

 

메시지루프

while (GetMessage(&Message,NULL,0,0)) {
  TranslateMessage(&Message);
  DispatchMessage(&Message);
}

 

윈도우즈는 프로그램의 실행순서가 명확하게 정해져 있지 않으며 상황에 따라 실행 순서가 달라진다.

여기서 말하는 상황이란 바로 <어떤 메시지가 발새했는가>를 의미한다.

 

메시지란 사용자나 시스템의 내부적인 동작에 의해 발생된 일체의 변화에 대한 정보.

(마우스 버튼 클릭, 키보드 누름, 윈도우가 최소화 되었다거나 하는 변화에 대한 정보)

 

윈도우즈 프로그램에서 메시지를 처리하는 부분을 메시지 루프(Message Loop)라고 하며

보통 WinMain 함수의 끝에 존재한다.

 

BOOL GetMessage(LPMSG lpMsg, HWND hWnd, UINT, wMsgFilterMin, UINT, wMsgFilterMax);

이 함수는 메시지 큐에서 메시지를 읽어들인다.

메시지 큐(Message Queue)는 시스템이나 사용자로부터 발생된 메시지가 잠시대기하는 저장영역.

읽어들인 메시지는 첫 번째 인수가 지정하는 MSG 구조체에 저장된다.

이 함수는 읽어들인 메시지가 프로그램을 종료하라는 WM_QUIT일 경우 FALSE를 리턴,

그 외의 메시지는 TRUE를 리턴. WM_QUIT 메시지가 읽혀질때까지 while 루프를 계속 반복.

 

BOOL TranslateMessage(CONST MSG *lpMsg);

이 함수는 키보드 입력 메시지를 가공하여 프로그램에서 쉽게 쓸 수 있도록 한다.

TranslateMessage 함수는 키보드의 눌림(WM_KEYDOWN)메시지가 발생할때 문자가 입력되었다는

메시지(WM_CHAR)를 만드는 역할을 한다.

 

LONG DispatchMessage(CONST MSG* lpmsg);

이 함수는 메시지 큐에서 꺼낸 메시지를 윈도우의 메시지 처리 함수(WndProc)로 전달한다.

함수에 의해 메시지가 윈도우로 전달되며

프로그램(윈도우 프로시저)에서는 전달된 메시지를 점거하여 다음 동작을 결정한다.

메시지를 전달하고 나면 다시 루프의 선두로 돌아가 다음 메시지를 기다린다.

 

위 메시지 루프의 세 함수는 아래의 공통된 MSG구조체를 사용한다.

 

typedef struct tagMSG

{

    HWND        hwnd;      // 메시지를 받을 윈도우 핸들

    UINT          message; // 어떤 종류의 메시지인가를 나타낸다. 가장 중요한 값

    WPARAM   wParam;   // 전달된 메시지에 대한 정보를 가진다. 32bit 값 

    LPARAM    lParam;    // 전달된 메시지에 대한 부가정인 정보를 가진다. 32bit 값

    DWORD      time;        // 메시지가 발생한 시간

    POINT        pt;           // 메시지가 발생했을 때의 마우스 위치

} MSG;

 

메시지는 실제로 하나의 정수값으로 표현,

종류가 무척 많고 메시지 번호를 일일이 암기하여 사용하기 힘들기 때문에

windows.h에 메시지별로 매크로 상수를 정의해 두었으며 접두어 WM_ 으로 시작된다.

아래는 자주 사용되는 메시지들이다

 

WM_QUIT - 프로그램을 끝낼 때 발생하는 메시지

WM_LBUTTONDOWN - 마우스의 좌측 버튼을 누를 경우 발생

WM_KEYDOWN - 키보드의 키를 누름

WM_CHAR - 키보드로부터 문자가 입력될 때 발생

WM_PAINT - 화면을 다시 그려야 할 필요가 있을 때 발생

WM_CREATE - 윈도우가 처음 만들어질 때 발생

WM_DESTROY - 윈도우가 메모리에서 파괴될 때 발생

 

메시지 루프가 종료되면 프로그램은 마지막으로 Message.wParam을 리턴하고 종료한다.

WM_QUIT 메시지로부터 전달된 탈출 코드(exit code)이며 프로그램을 실행한 운영체제로 리턴된다.

 

 

 

 

코드분석 ================================================================================

 

윈도우 프로시저 - WndProc

운영체제에 의해 호출

WndProc은 메시지가 입력되면 운영체제에 의해 호출되어 메시지를 처리

(운영체제에 의해 호출되는 응용 프로그램 내의 함수를 콜백(CALLBACK) 함수라 부른다.)

 

인수는 모두 4개이며 MSG 구조체 앞쪽 멤버 4개와 동일하다.

    HWND        hWnd;      // 메시지를 받을 윈도우 핸들

    UINT          iMessage; // 어떤 종류의 메시지인가를 나타낸다. 가장 중요한 값

    WPARAM   wParam;   // 전달된 메시지에 대한 정보를 가진다. 32bit 값 

    LPARAM    lParam;    // 전달된 메시지에 대한 부가정인 정보를 가진다. 32bit 값

 

hWnd는 메시지를 받을 윈도우의 핸들,

iMessage는 어떤 종류의 메시지인지,

  예를 들어 iMessage가 WM_SIZE - 윈도우의 크기가 변경되었음

  WM_KEYDOWN - 사용자가 키보드를 누른것 / WM_DESTROY - 윈도우가 파괴되었음

wParam, lParam은 iMessage의 메시지에 따른 부가적인 정보를 가진다.

  예를 들어 WM_LBUTTONDOWN 메시지가 입력되면 화면 어디쯤에서 마우스 버튼이 눌렸는지,

  그때의 키보드 상황(Shift, Ctrl, Alt)은 어떠한가에 관한 정보가 추가로 담으며,

  WM_CHAR 메시지가 입력되면 어떤문자가 입력되었는가에 관한 추가적 정보를 담는다.

기본 구조는 아래와 같다.

 

switch (iMessage) {
    case MSG1:
        처리1;
        return 0;

    case MSG2;

        처리2;
        return 0;

    case MSG3:
        처리3;
        return 0;

    default:

        return DefWindowProc(....);

}

 

마지막의 DefWindowProc 함수는 WndProc에서 처리하지 않는 나머지 메시지에 관한 처리를 한다.

 

예제 코드)

LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
    switch (iMessage) {
    case WM_DESTROY:
    PostQuitMessage(0);
    return 0;

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