OS/Windows API

#1 Windows API 소스 분석

VallistA2014. 8. 20. 15:33

저번 API 기초에서 시작하여,

오늘은 소스 분석을 해볼까 합니다.


오늘 공부할 것은 

 - WinMain의 기본, 윈도우 클래스

 - 메시지 루프


가 되겠습니다.


Windows API는 베이스 소스가 콘솔에서 했던 소스들과 전혀 다르게, WinMain 이란 것을 쓰며 상당히 소스가 깁니다.

소스가 긴 이유는 윈도우 라는 창을 만들기 위해서는 소스가 굉장히 많이 필요하며, 수많은 이벤트 등을 대응 하기위해서는 많은 소스가 필요하기 때문입니다.

소스를 보기전에 API에서 많이 다룰 이름에 대해서 알아봅시다.


우리가 API를 하면서 가장 많이 들을 이름들 입니다.


핸들 (Handle) : OS에게 요청한 오브젝트들을 중복되지 않게 핸들값을 부여합니다. 핸들이란 즉 오브젝트에 대응하는 겹치지 않는 값 입니다.  

왜 이러한 핸들의 값을 부여를 했는가 하면. OS는 오브젝트를 생성한 후에 중복되지 않는 정수형의 ID를 핸들값으로서 부여한 후 프로세서에 넘김으로서 오브젝트의 안정석을 확보하고 이 핸들을 받아 생성된 오브젝트를 아무대서나 자유롭게 참조 할 수 있게 만들어 놓았습니다. (즉 c의 포인터 같은 역할)

또, 핸들은 프로그램 종료시에 알아서 해제가 되므로 걱정하지 말고 만들어도 됩니다. (물론 수많이 만들라는 말은 아닙니다. 꼭 필요한 것만.)


인스턴스 (Instance) : 클래스가 메모리에 실제로 구현된 실체를 의미합니다. 즉 실행된 프로그램 이라는 뜻 입니다.


핸들 인스턴스 (Handle Instance),(HInstance) : 프로그램 코드를 담고 있는 모듈에 대한 핸들, 현재 실행중인 인스턴스에 관한 정보를 터득 가능

핸들 윈도우 (Handle Window),(HWND) : 현재 윈도우의 핸들을 가져옵니다. 

핸들 인스턴스는 핸들 윈도우를 가져올 수 있지만, 핸들 윈도우는 핸들 인스턴스를 가져오지 못합니다. 즉 핸들인스턴스가 핸들 윈도우보다 더 상위 객체 입니다.


윈도우 (WND),(Window) : 윈도우의 약어 입니다.


메시지 (Message), (MSG) : 메시지 입니다.


...W (뒤에 W가 붙는 오브젝트) : W는 유니코드 함수 또는 변수를 의미 합니다.

보통 W는 잘 쓰지 않습니다. 왜냐하면 API를 할 때 유니코드 중심이 아닌, 멀티바이트 중심으로 하기 때문이죠.

Visual Studio의 환경에 따라 UTF, ANCII 등으로 다 바뀌기 때문입니다. UTF-8 은 한글의 경우 3Byte 이며 영문의 경우 1Byte 입니다. UTF-16은 한글의 경우 2Byte, 영문의 경우 1Byte 입니다.


...A (뒤에 A가 붙는 오브젝트) : A는 멀티바이트 함수 또는 변수를 의미합니다.

보통 A를 많이 사용하며, 멀티바이트 기준으로 코딩을 하게 됩니다.


앞에 L을 붙이는 이유 : 문자열 앞에 L 을 붙이는 이유는 멀티바이트에서 유니코드를 사용하기 위해서 입니다.

예를들어 멀티바이트와 유니코드가 존재하는데, 유니코드는 char를 사용하지만 멀티바이트에서는 WCHAR를 사용합니다. 그것은 멀티바이트에서도 유니코드 변수를 사용하기위해서 사용하는 것이고 기존 Direct 3D나 모든 곳에서는 영문을 기준으로 사용하기 때문에 한국이나 2Byte 문자를 쓰는 나라들은 그러한 라이브 러리를 사용하기 위해서 유니코드 멀티바이트 호환이 필요했습니다.

