본문으로 바로가기

20151112 - WinAPI 기본형 배우기

category 마이 스토리/내용 정리중인 글들.. 2015. 11. 12. 21:57


남수진 씨 20151112 winapi 첫 수업


  • 앞으로의 기본형 소스
  • 기본형을 쓰라면 이 소스를 작성한다

  • 헤더파일은 windows.h 하나만
  • LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
    • LRESULT: long 형 반환값
    • CALLBACK
      • 컴파일 후 사라진다
        • 어셈블리 단계에서는 의미가 없다
        • 이걸 지워도 동작함
      • 운영체제가 호출하는 함수
        • 프로그램 내부에서는 호출되지 않음
      • 함수 call 방식을 결정(cdecl이나 stdcall)
    • HWND
      • 윈도우 핸들러(int 형 정수)
    • WPARAM
      • unsigned int 형(4byte)
    • LPARAM
      • long 형(4byte)
        • int가 16bit이던 시절에 32bit로 쓰다가 32비트로 넘어와서 int가 4byte가 되어서도 그대로 유지
  • HINSTANCE g_hInst;
    • HINSTANCE: 자료형의 일종
  • LPSTR lpszClass="First";
    • LPSTR
      • char *
    • lpsz
      • null로 끝나는 문자열의 포인터라는 뜻
      • lp : long pointer
      • s : string
      • z : zero
  • int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance ,LPSTR lpszCmdParam,int nCmdShow)
    • APIENTRY
      • 이 소스의 entry point
      • 컴파일할 때 없어짐
      • 함수의 call 방식을 define하고 컴파일러에게 알려줌
    • hInstance
    • hPrevInstance
      • 안쓰지만 옛날에 썼던게 남아있는거
    • lpszCmdParam
      • C 프로그램에서 argv랑 같은 기능
    • nCmdShow
      • 창을 화면에 보일 것인가 결정
  • HWND hWnd;
    • 윈도우 핸들러
  • MSG Message;
  • WNDCLASS WndClass;
    • 생성하는 윈도우의 정보를 가지고 있는 구조체
  • g_hInst=hInstance;
    • 다른 함수들이 hInstance에 접근할 수 있게 전역변수 g_hInst에 저장
  • WndClass : 윈도우 정보를 초기화
    • hbrBackground
      • 윈도우 창의 색깔
    • hCursor
      • 마우스 모양
    • hIcon
      • 윈도우 창 좌측 상단의 아이콘
    • hInstance
      • 윈도우의 첫번째 인자를 세팅한다
    • lpfnWndProc
      • lpfn: 함수 포인터
      • 함수의 주소를 넣는다
      • 운영체제는 어플리케이션에 마우스의 위치 정보 등을 전한다
        • 이를 메시지라 한다
        • 해당 어플리케이션을 클릭하거나 마우스를 그 위로 지나갈 때 어플리케이션에 메시지를 전송
          • 어플리케이션이 해당 동작에 맞는 기능을 수행하도록 한다
          • 오토 마우스같은 경우에는 윈도우 디바이스 드라이버를 설치하여 구현
      • lpfnWndProc은 운영체제에서 날아오는 메시지를 처리할 함수를 설정한다
        • 함수 이름은 WndProc 이외에 다른 것으로 해도 된다
    • lpszClassName
      • 실행 중인 윈도우 창의 소속을 구분하기위해 설정
      • 예를 들어 메모장에서 파생된 찾기, 다른이름으로 저장 창은 모두 메모장과 같은 lpszClassName을 가진다
    • lpszMenu
      • 메뉴 창
    • style
  • RegisterClass(&WndClass);
    • WndClass 구조체의 정보대로 윈도우 창을 만들기위해 윈도우 운영체제에 등록하는 함수
    • 이 이후로 WndClass는 쓸모가 없다
  • hWnd=CreateWindow(   lpszClass,
                            lpszClass,                    - 상단에 표시되는 창의 이름
                            WS_OVERLAPPEDWINDOW,   - 윈도우 모양을 좌우한다(soen의 API 초급강좌 2-3-마 윈도우 스타일에서 확인할 수 있다)
                            CW_USEDEFAULT,            
    - 창의 가로, 세로 크기를 결정
                            DW_USEDEFAULT,
                            CW_USEDEFAULT,
                            NULL,
                            (HMENU)NULL,
                            hInstance,
                            NULL);
    • 나중에 실습하면서 다시 볼거임
  • ShowWindow(hWnd,nCmdShow);
    • 창이 화면에 나타날지를 결정(2번째 인자 nCmdShow가 결정)
    • nCmdShow : WinMain의 4번째 인자, 창의 표시를 결정


  • while문
    • GetMessage(&Message,0,0,0)
      • 윈도우가 보내는 메시지를 받는 함수
      • 메시지 큐에서 있는 메시지 중 가장 오래된 메시지를 가져와서 Message 변수에 저장한다.
        • 마우스의 움직임, 화면의 움직임 등 모든 정보를 처리하기위해 윈도우에서 많은 양의 메시지를 보낸다
        • 윈도우가 보내는 메시지는 메시지 큐에 저장되는데 이것은 원형 큐 형태를 가지고 있다
          • 윈도우가 보내는 메시지가 어플이 저장하고 있는 최대 메시지양보다 더 많이 들어오면, 어플이 보관하고 있는 가장 오래된 메시지는 삭제된다
      • 창을 종료시키면 0을 반환하고 프로그램이 종료된다
    • TranslateMessage(&Message);
      • 키 입력을 해석
      • 예) 키보드 입력 신호에서 A,a,ㅁ 구분 
    • DispatchMessage(&Message);
      • 윈도우가 처리하고 남은 쓰레기를 넘긴다
      • 윈도우에 처리 상황을 보고 하면 윈도우가 WndProc을 호출
  • return Message.wParam;
    • 리턴값이 윈도우 로그에 남는다


  • LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
    • 어떤 일을 할지를 정하는 함수
    • WinMain이 껍데기고 이 함수가 핵심
    • iMessage
      • 이벤트 정보(메시지 번호)
      • 이 값에 따라 수행할 작업을 결정한다
    • WM_DESTROY
      • WM: Window Message의 약자
      • 창을 종료시킬 때 발생하는 메시지
        • 창을 닫기 전에 어떤 일을 처리하기 위해서 종료신호도 메시지로 처리
      • PostQuitMessage(0)
        • 윈도우 메시지 큐에 0을 대입한다
          • GetMessage에서 0이 반환됨
            • WinMain의 while문이 종료되며 프로그램 종료

    • First In First Out 개념의 자료 구조



  • 기본 소스로 예제 소스 컴파일
    • Visual Studio 2013에서 WIin32 프로젝트를 생성 후, 위의 소스를 붙여 Ctrl+F5로 컴파일
    • 기본창이 실행되는 것을 확인한다


