목표
이번 포스팅에서는 문자열 뿐만 아니라, 긴 텍스트, 픽셀, 원 등등 다양한 것들을 화면에 출력할 것입니다.
목차 클릭하면 해당 목차로 이동합니다.
개요
이전 포스팅에서 Device Context에 대한 개념과 GDI에 대해서 배웠습니다. 이를 통해, 출력 시 발생하는 메세지 및 메세지 처리를 비롯한 출력의 일련의 과정에 대해서 이해했습니다. 이번 포스팅에서는 긴 텍스트를 출력해보고, 간단한 도형과 메세지박스를 출력하는 프로젝트를 진행해서 간단한 출력에 대해서 이해하는 시간을 갖도록 하겠습니다.
긴 문자열 출력하기
이전 포스팅에서 문자열을 출력하기 위해 TextOut 함수를 사용했습니다. 말 그대로 문자열을 출력하는 함수입니다. 이번에는 사각형 안에 문자열을 그리는 DrawText 함수를 활용해서 긴 문자열을 출력하도록 하겠습니다.
DrawText 프로젝트
윈도우 프로시저에서 사용할 변수 선언
먼저, 출력을 위해 HDC 구조체와 PAINTSTRUCT를 선언합니다. DrawText 함수는 사각형 안에 문자열을 출력하기 때문에, 사각형 정보를 정의해줄 필요가 있습니다. 따라서, RECT 구조체를 선언하고 사각형 정보를 정의합니다. RECT 구조체의 원형은 다음과 같습니다.
typedef struct _RECT {
LONG left; //왼쪽 위
LONG top; // 오른쪽 위
LONG right; // 오른쪽 아래
LONG bottom; // 왼쪽 아래
}RECT;
4개의 좌표를 입력하는 것을 볼 수 있는데, 차례대로 { 왼쪽 위, 오른쪽 위, 오른쪽 아래, 왼쪽 아래 } 입니다. 사각형의 왼쪽 위 꼭지점에서 시계방향으로 돈다고 생각하면 됩니다. RECT 구조체는 상당히 많이 사용하기 때문에 유심히 볼 필요가 있습니다. 이후, 문자열을 정의합니다. 자세히 보시면 무언가 이상한 것이 있습니다. 바로, 각 문장 사이에 콤마가 없습니다. 이렇게 긴 문자열을 선언할 때, 콤마를 찍지 않고 마지막 문자열 끝에 세미콜론을 적어주면 됩니다.
WM_PAINT 메세지
DC를 얻기 위해 BeginPaint 함수를 사용했습니다. 그리고 DrawText 함수를 통해 긴 문자열을 출력합니다. 해당 함수의 원형은 다음과 같습니다.
DrawText(1. HDC hDC, 2. LPCTSTR lpString, 3. int nCount, 4. LPRECT lpRect, 5. UINT uFormat);
1. Device Context의 핸들
2. 출력될 문자 포인터
3. 출력될 문자열의 길이 (-1이면 NULL 종류 문자열로 간주)
4. 출력될 사각영역 RECT 구조체 포인터
5. 옵션 (사각형 안에서 문자열을 정렬하는 것에 대한 정보)
저번 포스팅에서 문자열의 길이는 숫자로 직접 적어줬는데, 일일히 그렇게 하는 것은 번거롭기 때문에 _tcslen(문자열) 함수를 통해 문자열의 길이를 구합니다. RECT 구조체의 주소를 넣어 사각형 영역 정보를 알려주고, 옵션을 넣습니다. Win32 API에서 옵션이 여러 개가 있을 경우 |(OR)연산을 통해 동시에 사용할 수 있습니다.
DrawText의 옵션은 다음과 같습니다.
값 | 기능 |
DT_LEFT | 수평 왼쪽 정렬 |
DT_RIGHT | 수평 오른쪽 정렬 |
DT_CENTER | 수평 중앙 정렬 |
DT_BOTTOM | 사각 영역의 바닥에 문자열 출력 |
DT_VCENTER | 사각 영역의 수직 중앙에 문자열 출력 |
DT_WORDBREAK | 사각 영역의 오른쪽 끝에서 자동으로 개행 |
DT_SINGLELINE | 한 줄로 출력 |
DT_NOCLIP | 사각 영역의 경계를 벗어나도 문자열을 자르지 않고 출력 |
결과를 보면 중앙에 정렬되어 출력하고 문자열이 사각형의 오른쪽을 넘어가면 자동으로 개행하는 것을 볼 수 있습니다.
이렇게, Win32 API는 기본 코드에 처리할 메세지를 정확하게 알고 WndProc 함수에 추가하면서 기능을 추가할 수 있습니다.
다양한 그래픽(도형) 출력하기
이번에는, 문자열이 아닌 그래픽을 출력하는 것을 해보도록 하겠습니다. Win32 API는 픽셀 하나부터, 선, 원, 사각형 등 다양한 도형을 그릴 수 있는 함수를 지원하고 있습니다.
픽셀: COLORREF SetPixel(hdc, nXPos, nYPos, corref) // X, Y좌표, 색깔 정보
선의 시작점: DWORD MoveToEx(hdc, x, y, lpPoint) // 선의 시작점의 X,Y 좌표, 이전 Ex 좌표
선의 끝점: BOOL LineTo(hdc, xEnd, yEnd) // 선의 끝 점의 X, Y좌표
사각형: BOOL Rectangle(hdc, nLeftRect, nTopRect, nRightRect, nBottomRect) //왼쪽 위부터 시계방향
원: BOOL Ellipse(hdc, nLeftRect, nTopRect, nRightRect, nBottomRect) // 사각형 내부에 원을 그림
출력을 위해 Device Context의 핸들정보인 HDC는 공통으로 들어갑니다. 선을 긋는 것은 조금 특이합니다. 시작점과 끝 점의 좌표만 주면 슥 그을 것 같지만, 두 개의 함수를 같이 사용해야 합니다. MoveToEx 함수로 현재 위치를 시작점으로 이동해서, LineTo 함수를 통해 끝 점으로 이동하면서 선을 긋는 원리입니다. 다소 복잡하고 귀찮은 과정 같지만, 이를 통해 다양한 활용이 가능합니다. 추후에 ROP 모드를 배우면서 자세히 다룰 예정입니다.
원은 사각형 영역 정보를 입력하면, 그 사각형에 내접하는 원을 그리는 원리입니다. 한번 직접 그려보면서 확인해보도록 하겠습니다.
GraphOut 프로젝트
보시는 것처럼, 각 함수를 이용해서 도형을 그리고 있습니다. 자세히 보시면, 타이틀바 바로 밑에 빨간색 픽셀이 하나 찍혀있는 것을 볼 수 있습니다. 다 위에서 말씀드린 함수의 인수에 좌표만 넣은 것이기 때문에 따로 설명할 부분은 없는 것 같습니다. 선을 긋는 것만 보면, 처음에 MoveToEx함수를 통해 (50, 50)으로 이동해서 LineTo 함수로 (300, 90)까지 선을 긋는 것을 볼 수 있습니다. 이 경우에, 이전 위치가 없기 때문에 MoveToEx 함수의 네 번째 인수는 NULL값이 들어가 있습니다. 두 함수는 같이 사용되는 콜라와 햄버거 같은 사이라고 생각하시면 좋을 것 같습니다.
메세지 박스(Message Box) 출력하기
창을 닫거나 할 때 메세지 박스를 보신 적이 많이 있을 것이라고 생각합니다. 보통 팝업창이라고도 많이 하는데요. 마우스의 왼쪽 버튼을 누를 경우 팝업을 띄우는 프로젝트를 진행하면서 출력을 마무리하려고 합니다. 새로운 윈도우를 띄우는 것이기 때문에 추후에 더 정확하게 다룰 예정입니다.
MessageBox 프로젝트
WM_LBUTTON 메세지
이전 프로젝트에서 본 메세지입니다. 왼쪽 마우스가 눌리는(떼거나 움직이는건 다른 메세지입니다.) 경우 발생하는 메세지인 WM_LBUTTONDOWN 입니다. 메세지가 발생하면 MessageBox함수를 통해 메세지 박스(팝업창)을 띄웁니다. 해당 함수의 원형은 다음과 같습니다.
int MessageBox(1. HWND hWnd, 2. LPCTSTR lpText, 3. LPCTSTR lpCaption, 4. UINT uType);
1. 메세지 박스의 Owner 윈도우(부모 윈도우)
2. 메세지 박스 안에 출력할 문자열
3. 메세지 박스 타이틀에 출력할 문자열
4. 메세지 박스의 종류(어떤 버튼이 나타나는지?)
먼저, 반환형이 int입니다. 이는 사용자가 어떤 버튼을 눌렀는지 인지해야 그에 맞는 대처를 할 수 있기 때문입니다. 이번 프로젝트는 변수에 따로 저장해놓지 않기 때문에, 다른 동작을 하지 않습니다. 해당 반환값을 변수에 저장해놓고 있다가조건문 등을 활용해 그에 맞는 대처를 할 수 있습니다.
MessageBox 함수의 인수 중 첫 번째 인수를 보면, Owner 윈도우라고 되어있습니다. 사실 메세지 박스(팝업창)은 자식 윈도우입니다. 우리가 작성한 프로그램 위에 새로운 윈도우를 띄우는 것이기 때문입니다. 아직은 복잡하게 느껴질 수 있기 때문에, 컨트롤에 대해서 배우면서 다시 한 번 언급하도록 하겠습니다.
메세지 박스에 예/아니오 뿐만 아니라, 다양한 버튼을 추가할 수 있습니다. 해당 내용과 관련된 것이 네 번째 인수입니다.
기본적으로 Win32 API에서 지원하는 Message Box의 형태는 다음과 같습니다.
값 | 기능 |
MB_ABORTRETRYIGNORE | Abort, Retry, Ignore 3개의 버튼이 나타난다. |
MB_OK | OK 버튼 하나만 나타난다. |
MB_OKCANCEL | OK, CANCEL 두 개의 버튼이 나타난다. |
MB_RETRYCANCEL | Retry, Cancel 두 개의 버튼이 나타난다. |
MB_YESNO | Yes, No 두 개의 버튼이 나타난다. |
MB_YESCONCANCLE | Yes, No, Cancel 세 개의 버튼이 나타난다. |
뭔가 길어 보이는 것 같아도, 네이밍이 상당히 단순합니다. Message Box를 뜻하는 MB_ 뒤에 나오는 버튼들을 나열한 것입니다.
왼쪽 마우스 버튼을 클릭하면 메세지 박스가 출력되는 것을 볼 수 있습니다.
정리
이렇게 Win32 API를 활용한 간단한 출력에 대해서 알아보았습니다. 기본적인 내용을 잘 숙지하고 있다면, 원리가 복잡하지 않기 때문에 금방 배울 수 있다고 생각합니다. 다음 포스팅에서는 입력에 대해서 다뤄보도록 하겠습니다.
*프로젝트의 코드는 Github에서 확인할 수 있습니다.
Github로 이동하기
'개발 > Win32 API Programming' 카테고리의 다른 글
[Windows API] Win32를 활용해 마우스 입력하기 (0) | 2022.01.09 |
---|---|
[Windows API] Win32 API를 활용해 키보드 입력하기 (2) | 2022.01.06 |
[Windows API] Device Context란?, Win32 API를 활용해 문자열 출력하기 (0) | 2021.12.31 |
[Windows API] Win32 API의 기본구조, 윈도우 프로시저 (0) | 2021.12.26 |
[Windows API] Win32 API의 기본구조, WinMain-(2) (0) | 2021.11.05 |