그것을 W나 A로 구분을 짓고 유니코드에서는 A를 사용하므로써 멀티바이트 호환, 멀티바이트에서는 W를 사용하므로써 유니코드 호환이 가능해졌습니다.


WPARAM : unsigned int 형으로써 주로 값을 넘기는데 사용합니다. 여기서 W는 Word 자료형의 줄임으로써 Word는 unsigned int를 줄인 형 입니다.


LPARAM : unsigned long 형으로 값들을 넘기는데에도 쓰고 포인터를 넘겨줄 때에도 사용합니다. 여기서 L은 Long의 줄인 형 입니다.


LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);  
//!< 콜백은 선언합니다. WndProc은 모든 이벤트를 관리합니다.
HINSTANCE g_hlnst; 
//!< 핸들 인스턴스를 전역으로 선언합니다. 핸들 인스턴스는 프로그램 코드를 담고 있는 모듈에 대한 핸들입니다. 프로그램 코드를 담고있는 파일을 메모리의 특정역역에 올려서 준비를 해놓습니다. 즉 현재 실행중인 인스턴스에 관한 정보를 가지고 있습니다.
LPCTSTR lpszClass=TEXT("First");
//!< 현재 윈도우의 이름을 선언 합니다. 
int APIENTRY WinMain(HINSTANCE hlnstance, HINSTANCE hPrevlnstance , LPSTR lpszCmdParam,int nCmdShow) 
//!< APIENTRY : CallBack 과 같은 뜻을 가진 함수 입니다. 내부적으로 돌아가는 것이나, 똑같습니다. CallBack 과 같은 의미로 사용하신다고 보시면 됩니다.
//!< hInstance : 현재 프로그램의 인스턴스 핸들입니다. (윈도우 에서는 모든 프로그램 각각의 윈도우별로 핸들값이 주어집니다.)
//!< hPrevInstance : 바로 앞에 실행된 현재 프로그램의 인스턴스 핸들입니다. 호환성을 위해서 존재합니다.
//!< lpszCmdParam : 명령 행입니다. 도스에서의 argv 인수에 해당합니다.
//!< nCmdShow : 프로그램이 실행될 형태 입니다. 최소화, 보통모양이 전달됩니다. WinMain 에서는 윈도우를 뿌려주기만 하는 작업을 합니다. 즉 윈도우의 색이나 메뉴등 옵션을 바꾸려면 여기서 바꾸어야겠고 이벤트를 받아오기 위해서는 MsgProc 에서 받아오면 됩니다.
{
     HWND hWnd;                 //!< HWND 는 윈도우 자체의 정보입니다.
     MSG Message;               //!< MSG 는 구조체로 이루어져 있습니다. 원형은 tagMSG 입니다. 내부에는 여러가지 정보가 들어가 있습니다. 메세지 관련해서 일을 합니다.
     WNDCLASS WndClass;         //!< WNDCLASS는 Define 으로 이루어져 있으며, 원형은 WNDCLASSA 입니다. 현재 윈도우 창에 관련된 속성값들이 들어가 있습니다.
     g_hlnst=hlnstance;         //!< 현재 글로벌로 선언된 핸들 인스턴스에 윈도우창의 정보를 넣습니다. 
     WndClass.cbClsExtra=0;     //!< 예약 영역 입니다. 윈도우즈가 내부적으로 사용하며 아주 특수한 목적에 사용되는 여분의 메모리 공간입니다. Cls는 현재 여분의 메모리를 체크하는 기능을 가지고 있습니다.
     WndClass.cbWndExtra=0;     //!< 예약 영역 입니다. 윈도우즈가 내부적으로 사용하며 아주 특수한 목적에 사용되는 여분의 메모리 공간입니다. Wnd는 현재 여분의 메모리입니다.
     WndClass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);   //!< 윈도우즈의 뒷 배경 색상을 지정합니다. (채색할브러쉬)GetStockObject 라는 함수를 사용하여 윈도우즈에서 기본적으로 제공하는 브러쉬를 지정하거나 아니면 시스템 색상을 지정할 수도 있습니다.
     WndClass.hCursor=LoadCursor(NULL,IDC_ARROW);                  //!< 이 윈도우가 사용할 마우스 커서를 지정합니다. 유저가 직접 만들수도 있습니다.
     WndClass.hIcon=LoadIcon(NULL,IDI_APPLICATION);                //!< 이 윈도우가 사용할 아이콘을 지정합니다. 유저가 직접 만들수도 있습니다.
     WndClass.hInstance=hlnstance;                                 //!< 이 윈도우가 사용할 프로그램의 번호를 가져옵니다. 이 값은 인수로 전달된 hInstance 를 가져와 쓰므로 현재 윈도우의 상태를 가져오는데 쓰입니다. 핸들인스턴스에 관해서는 위에 주석으로 있습니다.
     WndClass.lpfnWndProc=WndProc;                                 //!< 윈도우의 메시지 처리 함수를 지정합니다. 메시지가 발생할 때마다 이 멤버가 지정하는 함수가 호출되며 이 함수가 모든 메시지를 처리합니다. 메시지 호출 함수의 이름은 WndProc으로 정해져 있습니다.
     WndClass.lpszClassName=lpszClass;                             //!< 윈도우 클래스 이름을 문자열로 정의합니다. 보통은 윈도우 클래스에서 정의한 특성값을 고려하여, 실행파일 이름과 똑같이 만듭니다.
     WndClass.lpszMenuName=NULL;                                   //!< 이 프로그램이 사용할 메뉴를 지정합니다. 메뉴는 프로그램 코드에서 실행 중에 만드는 것이 아닙니다. 리소스 에디터에서 커스텀마이징을 하고 링크를 해주면 합쳐집니다. 메뉴를 사용하지 않을 경우 NULL을 대입해주면 됩니다.
     WndClass.style=CS_HREDRAW | CS_VREDRAW;                       //!< 윈도우의 스타일을 정의합니다. 윈도우가 어떠한 형태를 가질 것인가를 지정합니다. 이 멤버가 가질 수 있는 값은 수많이 존재합니다. 하지만 가장 많이 사용하는 값은 CS_HREDRAW 와 CS_VREDRAW 입니다. 두값을 OR 연산자로 이어서 사용하며, 이 스타일은 수직 및 수평 크기가 변할 경우 윈도우를 다시 그린다는 뜻입니다. 한번 여러가지 대입해보면서 해보시기 바랍니다.

     RegisterClass(&WndClass);                                     //!< 윈도우 클래스를 등록합니다. 함수의 인자로 WndClass 구조체의 번지수를 넘겨주면 됩니다. 이런 특성을 가진 윈도우를 앞으로 사용하겠다는 등록 과정이며 운영체제는 이 윈도우 클래스의 특성을 잘 기억해 놓습니다.
     hWnd=CreateWindow(lpszClass,lpszClass,WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,(HMENU)NULL,hlnstance,NULL);                                                          //!< 윈도우 클래스를 작성을 다 끝 마쳤으므로 실제 윈도우 창을 구성합니다.