2013:09:25 21:59:50

2013:09:25 21:59:50


2013:09:25 21:59:5










□ Win32 API



- CmdParam : 인자들이 파라미터로 인해 인자들이 받아들일수 있는 역할을 한다.


- CmdShow : 참의 내용을 확인하여 보여준다. (프로그램 짠 사람 마음... 보여주거나 안보여주거나..)


- WNDCLASS : 구조체의 형태 (찾아보면 struct 로 표기되어 있음)


- 전역변수에 값을 넣으면 다른 프로그램에게 공유하겠다는 의미를 가지고 있다. (전역변수에 집어넣지 않으면 다른 프로그램은 알 수가 없다.)


- 현재 나와있는 WndClass 변수에다가 값을 집어넣는데, 집어넣지 않으면 쓰레기값이 나오기 때문에 초기화 해주고 있다.


- IDI_APPLICATION : 예를 들어 메모장을 열었을때 제일 왼쪽 상단 위에 있는 아이콘 표시를 의미한다.


- hInstance : 전역변수에도 적어두고 메인함수 내부에도 적어두어야한다.


- lpfn : Long Pointer Function (함수 주소를 넣는 자리)


- 예를 들어 그림판과 메모장을 동시에 띄워서 마우스를 메모장에 올려두고 우선적으로 찍힌 메모장에 마우스 커서를 그림판으로 갖다 대면 마우스가 그림판에 있다는것을 인식한다. 이것은 운영체제가 창에 대해서 메세지를 보내는 것을 의미한다. (WndProc 을 써 뒀지만 반드시 WndProc을 쓸 필요는 없다. 변수는 바꿀수 있기 때문...)


