ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 프로젝트 생성 및 윈도우 창 띄우기
    Win API/1. 프로젝트 생성하기 2020. 9. 30. 08:05

    Visual Studio 2019를 실행시킨다.

    그리고 새 프로젝트 만들기를 선택한다.

     

    Windows 데스크톱 마법사를 선택한다.

     

    적당한 프로젝트 이름을 정하고 만들기를 선택한다.

     

    애플리케이션 종류는 데스크톱 애플리케이션을 선택하고, 추가 옵션은 빈 프로젝트를 선택한다.

    여기서 빈 프로젝트가 아닌 미리 컴파일된 헤더를 선택해서 프로젝트를 만들어도 된다.

    만약, 미리 컴파일된 헤더를 선택해서 만들었다면 프로젝트 명과 동일한 cpp파일이 있다.

    이 cpp파일의 내용을 지우고 아래의 내용을 복사해서 만들어도 된다.

    다만 그대로 컴파일을 하면 아래와 같은 컴파일 에러가 발생한다.

    이는 다음과 같이 인클루드를 해주면 된다.

     

     

    소스 파일에서 cpp파일을 하나 생성한다.

     

     

    Winmain.cpp 파일에 다음의 내용을 복사해서 붙여넣자.

    #include <windows.h>
    #include <tchar.h>
    
    /*
    	WndProc 함수는 메시지 처리 함수로, 모든 메시지를 처리한다.
    		매개변수에 대한 설명
    			1. hWnd		:	'누가'에 해당.
    			2. uMsg		:	'메시지 제목'에 해당.
    			3. wParam	:	'내용1'에 해당.
    			4. lParam	:	'내용2'에 해당.
    */
    LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInst, LPSTR strCmdLine, int nShowCmd)
    {
    	WNDCLASSEX wcex;		//	윈도우 정보 구조체
    	memset(&wcex, 0, sizeof(wcex));	//	초기화
    
    	/*	WNDCLASSEX에 필요한 값을 설정하는 부분 시작	*/
    	wcex.cbSize = sizeof(wcex);		// 윈도우 구조체 크기
    	wcex.style = CS_HREDRAW | CS_VREDRAW;	// 윈도우 스타일
    	wcex.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);	//	클라이언트 색상 변경 (클라이언트 배경색)
    	wcex.hCursor = LoadCursor(NULL, IDC_ARROW);					//	마우스 커서 변경 IDC_WAIT는 모래시계모양의 커서이다.
    	wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION);				//	아이콘 모양
    	wcex.hInstance = hInstance;			//	프로그램 번호
    	wcex.cbClsExtra = 0;				//	윈도우즈 예역 영역
    	wcex.cbWndExtra = 0;				//	윈도우즈 예역 영역
    	wcex.hIconSm = NULL;				//	작은 아이콘
    	wcex.lpszMenuName = NULL;			//	메뉴, 리소스 에디터로 제작.
    	wcex.lpfnWndProc = WndProc;			//	윈도우의 메시지 처리 함수를 지정한다.
    													
    	wcex.lpszClassName = L"MyWindow";	//	위 설정의 윈도우 클래스 이름
    	RegisterClassEx(&wcex);				//	RegisterClassEx로, OS에 등록(윈도우에 등록).
    	/*	WNDCLASSEX에 필요한 값을 설정하는 부분 끝	*/
    
    	//	내가 등록했던 클래스로 윈도우를 생성하는 함수가 CreateWindow 함수이다.
    	HWND hWnd = CreateWindow(L"MyWindow", L"MyWindow", WS_OVERLAPPEDWINDOW,		// 캡션 변경, 윈도우 스타일 변경 가능
    		CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);	// 윈도우 크기, 윈도우의 위치 정의 할수있다.
    
    	if (hWnd == NULL)
    	{
    		MessageBox(NULL, L"윈도우 생성 실패", L"에러", MB_ICONERROR | MB_OK);
    		return -1;
    	}
    
    	ShowWindow(hWnd, SW_SHOW);
    
    	MSG msg;
    	memset(&msg, 0, sizeof(msg));
    
    	while (GetMessage(&msg, NULL, 0, 0))
    	{
    		TranslateMessage(&msg);
    		DispatchMessage(&msg);
    	}
    	return 0;
    }
    
    /*
    	WndProc 함수는 메시지 처리 함수로, 모든 메시지를 처리한다.
    		매개변수에 대한 설명
    			1. hWnd		:	'누가'에 해당.
    			2. uMsg		:	'메시지 제목'에 해당.
    			3. wParam	:	'내용1'에 해당.
    			4. lParam	:	'내용2'에 해당.
    */
    LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
    	//	조사
    	switch (uMsg)
    	{
    	case WM_DESTROY:	//	윈도우 창이 파괴된 메시지.
    	{
    		PostQuitMessage(0);	//	PostQuitMessage함수에서 WM_QUIT가 발생함.
    		break;
    	}
    	}
    	//	DefWindowProc함수는 윈도우 보고용 함수.
    	return DefWindowProc(hWnd, uMsg, wParam, lParam);
    }

     

     

    소스 파일1

    Winmain.cpp
    0.00MB

     

     

    소스 파일2

    아래의 소스 파일은 Visual Studio 2008에서는 문제 없이 실행되는 파일이다.

    WinMain.cpp
    0.00MB

     

     

    윈도우 클래스를 정의하고 등록하는 부분에 대해 알아보자.

    아래의 부분을 보자.

     

     

    CreateWindow 함수에 대해 알아보자.

    WinMain은 메인 윈도우를 만들고, 화면에 윈도우를 표시하기만 할뿐, 대부분의 일은 WndProc에서 이루어진다.
    WinMain함수에서 하는 가장 중요한 일은 메인 윈도우를 만드는 일이다.

    윈도우 클래스를 등록한 후에는 이 윈도우 클래스를 기본으로 실제 윈도우를 생성한다.

    모든 윈도우는 윈도우 클래스의 정보를 기반으로 하여 만들어진다.

    윈도우 클래스는 만들어질 윈도우의 여러 가지 특성을 정의하는 구조체이다.

    윈도우를 생성할 때는 CreateWindow함수를 사용한다. 

    CreateWindow함수의 반환형은 HWND이다.

    CreateWindow함수는 윈도우에 관한 모든 정보를 메모리에 만든 후,

    윈도우를 대표하는 번호인 윈도우 핸들을 리턴한다. 

    넘겨지는 윈도우 핸들은 hWnd라는 변수에 저장되었다가 이 윈도우를 참조하는 모든 함수의 인수로 사용된다. 

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

    WinMain 함수의 매개변수 중 hInstance이외에는 잘 사용되지 않는다.

    CreateWindow함수의 매개변수를 하나하나 설명하면
    첫 번째 L"MyWindow"는 생성하고자 하는 클래스. 두 번째 L"MyWindow"는 윈도우의 캡션
    세 번째 WS_OVERLAPPEDWINDOW는 윈도우 형태를 결정함.

    이 매개변수의 정의로 이동해서 윈도우 스타일 변경할 수있다.
    네 번째 CW_USEDEFAULT와 다섯 번째 CW_USEDEFAULT는 각 각, 윈도우 창의 x와 y위치를 결정.
    여섯 번째 CW_USEDEFAULT와 일곱 번째 CW_USEDEFAULT는 윈도우 창의 가로와 세로의 길이를 결정함.

    여덟번째 매개변수는 부모 윈도우가 있을 경우, 부모 윈도우의 핸들을 지정한다.

    MDI프로그램이나 팝업 윈도우는 윈도우끼리 수직적인 상하관계를 가져 부자관계(Parent-Child)가 성립되는데,

    이 관계를 지정하는 인수이다.

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

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

    아홉번째 매개변수는 윈도우에서 사용할 메뉴의 핸들을 지정한다.

    윈도우 클래스에서 지정한 메뉴를 그대로 사용하려면 이 인수를 NULL로 지정하며, 다른 메뉴를 사용하려면

    이 인수에 원하는 메뉴 핸들을 지정한다.

    열번째 매개변수는 윈도우를 만드는 주체. 즉, 프로그램의 핸들을 지정한다.

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

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

    자동으로 파괴한다.

    마지막 매개변수는 여러개의 윈도우를 만들 때, 각 윈도우에 고유의 파라미터를 전달하는 특수한 목적에 사용하는데,

    보통은 NULL값을 사용한다.

    hInstance라는 말은 실행중인 프로그램 하나를 칭하는 용어이다.

    윈도우즈는 여러 개의 프로그램이 동시에 실행되는 멀티 태스킹 시스템일 뿐만 아니라,

    하나의 프로그램이 여러 번 실행될 수 있다.
    이 때 실행되고 있는 각 각의 프로그램을 프로그램 인스턴스라고 하며, 이를 간단히 줄여서 인스턴스라 한다.
    메모장이 두 번 실행되었다고 가정하면, 두 프로그램 모두 메모장이지만, OS는 각 각 다른 메모리를 사용하는 
    다른 프로그램으로 인식한다.

    각 메모장은 서로 다른 인스턴스 핸들을 가짐. OS는 이 인스턴스 핸들값으로 두 개의 메모장을 서로 구별함.

    hInstance란 프로그램 자체를 일컫는 정수값이며, 프로그램내부에서 자기자신을 가리키는 1인칭 대명사이다.

     

     

    ShowWindow 함수에 대해 알아보자.

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

    위의 함수의 원형은 아래와 같다.

    BOOL ShowWindow( hWnd, nCmdShow );

    위 함수의 매개변수에 대해 알아보자.

    첫 번째 매개변수 hWnd는 화면으로 출력하고자 하는 윈도우의 핸들이며,
    CreateWindow함수가 리턴한 핸들을 그대로 넘기면 된다.

    이 프로그램 소스에서 CreateWindow함수가 리턴한 핸들은 hWnd이므로,

    ShowWindow함수의 첫 번째 매개변수는 hWnd가 된다.

    두 번째 매개변수 nCmdShow는 윈도우를 화면에 출력하는 방법을 지정한다.

    SW_SHOW의 의미는 윈도우를 활성화하여 보여준다는 의미이다.

     

    메시지 루프의 내용을 알아보자.

    MSG구조체는 메시지에 대한 정보를 정의 한다.

    아래의 while 루프 문장이 메시지 루프이다.

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

    즉, 어떤 메시지가 발생했는가에 따라 실행순서가 달라진다.

    윈도우즈 프로그램에서 메시지를 처리하는 부분을 메시지 루프라 한다.

    보통 WinMain함수의 끝에 위의 형식으로 존재한다. 메인윈도우를 만든 직후 WinMain은 메시지 루프를 실행한다.

    위의 while문을 해석해보면.
    WM_QUIT가 아닐 때까지 GetMessage함수로 메시지를 가져 온다.
    메시지 처리 함수에서는 메시지를 확인하여 해당되는 상황이 있다면, 메시지를 처리한다.

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

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

    이 GetMessage함수는 읽어들인 메시지가 프로그램을 종료하라는 

    WM_QUIT일 경우, FALSE를 리턴. 그 외의 메시지이면 TRUE를 리턴한다.

    따라서, WM_QUIT메시지가 읽혀질 때까지, 즉, 프로그램이 종료될 때까지

    전체 while 루프가 계속 반복된다. 나머지 3개의 매개변수는 읽어 들일 메시지의 범위를 지정한다.

    TranslateMessage는 리턴형이 BOOL이며, 키보드 입력 메시지를 가공하여 프로그램에서 쉽게 쓸 수 있도록 한다.

    키보드의 어떤 키가 눌려지거나, 떨어졌을 때 키보드 메시지를 발생시키는데, 이 TranslateMessage함수는 

    키보드의 눌림(WM_KEYDOWN)메시지가 발생할 때 문자가 입력되었다는 메시지(WM_CHAR)를 만드는 역할을 함.

    예를 들어, A키가 눌리면 A문자가 입력되었다는 메시지를 만들어 낸다.

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

    DispatchMessage함수에 의해 메시지가 윈도우로 전달되며, 윈도우 프로시저에서는 전달된 메시지를 점검하여

    다음 동작을 결정한다.

    이 함수가 메시지를 전달하면, 다시 루프의 선두로 돌아가 다음 메시지를 기다린다.

    메시지 루프가 하는 일이란 메시지 큐에서 메시지를 꺼내 메시지 처리 함수로 보내는 것 뿐이다.

    실제 메시지 처리는 별도의 메시지 처리 함수에서 수행한다.

    메시지 루프 과정은 WM_QUIT 메시지가 전달될 때까지, 즉, 프로그램이 종료 될 때까지 반복된다.

     DispatchMessage함수의 리턴형은 LONG형이다.

    GetMessage함수는 읽은 메시지를 MSG형의 구조체에 대입하며,

    이 구조체는 DispatchMessage함수에 의해 응용 프로그램의 메시지 처리 함수로 전달된다.

    LRESULT자료형은 long이라고 봐도 된다.

     

     

    WndProc 함수( 메시지 처리 함수 )에 대해 알아보자.

    첫 번째 매개변수는 메시지를 받을 윈도우의 핸들이며,

    두 번째 매개변수는 메시지의 종류(어떤 변화가 발생했는가에 대한 정보) 이다.

    메시지 처리 함수란, 메시지가 발생할 때 프로그램의 반응을 처리하는 일을 하며, WinMain함수와는

    별도로 WndProc이라는 이름으로 존재한다.   

    WndProc은 WinMain에서 호출되는 것이 아니라 운영체제에 의해 호출 된다.

    WndProc함수는 사용자와 시스템이 보내오는 메시지를 처리하는 아주 중요한 일을 한다.

    윈도우즈에서는 아주 특별한 경우를 제외하고는 WinMain함수와 WndProc함수가 모두 있어야 한다.

    WM_DESTROY 메시지는 종료처리를 하기에 적합하다. WM_DESTROY메시지는 윈도우의 일생을 통틀어 

    딱 한번만 전달되는 특성이 있어서 일회적인 작업인 종료 처리에 사용할 수 있다.

    WM_DESTROY메시지는 메인 윈도우가 파괴되기 직전에 보내지는데, 

    메인 윈도우 파괴 후에는 메시지 루프가 끝나므로 메시지 루프 다음에 종료 처리 코드를 작성할 수 있다.

    WM_DESTROY는 특정한 윈도우에 관련된 종료처리를 하는데 사용하는 것이 좋다.

    WM_DESTROY에서 주의해야 할 것은 메인 윈도우의 경우 반드시 PostQuitMessage함수를 호출해야 한다.

    메인 윈도우는 응용 프로그램이 처음 만드는 윈도우인데,

    자신이 파괴될 때 응용 프로그램을 종료해야 하는 막중한 임무가 부여되어 있다.

    PostQuitMessage함수는 메시지 큐에 WM_QUIT를 넣음으로써 WinMain에 있는 메시지 루프를 종료시키고,

    WinMain함수 자체를 종료하여, 운영체제로 복귀시키는 역할을 한다.

    메인 윈도우는 응용 프로그램을 종료해야 할 의무가 있으므로, 

    다른 메시지는 DefWindowProc으로 넘기더라도 최소한 WM_DESTROY메시지는 처리해야 한다.

    WndProc은 메시지를 무사히 처리했으면 0을 리턴하도록 약속한다.

    위의 WndProc함수는 WM_DESTROY 메시지만을 처리하고 있으며,

    나머지 메시지에 대해서는 DefWindowProc함수에 맡긴다.

    WM_DESTROY메시지는 사용자가 시스템 메뉴를 더블클릭하거나 Alt+F4를 눌러 프로그램을 끝내려고 할 때

    발생하는 메시지이다.

    WM_DESTROY메시지가 발생하면 PostQuitMessage함수를 호출하여 WM_QUIT 메시지를 보낸다.

    WM_QUIT메시지가 입력되면, 메시지 루프의 GetMessage함수 리턴값이 FALSE가 되어 while루프를 빠져나오며,

    WinMain이 종료된다.

    DefWindowProc함수가 메시지를 처리했을 경우, 이 함수가 리턴한 값을 WndProc함수가 다시 리턴해야 한다.

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

    예를 들어, 시스템 메뉴를 더블클릭하면 프로그램이 종료되는데 이런 처리는 별도로 하지 않아도 

    DefWindowProc함수에서 알아서 한다.

    그래서, 윈도우의 이동이나 크기 변경 따위의 처리는 프로그램이 직접할 필요없이 DefWindowProc함수로

    넘기기만 하면 된다.

     

     

    PeekMessage 함수와 GetMessage 함수를 비교 해보자.

    GetMessage 함수의 리턴형은 처리결과를 의미하며, 이는 메시지가 없다면 리턴도 없다.

    GetMessage 함수의 매개변수는 다음과 같다.

    (1). 빈 메시지 구조체

    (2). 가져올 장소

    (3). 작은 값

    (4). 큰 값

    그리고 아래의 차이가 존재한다.

    PeekMessage는 게임에 적합하다.

    GetMessage는 메시지가 없다면 while문을 안 돌기 때문에 문서작성에 적합하다. 

     

     

    메시지 루프와 WndProc의 전체적인 구동방식

    GetMessage함수를 이용해서 메시지를 꺼낸다.

    꺼낸 메시지가 WM_QUIT이라면, 종료를 하고, 아니라면 TranslateMessage함수로 키보드를 해석한다.

    DispatchMessage함수를 이용해서 메시지를 WndProc으로 보낸다.

    WndProc함수에서 uMsg라는 메시지의 종류에 따라, 해당 메시지를 처리한다.

     

     

    윈도우를 만드는 과정을 요약하자면 아래와 같다.

    아래의 과정은 거의 정형화되어있고, WinMain함수에서 반드시 해야할 과정이다.

    1. WNDCLASSEX라는 윈도우 클래스를 정의( 윈도우의 기반이 되는 클래스를 정의.

                                                        즉, 만들고자하는 윈도우의 속성을정의 한다.)

    2. RegisterClassEx라는 RegisterClass를 이용. ( 윈도우 클래스를 등록한다. )

    3. CreateWindow함수를 만든다.( 메모리 상에 윈도우를 생성. )

    4. ShowWindow함수를 만든다. ( 윈도우를 화면에 표시한다. )

    5. 메시지 루프( 사용자로부터의 메시지를 처리한다. )

     

     

    그리고 우리가 만든 윈도우를 보면, 정말 아무 것도 없다.

    이 윈도우를 약간만 꾸며 보도록 하자.

    1. 클라이언트 배경색을 변경해보기

    WNDCLASSEX에 필요한 값을 설정하는 부분 내에 다음과 같은 문장이 있다.

    GetStockObject함수의 매개변수에 WHITE가 있음을 알고 있다. 위의 매개변수를 다른 색으로 바꾸면 된다.

    아래와 같이 변경해보자.

    2. 마우스 커서의 모양을 변경해보자.

    WNDCLASSEX에 필요한 값을 설정하는 부분 내에 다음과 같은 문장이 있다.

    위의 LoadCursor의 두 번째 매개변수를 다음과 같이 변경해보자.

    위와 같이 IDC_WAIT 와 IDC_CROSS를 적용해보자.

     

    3. 윈도우 창의 위치, 크기, 캡션, 윈도우 스타일을 변경해보자.

    캡션은 윈도우창 의 좌측상단의 이름이다. 

    이는 CreateWindow함수에서 적절하게 수정하면 된다.

    먼저, 윈도우 창의 위치와 크기를 아래와 같이 두자.

    그리고, CreateWindow함수를 보자.

    이 부분을 아래와 같이 변경해보자.

     

    댓글

Designed by Tistory.