//!< Create WindowEx가 있고 CreateWindow가 존재합니다.
//!< 첫번째 인자 : 생성하고자 하는 윈도우의 클래스를 지정한 문자열 입니다. CreateWindow 함수는 윈도우 클래스에 정의된 속성대로 윈도우를 생성합니다. 
//!< 두번째 인자 : 윈도우 타이틀 바에 나타날 문자열입니다. 여기서 지정한 문자열이 윈도우 타이틀 바에 나타납니다. 프로그래머가 마음대로 지정할 수 있습니다.
//!< 세번째 인자 : 만드려는 윈도우 형태를 지정하는 인수입니다. 여러가지 값들을 OR 연산자로 나타낼 수 있습니다. WS_OVERLAPPEDWINDOW를 가장 보편적으로 사용합니다.
//!< 네번째 인자 ~ 일곱번째 인자 : x , y , width , height 입니다. x,y는 시작할 값이며 width , height는 넓이, 높이 입니다.
//!< 여덞번째 인자 : 부모 윈도우를 설정해 줍니다. 부모 윈도우가 없을 경우에는 NULL 로 지정합니다.
//!< 아홉번째 인자 : 윈도우에서 사용할 메뉴 핸들을 지정합니다. 윈도우 클래스 에서도 메뉴를 지정하는 멤버가 있는데 윈도우 클래스의 메뉴는 그 윈도우 클래스를 기반으로 하는 모든 윈도우에서 사용하는 반면 이 인수로 지정된 메뉴는 현재 CreateWindow 함수로 만들어지는 윈도우 에서만 사용됩니다. 모든 윈도우 클래스에서 지정한 메뉴를 그대로 사용하길 원하시면 이 인수를 NULL로 설정하시면 됩니다. 다른 메뉴를 사용하려면 이 인수를 바꿔 주시면 됩니다.
//!< 열번째 인자 : 윈도우를 만드는 주체입니다. 프로그램의 핸들을 지정해 주시면 됩니다.
//!< 열한번째 인자 : CREATESTRUCT 라는 구조체의 번지입니다. 특수한 목적에 사용되고 보통은 NULL 값을 선호합니다.
//!< 이렇게 사용할 값을 hWnd에 넘겨주고 넘겨받은 hWnd(윈도우 핸들)은 지역변수로 저장을 하게 됩니다. 윈도우가 메모리상에 불러졌고 이제 그 윈도우를 보여주어야 합니다.
     ShowWindow(hWnd,nCmdShow);                                   //!< 보여지는 윈도우를 설정합니다. 메모리에 있는 핸들을 넣고, 보여지는 옵션을 넣어줍니다.