- 우리는 창에 대해서 클릭을 하는것으로 착각을 하고 있으며, 현실적으로는 OS (운영체제)가 해당 창에 대해서 메세지를 보낸다.


- 실행 중인 내용의 한 덩어리를 Class라고한다. 창에 대한 Class 를 First 라고 해두었다.


- style : 윈도우 가로 세로가 조절이 가능하다(?)


- HREDRAW , VREDRAW : 가로 크기 세로 크기의 형태


- RegisterClass : 창의 정보를 구조체로 입력한다. 그리고 변수를 선언하는 변수는 아무것도 하지 않는다. (구조체는 등록하고 나면 쓸모가 없음.. main 함수가 끝날때까지 없어지지 않는다). 이 구조체가 살아있는 이유는 Register 함수까지 가기 위해서 살아 있는 것이다.


- CreateWindow : (인자순서) -> 클래스 이름 , WS_OVERLAPPEDWINDOW, 가로 값, 세로 값 등을 나타낸 시작 좌표값 CW (1,2번째), 가로의 크기와 세로의 크기의 CW (3,4번째) [좌표는 항상 4개가 필요 원을 그릴때도 마찬가지],NULL(?), Menu 값  


-WS_OVERLAPPEDWINDOW를 나타낸 표 


- ShowWindow : 창을 보여줄건지 안보여줄건지 결정한다. 만약 보여주지 않으려면 USEDEFAULT를 넣으면 된다. (창을 숨길때만 사용하지 의도적으로 숨길 이유는 없다.)


- GetMessage : 해당 창에다가 메세지를 보내는 역할을 한다. (초고속으로 window 가 창에게 엄청나게 많은 양의 message를 보내고 있다.)


- 일반프로세서는 메세지를 계속 보내다가 처리를 못하게 되면 앞전에 있던 일은 버려진다. 이것을 메세지 원형큐 라고 한다. (큐는 First In First Out 구조)


- GetMessage는 원형큐에서 가장 마지막에 저장한 내용을 처리한다.


&Message => 전달된 메시지를 놔두는 위치가 된다 (즉, 창)


- 0,0,0 =>윈도우 창에서 x를 의미 한다. x를 클릭하면 해당 OS에 Message.wParam 을 반환한다. (정상 종료인지 비정상 종료인지 확인을 해준다)


- TranslateMessage : Window 창에서 사용하는 키보드를 의미한다 (A를 지정했을때 A가 가지는 가지수가 많다)


- DispatchMessage : 쓰레기 값을 처리



- 이제 위의 내용을 Visual Studio 에서 코드를 붙여놓고 실행 시켜보면 다음과 같은 창이 실행 되어야 정상적으로 완료 된 것이다.



- 코드 부분에서 위의 이름이 한문으로 뜨게 되는데 전역변수 구간에서 First 구간 큰따옴표 왼쪽에 L을 넣게 되면 정상적으로 실행이 된다.




↑ 위와 같이 되는 이유는 내일... 











#include <windows.h>

LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
HINSTANCE g_hInst;
LPSTR lpszClass="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)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,0,0,0)) {
		TranslateMessage(&Message);
		DispatchMessage(&Message);
	}
	return 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));
}

이제 소스를 차근 차근히 분석해 보되 그 전에 우선 익숙해져 있는 도스에서와의 차이점을 알아보자.

4헤더 파일

우선 제일 첫행을 보면 windows.h 하나만 인클루드되어 있다. 

도스에서는 사용하는 함수에 따라 여러 개의 헤더 파일을 포함하지만 윈도우즈에서는 하나의 헤더 파일에 모든 API 함수들의 원형과 사용하는 상수들을 죄다 정의하고 있기 때문에 windows.h만 포함해 주면 된다. 

stdio.h나 conio.h, graphics.h 등을 포함해 줄 필요가 없다. 

물론 특별한 경우에는 해당하는 헤더 파일을 포함해야 하지만 예제 수준에서는 windows.h만 포함시키면 거의 문제가 없다. 

windows.h 헤더 파일은 기본적인 데이터 타입, 함수 원형 등을 정의하며 그 외 필요한 헤더 파일을 포함하고 있다. 

