24/03/13
1화. Win32API 기본 (1)
OS (운영체제)도 일종의 프로그램으로 컴퓨터를 켰을때 제일 처음 메모리를 점유하는 프로그램은 운영체제이다.
프로젝트 생성 :
"Window Desktop Application" 으로 생성
프로젝트를 생성하면 기존의 C++ 프로젝트와 달리 처음부터 제공되는 코드들이 많다.
SAL :
"Source-Code Annotation Language" 의 줄임말로 "주석언어"를 의미한다.
<sal.h> 헤더에 정의되어 있다.
- _In_ : 호출된 함수에 전달되고 읽기 전용으로 취급한다.
- _Inout_ : 호출된 함수에 전달되고 (잠재적으로?)수정된다.
- _Out_ : 호출된 함수는 해당 공간에 데이터를 쓴다.
- _Outptr_ : 호출된 함수는 해당 공간에 포인타입 데이터를 쓴다.
HINSTANCE :
프로그램의 핸들
실행중인 프로그램이 메모리 영역에 할당된 공간의 시작주소
Windows 운영체제에서 실행중인 프로그램을 구분하는 ID값으로 사용한다.
데스크톱 애플리케이션의 메인함수인 wWinMain() 함수의 인자값으로 한번 들어오는 값을 저장해서 사용한다.
LoadStringW() :
hInstance에 해당하는 프로그램의 String Table에서 uID에 해당하는 값을 가져와 lpBuffer 에 대입해준다.
int LoadStringW(
_In_opt_ HINSTANCE hInstance,
_In_ UINT uID,
_Out_writes_to_(cchBufferMax,return + 1) LPWSTR lpBuffer,
_In_ int cchBufferMax
);
Resource View 는 상단 Tool Bar 에서 View->Other Windows -> Resource View 를 선택하면된다.
MyRegisterClass :
생성하고자 하는 윈도우의 정보를 설정해주고 등록한다.
내부에서 RegisterClassExW() 함수를 호출하는데 이는 Windows에서 제공하는 함수로 윈도우에 대한 정보를 등록하는 역할은 해당 함수가 수행한다.
InitInstance() :
등록된 윈도으 정보를 기반으로 윈도우를 생성해준다.
szWindowClass 를 키값으로 윈도우 정보를 찾는다.
2화. Win32API 기본 (2)
Message Loop :
메세지 루프문이 종료되면 프로그램이 종료된다.
Message Queue :
Queue는 처음 들어간 데이터가 제일 처음 빠져나오는 "FIFO(First-In First-Out)" 구조이다.
포커싱 되어 있는 프로그램의 Queue로 메세지가 발생한다.
ex) 그림판을 켜두고 Visual Studio 마우스 드래그를 한다고 그림판에 그림이 그려지진 않는다.
GetMessage() :
Queue에서 메세지를 꺼내 MSG 타입 변수에 넣어준다.
인자값으로 hWnd가 들어가는 것으로 보아 윈도우마다 Queue가 따로 존재함을 알 수 있다.
특징 :
- 메세지 큐에 메세지가 없으면 어떠한 것도 반환하지 않는다. 즉, 메세지가 들어올때까지 대기한다. (= 프로그램 정지)
- 메세지의 종류에따라 반환하는 bool 값이 다르다. 메세지 타입이 WM_QUIT 이면 false 를 반, 그 외에는 전부 true
#define GetMessage GetMessageW
BOOL GetMessageW(
_Out_ LPMSG lpMsg,
_In_opt_ HWND hWnd,
_In_ UINT wMsgFilterMin,
_In_ UINT wMsgFilterMax
);
MSG :
MSG.hWnd : 메세지가 발생한 window를 의미한다. 하나의 프로그램(Process)는 여러개의 윈도우를 만들 수 있다.
typedef struct tagMSG {
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
} MSG, *PMSG, NEAR *NPMSG, FAR *LPMSG;
메세지에 대한 처리는 Window가 소유하고 있는 "프로시저(처리기)" 함수가 해준다.
MyRegisterClass() 함수에서 생성하고자 하는 윈도우 정보 등록시 설정해준다. (lpfnWndProc)
프로시저 함수에서 정의하지 않은 메세지는 DefWindowProc() 함수가 처리해준다.
Rectangle() :
BOOL Rectangle(_In_ HDC hdc, _In_ int left, _In_ int top, _In_ int right, _In_ int bottom);
Windows 에서 제공해주는 사각형 그리는 함수
윈도우 핸들 그리고 좌 상단의 x,y 좌표, 우 하단의 x,y 좌표를 인자값으로 받는다.
3화. Win32API 핸들, DC, 윈도우 이벤트 (1)
무효화 영역 (Invalidate) :
Window가 화면에서 벗어나거나 최소화되어 화면에서 사라지면 발생한다.
무효화 영역이 발생하면 화면에 무효화 영역이 되었던 부분이 나타날시 다시 그려준다. (WM_PAINT 메세지 발생)
HDC :
DC는 그리기 작업을 수향하기 위해 필요한 도구(데이터)들의 집합체이다.
DC에는 PEN, BRUSH 가 설정되어 있는데 기본값을 각각 BLACK, WHITE 이다.
// HDC의 정의
DECLARE_HANDLE(HDC);
// DECLARE_HANDLE 의 정의
#define DECLARE_HANDLE(name)
struct name##__
{
int unused;
}; typedef struct name##__ *name
HDC타입은 정의를 확인해보면 멤버로 정수값 하나를 가지는 "HDC__" 구조체이다.
HDC 외에도 HWND, HPEN, HBRUSH 등은 커널 오브젝트로 불린다. 이는 운영체제에서 관리하는 객체를 의미한다.
"H"는 핸들의 약자이며 핸들은 주소값같은것이다. 즉, HDC는 커널오브젝트 DC의 주소값을 저장하는 타입이다.
HDC의 생성함수 BeginPaint() 를 보면 인자로 HWND를 받는다. 이는 DC를 생성하기 위해선 DC를 사용할 윈도우를 알아야함을 의미한다.
픽셀 (Pixel) :
픽셀 하나는 RGB 3원색에 대한 정보를 가지고 있으며 총 3Byte이다.
1920 x 1080 화질의 이미지는 약 6MB(= 6,163,200 Byte)의 용량을 가진다.
1Byte 는 8bit 이며 이는 256까지 표현이 가능하다. 때문에 픽셀 하나는 약 1600만개 (= 1600만 화소) 의 색상을 표현할 수 있다.
4화. Win32API 핸들, DC, 윈도우 이벤트 (2)
CreatePen() :
HPEN 객체를 생성하는 함수
HPEN CreatePen( _In_ int iStyle, _In_ int cWidth, _In_ COLORREF color);
// cWidth : 선 굵기
// color : RGB 메크로 사용
RGB 메크로를 해석해보면 G 와 B에 대한 값을 각각 8bit, 16bit 왼쪽으로 밀어준다.
즉, 입력은 RGB 순서로 했지만, 저장은 BGR 순서로 된다.
// RGB 메크로
#define RGB(r,g,b) (COLORREF)(((BYTE)(r)|((WORD)((BYTE)(g))<<8))|(((DWORD)(BYTE)(b))<<16)))
SelectObject() :
커널 오브젝트를 교체해주는 함수
HGDIOBJ SelectObject(_In_ HDC hdc, _In_ HGDIOBJ h);
PEN 또는 BRUSH를 교체해줄 DC와 새로운 PEN/BRUSH 를 인자로 넣어준다.
교체가 성공적으로 이루어지면 이전에 사용하던 커널 객체를 반환한다.
나중에 사용이 끝나면 반환되었던 기존 객체를 다시 넣어줘야한다.
사용이 다 끝난 커널 객체는 DeleteObject() 함수로 메모리 해제 해준다.
BOOL DeleteObject( _In_ HGDIOBJ ho);
GetStockObject() :
미리 생성되어 저장된 커널 오브젝트 객체를 호출한다.
HGDIOBJ GetStockObject( _In_ int i);
"HOLLOW_BRUSH"는 자주 사용되는 "투명" 브러쉬다.
Stock Object의 경우 직접 생성한 객체가 아니기 때문에 사용을 다 했다 할지라도 DeleteObject 하지 않아도 된다.
WM_KEYDOWN :
키보드 입력시 발생하는 메세지
프로시져 함수의 인자에 wParam, lParam 이 있는데 각각 키보드, 마우스에 대한 값이 들어간다.
키보드 입력값은 대문자 기준이다.
WM_LBUTTONDOWN :
마우스 좌측 클릭시 발생하는 메세지
lParam 에 마우스에 대한 부가 정보가 들어가있다.
변수 하나에 x,y 좌표 값이 메모리를 4Byte씩 할당하여 저장되어 있다. -> LOWORD(), HIWORD() 함수를 활용
#define LOWORD(l) ((WORD)(((DWORD_PTR)(l)) & 0xffff))
#define HIWORD(l) ((WORD)((((DWORD_PTR)(l)) >> 16) & 0xffff))
InvalidateRect() :
강제로 무효화 영역을 발생시켜 WM_PAINT 메세지를 발생시킨다.
BOOL InvalidateRect(
_In_opt_ HWND hWnd, // 무효화 영역을 발생시킬 윈도우의 핸들(주소)
_In_opt_ CONST RECT *lpRect, // 발생시킬 영역
_In_ BOOL bErase // 이전 장면 지움여부
);
lpRect 값으로 nullptr 을 넘겨주면 윈도우의 모든 영역에 무효화 영역을 발생시킨다.
bErase 는 이전 장면 갱신여부를 지정하는 인자인데 false 이면 이전장면을 지우지 않고 남기며, true 는 이전장면을 지우고 새로운 장면을 그리는걸 의미한다.
24/03/14
5화. Win32API PeekMessage (1)
실습 :
오브젝트 구조체 생성 -> std::vector 컨테이너에 저장
WM_MOUSEMOVE :
마우스 움직일때만다 발생하는 이벤트
화면상에 그려지는 객체가 추가될수록 화면 깜빡임이 심해지는 이유 :
InvalidateRect() 함수에 의해서 WM_PAINT 메세지가 발생하여 갱신된 정보를 화면상에 새롭게 그려주는데, 너무 빠른 속도로 지우고 그리기가 반복됨에 따라 눈에 인식되지 못한 사각형들이 생기기 때문이다. (타이밍 문제)
이러한 문제를 해결하기 위해 다른 도화지(윈도우)를 준비한다. 준비한 도화지에 다음 Frame에 보여줄 화면을 그리고 다 그려지면 Main 도화지(window)와 바꿔치기 해준다.
현재 해결해야 할 문제 :
- 화면 깜빡임
- Message가 발생안하면 프로그램이 멈춘다 -> GetMessage() 함수의 한계
6화. Win32API PeekMessage (2)
PeekMessage() :
BOOL PeekMessageW(
_Out_ LPMSG lpMsg,
_In_opt_ HWND hWnd,
_In_ UINT wMsgFilterMin,
_In_ UINT wMsgFilterMax,
_In_ UINT wRemoveMsg // 메세지 삭제 유무
);
Peek 은 "훔쳐보다, 몰래보다"의 의미를 가지고 있다. 즉, Message가 있는지 없는지 확인하는 함수이다.
Message 유무에 상관없이 반환값을 가지는 함수이다.
메세지가 잇으면 true, 메세지가 없으면 false 를 반환한다.
마지막 인자로 "PM_REMOVE" 인자를 넣어주면, 확인한 메세지를 메세지 큐에서 제거한다.
7화. Core 클래스 (1) / Singleton (1)
Singleton :
"디자인 패턴"의 설계유형중 하나
생성될 수 있는 객체를 하나로 제한한다.
어디서든 편하게 접근할 수 있도록 해줘야한다.
- extern 키워드를 주면 어디서든 편하게 접근 할 수 있지만, 하나만 만들 수 있는 강제성을 주는 방법이 아니기에 사용할 수 없는 방법이다.
- 생성자를 "private"로 선언하여 외부에서 생성자를 호출할 수 없도록 만든다.
- 해당 클래스 객체를 생성하고 제공하는 함수를 제공한다. -> 해당 함수로만 객체를 얻어올 수 있다.
- 생성자가 private 으로 선언되어 있기에 외부에서 객체를 생성할 수 없기에 객체가 존재하지 않을텐데 멤버함수로 객체를 반환해줘야하는 모순이 발생한다. -> 이를 해결하기 위해 static 키워드 사용한다.
- static 함수는 객체가 없더라도 호출할 수 있다.
- static 함수는 일반 멤버에 접근이 불가능다. static 멤버에는 접근할 수 있다. -> this 키워드가 없기 때문
class CCore
{
private:
static CCore* g_pInst;
public:
CCore* GetInst()
{
if (nullptr == g_pInst)
g_pInst = new CCore;
return g_pInst;
}
void Release()
{
if (nullptr != g_pInst)
{
delete g_pInst;
g_pInst = nullptr;
}
}
private:
CCore();
~CCore();
};
정적(static) 변수 :
"데이터 영역"을 사용한다.
- 함수안에서 선언된 변수가 static 키워드를 가진 경우 해당 함수 안에서만 접근 가능
- 파일안에 선언된 변수가 static 키워드를 가진 경우 해당 파일 안에서만 접근 가능 -> 때문에 다른 파일에 동일 이름의 static 변수가 존재하더라도 변수명 중복 에러가 발생하지 않는다.
- 클래스안에 선언되는 경우 클래스의 사이즈에 포함되지 않지만 해당 클래스를 통해서만 접근 가능하다.
정적변수의 초기화는 함수가 호출될때마다 발생하는게 아니라 함수가 여러번 호출 되더라도 최초 딱 한번만 수행된다.
객체에 대한 static 변수가 함수안에 선언되어 있으면 Release() 와 GetInst() 에서 객체에 대한 정보 공유가 안되어 동기화가 불가능하기 때문에 함수 안이 아닌 클래스 안에 객체 변수를 선언해준다.
8화. Singleton (2)
Singleton 2탄 :
싱글톤 패턴을 구현하는 방법엔 여러가지 방법이 있다.
- static 키워드를 활용하여 구현
- 데이터 영역을 활용하여 구현
- etc ...
이 외에도 많지만, 데이터 영역을 활용하는게 간편하기에 해당 방법을 채택한다.
데이터 영역을 활용하면 구현하기 간단하며, 메모리 해제를 신경써주지 않아도 된다는 장점이 있다.
하지만 프로그램 중간에 해당 객체의 메모리 해제를 할 수 없다보니 프로그램이 종료될때까지 가지고 가야한다는 단점이 있다.
class CCore
{
public:
static CCore* GetInst()
{
// 정적(static) 변수는 최초 호출시에만 초기화가 수행된다.
// 두번째 부턴 초기화 코드 무시
static CCore core;
return &core;
}
public:
int Init(HWND _hWnd, POINT _ptResolution);
public:
HWND m_hWnd;
POINT m_ptResolution;
private:
CCore();
~CCore();
};
메크로(Macro) :
#define SINGLE(type) public:\
static type* GetInst()\
{\
static type mgr;\
return &mgr;\
}
자주 사용하는 코드는 메크로화 해주면 편하다.
Singleton 패턴의 경우 자주 사용되기에 메크로화 해준다. -> Manager 객체는 대부분 Singleton 패턴구현
메크로를 사용할 땐 중의해야 할 상황이 있는데...
메크로는 함수처럼 실행되는게 아니라 해당 위치에 코드가 치환되어 실행된다는 점을 잊으면 안된다.
#define ADD(a,b) (a + b)
int Add(int a, int b)
{
return a + b;
}
// 주의점 :
int result1 = 10 * Add(10, 20); // 함수 : 300
int result2 = 10 * ADD(10, 20); // 매크로 : 120
위의 예제를 보면 메크로 ADD 의 경우 코드만 치환되기에 "10 * 10 + 20" 이 되어 120 이라는 결과가 나오고 함수 Add의 경우 "10 * (10 + 20)" 이 되어 300 이라는 결과값이 반환된다.
위와 같이 의도하지 않은 결과가 나올 수 있기에 주의하도록 하자.
Next Note
Win32API 강의_2
24/03/17 9화. Core 클래스 (2) 윈도우 사이즈 조정 : 윈도우 생성시 입력해준 윈도우 사이즈에는 출력 영역 외에도 제목표시줄, 메뉴바 영역도 포함한 사이즈이다. 이미지가 출력되는 영역을 "클라인
coder-qussong.tistory.com
'Win32API' 카테고리의 다른 글
Win32API_StudyNote_6 (0) | 2024.03.24 |
---|---|
Win32API_StudyNote_5 (0) | 2024.03.22 |
Win32API_StudyNote_4 (0) | 2024.03.21 |
Win32API_StudyNote_3 (0) | 2024.03.19 |
Win32API_StudyNote_2 (0) | 2024.03.17 |