//!< hWnd 핸들의 인수에는 여러가지가 들어갑니다.
//!< SW_HIDE 윈도우를 숨깁니다.
//!< SW_MINIMIZE : 윈도우를 최소화 시키고 활성화하지 않습니다.
//!< SW_RESTORE: 윈도우를 활성화 시킵니다.
//!< SW_SHOW : 윈도우를 활성화 시켜 보여줍니다.
//!< SW_SHOWNORMAL : 윈도우를 활성화 시켜 보여줍니다.
//!< 그리고 nCmdShow는 그저 기본적으로 함수에서 제공하는 것으로 넘겨주면 됩니다.
      
     //!< 메시지 루프 입니다. 
     //!< 함수의 원형은 GetMessage(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax) 입니다.
     //!< 메시지큐에서 읽어들인 메세지가 WM_QUIT면 false 리턴 나머지는 true 리턴
     //!< lpMsg : 메시지의 주소값을 넘겨주시면 됩니다. 
     //!< hWnd  : 윈도우 핸들값을 넘겨주시면 됩니다. 0이면 모든 윈도우의 메시지를 가져옵니다. 핸들 값을 지정하면 그 핸들 값에 포함된 메시지만 가져옵니다.
     //!< wMsgFilterMin , wMsgFilterMax 메시지를 읽어들일 범위 최소값 최대값 입니다. 사용할 경우 시스템이 무한루프에 빠질 수도 있습니다.
     //!< GetMessage 는 시스템이 유지하는 메시지 큐에서 메시지를 읽어 들입니다.
     //!< 읽은 메시지는 첫번째 인수가 지정하는 MSG 구조체에 사용됩니다.
     //!< 메시지가 WM_QUIT일 경우 False를 리넡합니다. 그외에 메시지는 True를 리턴합니다.
     //!< 나머지 세개의 인수는 메시지의 범위를 지정하는데 사용합니다.
     while(GetMessage(&Message,NULL,0,0))
     {
          TranslateMessage(&Message);   //!< 키보드 이벤트를 가공합니다. 키보드의 어떤 키가 눌려졌거나 떨어졌을 경우 키보드 메시지를 발생시킵니다.
          DispatchMessage(&Message);    //!< 메시지를 가지고 WndProc 을 호출합니다. 이 함수에 의해서 메시지가 프로그램으로 전달됩니다. 실제 처리는 하지 않습니다.
     }

     return (int)Message.wParam;        //!< wParam은 값을 넘기는데 사용합니다. 즉 Message 의 값을 리턴합니다. 메시지 루프가 종료되면 반환합니다. 이 값은 WM_QUIT 메시지로부터 전달된 탈출 코드 입니다.
}
 