1그래서 윈도우즈 프로그램의 첫 줄은 거의 항상 #include <windows.h> 로 시작된다.





4 시작점

다음으로 차이나는 점은 프로그램의 시작점인 엔트리 포인트(Entry Point)가 main 함수가 아니라 WinMain이라는 점이다. 

윈도우즈 프로그램의 시작점은 main이 아닌 WinMain이다. 원형은 다음과 같다.

int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance ,LPSTR lpszCmdParam,int nCmdShow) 

도스에서의 main 함수는 인수 사용여부에 따라 여러 가지 원형이 있지만 WinMain의 원형은 위와 같이 고정되어 있다. APIENTRY 지정자는 __stdcall형 호출 규약을 사용한다는 뜻인데 일단은 없다고 생각해도 무방하다. 4개의 인수를 취하는데 각 인수의 의미는 다음과 같다.

인수의미
hInstance프로그램의 인스턴스 핸들
hPrevInstance바로 앞에 실행된 현재 프로그램의 인스턴스 핸들. 없을 경우는 NULL이 되며 WIN32에서는 항상 NULL이다. 호환성을 위해서만 존재하는 인수이므로 신경쓰지 않아도 된다.
lpCmdLine명령행으로 입력된 프로그램 인수이다. 도스의 argv인수에 해당한다.
nCmdShow프로그램이 실행될 형태이며 최소화, 보통모양 등이 전달된다.

이 중 hInstance 이외에는 잘 사용되지 않는다. 인스턴스(Instance)라는 말은 클래스가 메모리에 실제로 구현된 실체를 의미한다. 윈도우즈용 프로그램은 여러개의 프로그램이 동시에 실행되는 멀티태스킹 시스템일 뿐만 아니라 하나의 프로그램이 여러 번 실행될 수도 있다. 이때 실행되고 있는 각각의 프로그램을 프로그램 인스턴스라고 하며 간단히 줄여서 인스턴스라고 한다. 예를 들어 다음과 같이 메모장이 두번 실행되어 있다고 해 보자.

이 때 두 프로그램은 모두 메모장(Notepad.exe)이지만 운영체제는 각각 다른 메모리를 사용하는 다른 프로그램으로 인식한다. 이때 각 메모장은 서로 다른 인스턴스 핸들을 가지며 운영체제는 이 인스턴스 핸들값으로 두 개의 메모장을 서로 구별한다.

hInstance란 프로그램 자체를 일컫는 정수값이며 API 함수에서 수시로 사용된다. 그래서 이 예제에서는 WinMain의 인수로 전달된 hInstance값을 전역 변수 g_hInst에 대입해 두었다. hInstance 인수는 기억부류가 지역 변수이기 때문에 WinMain의 밖에서는 사용할 수 없기 때문이다. 이 예제에서는 g_hInst 값을 당장 사용하지 않고 있지만 앞으로의 예제에서는 이 값을 사용하게 될 것이다.

그리고 lpszClass라는 전역 문자열이 정의되어 있는데 이 문자열은 윈도우 클래스를 정의하는데 사용된다. 잠시 후에 이 문자열이 사용되는 곳에서 설명을 하도록 한다.

HINSTANCE g_hInst;
LPSTR lpszClass="First";






4 메시지 처리 함수

이 프로그램을 자세히 보면 두개의 함수만 있다. 하나는 프로그램의 시작점인 WinMain이며 나머지 하나는 WndProc이다. 도스에서는 main 함수만으로도 프로그램을 작성할 수 있지만 윈도우즈에서는 아주 특별한 경우를 제외하고는 이 두개의 함수가 모두 있어야 한다.

WinMain에서는 윈도우를 만들고 화면에 출력하기만 할 뿐이며 대부분의 일은 WndProc에서 이루어진다. WinMain은 프로그램을 시작시키기만 하며 실질적인 처리는 대부분 WndProc에서 이루어진다. WinMain의 모양은 대체로 일정하며 특별한 일을 하지 않지만 WndProc는 프로그램에 따라 천차만별로 달라진다. 그래서 소스를 분석할 때 주의깊게 봐야 할 부분은 WinMain이 아니라 WndProc이다. WinMain 바로 윗부분에 WndProc 함수의 원형이 선언되어 있다.

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