//!< LRESULT는 long형 정수 입니다.
//!< Win32 환경에서 메시지 처리를 마친후 운영체제에게 신호를 주기위해 사용하는 값입니다.
//!< 즉 콜백 함수에서 처리 값을 -1, 0, 1 혹은 비트 플래그로 운영체제에게 어떤 작업을 해야하는지 추가로 알려줍니다.
//!< 0을 리턴하면 모든 것들이 처리 되었으니 처리할 것들이(메시지들) 없다. 라는 뜻 또는 진행중인 작업 처리 취소 명령을 가집니다.

//!< 메시지 처리 함수인 WndProc은 이러한 일을 합니다
//!< 메시지가 발생할 때 프로그램의 반응을 처리합니다.
//!< WndProc는 윈도우 프록시저 (Window Procedure) 라는 뜻 입니다.
//!< WinMain내의 메시지 루프는 메시지를 메시지 처리 함수로 보내주기만 합니다. WinMain에 의해 호출되는 것이 아니라 윈도우즈에 의해 호출됩니다.
//!< 운영체제에 의해 호출되는 응용 프로그램 내의 함수를 콜백 함수라고 합니다.
//!< 인수 4개는 MSG 구조체의 멤버 4개와 동일합니다.
//!< HWND : 메시지를 받는 핸들입니다.
//!< UINT : 메시지의 종류 입니다.
//!< WPARAM, LPARAM : 메시지에 따라 부가적인 정보들을 전달합니다.
LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM IParam)
{
     switch(iMessage)
     {
     case WM_DESTROY:
          //!< 메시지를 처리 했을 경우 반드시 0을 리턴해주어야 합니다.
          PostQuitMessage(0);
      return 0;
     }

     //!< DefWindowProc 함수는 WndProc에 처리하지 않은 나머지 메시지에 관한 처리를 합니다.
     //!< 윈도우 크기 변경이라던지, 시스템 메뉴 더블클릭 이라던지..
     return(DefWindowProc(hWnd,iMessage,wParam,IParam)); 
}


이 글의 주석만 다 이해하셔도 API의 반을 하신겁니다.

API 그렇게 많은 것이 변하지 않았습니다. 알고리즘이나 그런것들은 적용만 시키면 끝납니다.


다음장에서는 좀 더 내부적인 구조에 대해서 알아보도록 하겠습니다.

댓글

VallistA

병특이 끝나서 게임에서 웹으로 스위칭한 프로그래머.
프로그래밍 정보등을 공유합니다.
현재는 이 블로그를 운영하지 않습니다.
vallista.kr 로 와주시면 감사하겠습니다!

자고 싶습니다. ㅠㅠ

Github      :: 링크

궁금한점 문의 주시면 답변드리도록 하겠습니다

VISITED

Today :

Total :

SNS

  • 페이스북아이콘
  • 카카오톡아이콘
  • 트위터아이콘

Lately Post

Lately Comment