>아이콘, 커서
아이콘이나 커서도 리소스의 일종이며 각각의 편집기를 사용하여 제작할 수 있다.
![](https://t1.daumcdn.net/cfile/tistory/237FCB4C564A75470E)
![](https://t1.daumcdn.net/cfile/tistory/2248154B564A754F1B)
![](https://t1.daumcdn.net/cfile/tistory/2618D144564A8C100E)
![](https://t1.daumcdn.net/cfile/tistory/2619C744564A8C120D)
![](https://t1.daumcdn.net/cfile/tistory/27248844564A8C1405)
![](https://t1.daumcdn.net/cfile/tistory/2316A244564A8C1710)
>액셀러레이터
액셀러레이터(Accelerator)는 아주 쉽게 말해서 단축키
![](https://t1.daumcdn.net/cfile/tistory/243C5E41564A8CB735)
![](https://t1.daumcdn.net/cfile/tistory/237A0141564A8CC807)
![](https://t1.daumcdn.net/cfile/tistory/25140445564A8CDA0B)
![](https://t1.daumcdn.net/cfile/tistory/24166A3F564A8CE505)
![](https://t1.daumcdn.net/cfile/tistory/252F263D564A8D3939)
ID 액셀러레이터의 ID이며 프로그램 소스에서 액셀러레이터를 참조할 때 이 값을 사용한다. 메뉴의 ID를 작성하는 방법과 동일한 규칙대로 ID를 작성하되 이 예제의 경우는 메뉴에 작성되어 있는 ID를 그대로 사용하면 된다. 드롭다운 리스트를 열어 ID에 ID_FILE_MENU1을 선택한다.
Key 액셀러레이터로 사용할 키를 선택한다. 펑션키나 특수키를 사용하려면 드롭다운 리스트를 열어 선택하고 알파벳키나 숫자키를 선택하려면 바로 아래쪽의 Next Key Typed버튼을 누른 후 원하는 키를 키보드에서 누르면 된다. Next Key Typed를 누른 후 키보드에서 "A"키를 누른다.
Modifier Key와 함께 눌러질 조합키를 선택한다. Ctrl, Alt, Shift를 개별적으로 또는 여러개를 한꺼번에 선택할 수 있다. Ctrl키만 선택한다.
Type Key값이 아스키 코드값인지 가상키 코드값인지를 설정한다. 보통 가상키 코드를 많이 사용한다
![](https://t1.daumcdn.net/cfile/tistory/2312393F564A8D5E0B)
![](https://t1.daumcdn.net/cfile/tistory/23545D46564A8D6A0A)
HACCEL LoadAccelerators(HINSTANCE hInstance, LPCTSTR lpTableName );
이 함수는 리소스로부터 액셀러레이터 테이블을 읽어들인다. 두번째 인수 lpTableName은 액셀러레이터 테이블의 이름 문자열 포인터이되 우리가 작성한 액셀러레이터 테이블 IDR_ACCELERATOR1은 정수값이므로 MAKEINTRESOURCE 매크로를 사용해야 한다. 이 함수는 리소스에서 액셀러레이터 테이블을 읽은 후 그 핸들값을 리턴해 준다. 이 핸들값을 hAccel이라는 변수에 대입해 두면 다음부터 hAccel을 통해 액셀러레이터 테이블을 읽을 수 있다. 메시지 루프안에는 다음 함수가 삽입되었다.
int TranslateAccelerator(HWND hWnd, HACCEL hAccTable, LPMSG lpMsg );
이 함수는 키보드 메시지를 WM_COMMAND 메시지로 변경해 주어 액셀러레이터가 동작할 수 있도록 해 준다.
액셀러레이터 Ctrl+A가 입력되었다고 해 보자. Ctrl+A는 액셀러레이터이기 이전에 키보드로부터의 입력이므로 먼저 WM_KEYDOWN 메시지가 발생할 것이고 그대로 내버려 두면 WndProc의 WM_KEYDOWN 메시지 처리 루틴에서 먼저 이 키값을 처리해 버릴 것이다. 그래서 TranslateAccelerator 함수는 키보드 입력값을 읽어 이 키값이 지정한 액셀러레이터 테이블에 있는지를 먼저 살펴보고 있을 경우 WM_COMMAND 메시지를 발생시키고 TRUE를 리턴해 버린다. 그래서 액셀러레이터가 입력되었을 경우 TranslateMessage, DispatchMessage 함수가 실행되지 못하도록 막아 버리며 다음번의 WM_COMMAND메시지가 처리되도록 해 준다. 물론 액셀러레이터 입력이 아니면 FALSE를 리턴하여 다른 메시지들은 정상적으로 처리되도록 해 준다.
![](https://t1.daumcdn.net/cfile/tistory/2757F446564A8D8A08)
>문자열 테이블
![](https://t1.daumcdn.net/cfile/tistory/2310FF40564A8DC333)
![](https://t1.daumcdn.net/cfile/tistory/21422D40564A8DD10F)
리소스 ID를 인식하기 위해 resource.h를 먼저 포함시켰으며 WM_PAINT에서 문자열 리소스를 읽어와 화면으로 출력하였다. 문자열 리소스를 읽을 때는 다음 함수를 사용한다.
int LoadString( HINSTANCE hInstance, UINT uID, LPTSTR lpBuffer, int nBufferMax);
첫번째 인수는 문자열 리소스를 가진 인스턴스 핸들인데 이 값은 WinMain의 첫번째 인수로 전달되며 ApiStart.txt에서 이 인수를 전역 변수 g_hInst에 대입해
두었으므로 g_hInst를 써 주면 된다.
두번째 인수로 읽어올 문자열의 ID를 주고 세번재 인수로 문자열을 읽을 버퍼, 네번째 인수로 버퍼의 길이를 주면 된다.
LoadString(g_hInst, IDS_STRING1, str, 256);
이 함수 호출문의 의미를 해석해 보면 g_hInst 인스턴스에 정의된 IDS_STRING1 문자열을 길이 256의 str 문자 배열로 읽어 오라는 뜻이다. 문자열 리소스는 최대 255문자까지 가능하므로 버퍼 길이는 256이면 충분하다. 이렇게 읽은 문자열 str을 TextOut으로 화면에 출력하였다
![](https://t1.daumcdn.net/cfile/tistory/2515203F564A8DF50C)
스톡 오브젝트(Stock Object)는 윈도우즈가 기본적으로 제공해 주는 GDI 오브젝트를 말한다. 운영체제가 제공해 주므로 일부러 만들지 않아도 언제든지 사용할 수 있으며 사용하고 난 후에 파괴시켜 줄 필요도 없다. 다음 함수로 핸들을 얻어 사용하기만 하면 된다.
HGDIOBJ GetStockObject( int fnObject );
fnObject 인수에 사용하고자 하는 스톡 오브젝트를 기입해 주면 된다. 사용 가능한 스톡 오브젝트는 다음과 같다. 주로 브러시와 펜이 스톡 오브젝트로 제공된다. 2장에서 처음 공부했던 WHITE_BRUSH도 알고보면 스톡 오브젝트의 일종이다.
![](https://t1.daumcdn.net/cfile/tistory/24532244564BD7DE12)
![](https://t1.daumcdn.net/cfile/tistory/25649B44564BD7DF06)
도스에서는 WHITE, YELLOW, RED 등의 매크로 상수로 색상을 표현했고 이 매크로들의 실제값은 0~15까지의 정수였었다. 도스에서야 기껏 16색상까지 사용할 수 있었으므로 각 색상마다 이름을 줄 수 있었지만 최대 천육백만가지 색상을 사용할 수 있는 윈도우즈 환경에서는 이런 간단한 방법을 쓸 수 없다. 윈도우즈에서는 색상값을 표현하기 위해 COLORREF라는 데이터형을 사용하는데 이는 다음과 같이 정의되어 있다.
typedef DWORD COLORREF;
보다시피 COLORREF 형은 부호없는 32비트 크기의 정수형이며 8비트씩 빨간색, 초록색, 파란색의 농도를 나타내며 상위 8비트는 사용되지 않는다. 각 색상 요소는 1바이트의 크기를 가지므로 0~255까지의 농도를 표현할 수 있다.
![](https://t1.daumcdn.net/cfile/tistory/23300F3F564BD80C16)
COLORREF형은 32비트 정수일 뿐이므로 직접 16진수로 표현할 수도 있다. 예를 들어 0는 검정색이 되며 0xff는 빨간색, 0xff0000은 파란색이 된다. 하지만 이 방법은 너무 기계적이고 초보자가 쓰기는 힘들므로 색상값을 만들 때는 통상 RGB 매크로 함수를 사용하며 이 매크로는 다음과 같이 정의되어 있다.
#define RGB(r,g,b) ((COLORREF)(((BYTE)(r) | ((WORD)((BYTE)(g))<<8)) | (((DWORD)(BYTE)(b))<<16)))
세 개의 인수를 가지는데 각각 빨간색, 초록색, 파란색의 농도이며 이 세값을 조립하여 하나의 32비트 색상값을 만들어내는 간단한 비트 연산을 하고 있다. 비트 쉬프트, 비트 OR 연산자를 적절히 혼합한 문장인데 그리 어렵지 않게 이해가 될 것이다.
각 색상 요소가 얼마만큼 혼합되어 있는가에 따라 실제 색상이 결정되는데 RGB(255,0,0)는 빨간색, RGB(0,0,255)는 파란색이다. 세 요소가 모두 다 최대치인 RGB(255,255,255)는 흰색이며 반대로 RGB(0,0,0)는 검정색이다. 윈도우즈에서는 이와같이 RGB 매크로를 사용하여 색상값을 표현하며 COLORREF 형의 인수 자리에는 RGB 매크로를 사용하면 된다. 다음 세 매크로 함수는 COLORREF 형 변수값에서 각 색상요소의 농도를 분리해내는 함수이다.
#define GetRValue(rgb) ((BYTE)(rgb))
#define GetGValue(rgb) ((BYTE)(((WORD)(rgb)) >> 8))
#define GetBValue(rgb) ((BYTE)((rgb)>>16))
>펜
펜은 선을 그을 때 사용되는 GDI 오브젝트이다. 펜을 변경하면 그려지는 선의 모양을 마음대로 변경할 수 있다. 그런데 윈도우즈가 제공하는 스톡 펜은 흰색, 검정색, 투명색 세 가지 뿐이며 파란색, 노란색 등의 원색 펜은 없다. 이런 펜을 사용하고자 할 때는 직접 만들어서 사용해야 한다. 펜을 만들 때는 다음 함수를 사용한다.
HPEN CreatePen( int fnPenStyle, int nWidth, COLORREF crColor );
fnPenStyle
그려질 선의 모양을 정의한다. 이 값을 변경하면 실선뿐만 아니라 다양한 형태의 선을 만들 수 있다. 실선, 점선, 일점 쇄선 등등의 선 모양이 있다.
![](https://t1.daumcdn.net/cfile/tistory/246CB840564BD82C10)
nWidth
선의 폭을 지정한다. 디폴트 선의 굵기는 1이지만 이 값을 2나 3으로 변경해 주면 두꺼운 선을 그릴 수 있다. 단 이 값이 0일 경우는 맵핑 모드에 상관없이 무조건 1픽셀 두께의 선이 만들어진다.
crColor
선의 색상을 지정한다. COLORREF 형이므로 RGB 매크로 함수를 사용하면 된다.
![](https://t1.daumcdn.net/cfile/tistory/24589938564BD8491E)
GDI 오브젝트는 사용한 후 반드시 삭제해 주어야 한다. 왜냐하면 GDI 오브젝트도 메모리를 사용하기 때문이다. 메모리를 할당한 후 반드시 해제해 주어야 하는 것과 마찬가지로 GDI 오브젝트도 사용이 끝나면 해제해 주어야 하는 것이 원칙이다. 만약 해제해 주지 않으면 시스템의 메모리를 갉아먹게 될 것이다. GDI 오브젝트를 삭제할 때는 다음 함수를 사용한다
BOOL DeleteObject( HGDIOBJ hObject );
삭제하고자 하는 GDI 오브젝트의 핸들만 인수로 넘겨주면 된다. 단, 이때 주의할 것은 DC에 현재 선택되어 있는 GDI 오브젝트는 삭제할 수 없다는 점이다. 현재 사용되고 있는 객체를 함부로 삭제하도록 내버려 둘 수는 없기 때문에 생긴 일종의 안전 장치 역할을 하는 규정이다. 그래서 삭제를 하기 전에 먼저 DC에 선택된 객체를 선택 해제해 주어야 하는데 선택을 해제시켜주는 별도의 함수는 제공되지 않으므로 다른 GDI 오브젝트를 선택해 주는 방법을 사용한다. 이런 이유로 OldPen이라는 핸들을 만든 후 이 핸들에 MyPen이 선택되기 전의 펜 핸들을 저장해 두고 MyPen을 삭제하기 전에 OldPen을 다시 선택해 주는 것이다.
SelectObject(hdc, OldPen);
DeleteObject(MyPen);
OldPen이 DC에 선택되면 자연히 현재 선택되어 있는 MyPen이 선택해제될 것이고 따라서 MyPen을 안전하게 삭제할 수 있다. 이 두 줄은 다음과 같이 한줄로도 작성할 수 있다.
DeleteObject(SelectObject(hdc, OldPen));
SelectObject가 이전 핸들값을 리턴해 주므로 OldPen을 선택함과 동시에 리턴되어져 나오는 MyPen을 삭제하는 것이다. 이때 사용되는 OldPen은 이전에 선택되어 있던 펜을 보존한다기보다는 단순히 MyPen을 선택해제하기 위한 용도로 사용된 것이다. GDI 오브젝트를 만들고 사용하는 일반적인 절차는 다음과 같다.
![](https://t1.daumcdn.net/cfile/tistory/21431E38564BD86D2D)
펜뿐만 아니라 브러시, 폰트 등 모든 GDI 오브젝트는 일반적으로 이런 절차를 거쳐 만들어지고 사용된다.
>브러시
브러시는 채워지는 면을 채색하는 용도로 사용된다. 사각형의 안쪽이나 원의 내부 또는 다각형의 내부를 채색할 때 현재 DC에 선택된 브러시가 사용된다.
HBRUSH CreateSolidBrush( COLORREF crColor );
HBRUSH CreateHatchBrush( int fnStyle, COLORREF clrref );
첫번째 함수는 단색의 브러시만을 만들 수있으며 브러시의 색상만 인수로 전달해 주면 된다.
두번째 함수는 색상뿐만 아니라 무늬도 같이 지정할 수 있다. 지정할 수 있는 무늬의 종류는 다음과 같다.
![](https://t1.daumcdn.net/cfile/tistory/2203C833564BD88E28)
두 함수 모두 리턴하는 값은 만들어진 브러시의 핸들이다.
![](https://t1.daumcdn.net/cfile/tistory/27072B35564BD8A91B)
이전에 사용하던거 조합
>Ropmode
반전모드를 사용하는 예제를 만들어 보자. 마우스로 선을 그리도록 하되 선이 그려지는 중간 과정을 보여주도록 한다. 즉 마우스 버튼을 누른 위치에서부터 시작해서 다시 마우스 버튼을 놓는 자리까지 선을 긋되 버튼을 누른채로 마우스를 움직이면 중간에 그려지는 선의 모양을 보여주도록 하는 것이다
![](https://t1.daumcdn.net/cfile/tistory/277B9C34564BD8C92B)
![](https://t1.daumcdn.net/cfile/tistory/2234193C564BD8E21C)
>맵핑 모드
![](https://t1.daumcdn.net/cfile/tistory/24135539564BD8FD20)
몰라
>bitmap.dsw
![](https://t1.daumcdn.net/cfile/tistory/23132A39564BD92C23)
![](https://t1.daumcdn.net/cfile/tistory/22131839564BD92E23)
![](https://t1.daumcdn.net/cfile/tistory/24168239564BD92F20)
![](https://t1.daumcdn.net/cfile/tistory/230FFD39564BD93019)
>BitBlt
BitBlt 함수는 DC간의 영역끼리 고속 복사를 수행한다. 메모리 DC의 표면에 그려져 있는 비트맵을 화면 DC로 복사함으로써 비트맵을 화면으로 출력
원본 그대로 복사한다
BOOL BitBlt(HDC hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, HDC hdcSrc, int nXSrc, int nYSrc, DWORD dwRop)
첫번째 인수는 복사 대상 DC이며 다음 네 개의 인수는 복사 대상의 XYWH이며 PSrcDC가 복사원의 DC이다. xSrc, ySrc는 복사원의 좌표이되 BitBlt는 비트맵의 크기를 변경시키지 않고 복사를 수행하므로 폭과 높이는 복사 대상에서 한번만 지정하고 복사원에서는 이 값을 그대로 사용한다. 소스를 보면 MemDC의 0,0위치를 복사 대상의 0,0위치에 폭 123, 높이 160만큼 복사한다.
![](https://t1.daumcdn.net/cfile/tistory/257E9C37564BD9C01C)
BitBlt의 마지막 인수 dwRop는 레스터 연산 방법을 지정하며 SRCCOPY를 쓰면 복사원을 그대로 복사 대상으로 복사한다. dwRop에 다른 값을 사용하면 기존 그림에 겹친다거나 반전시킬 수도 있다. 앞에서 배운 그리기 모드와 개념적으로 유사하며 다음과 같은 값들이 가능하다.
![](https://t1.daumcdn.net/cfile/tistory/224B0038564BD9DD26)
비트맵 출력이 끝난 후에는 비트맵 자체와 메모리 DC를 해제해 주어야 한다. 비트맵은 GDI 오브젝트이므로 DeleteObject 함수로 지우면 되고 메모리 DC는 DeleteDC라는 별도의 함수를 사용하여 지운다
>StretchBlt
BOOL StretchBlt ( HDC hdcDest, int nXOriginDest, int nYOriginDest, int nWidthDest, int nHeightDest, HDC hdcSrc, int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc, DWORD dwRop ); |
BitBlt와 마찬가지로 DC간의 복사를 수행하지만 복사 후에 크기가 변경된다는 점이 다르다. 인수를 보면 복사 대상과 복사원이 모두 폭과 높이를 가지고 있다. 복사원의 지정한 영역이 복사대상의 지정한 영역의 크기만큼 확대되어 출력된다. 물론 복사대상의 영역이 복사원보다 더 좁다면 축소가 발생할 것이다.
![](https://t1.daumcdn.net/cfile/tistory/247FFB33564BD9FD2D)
StretchBlt(hdc,0,0,500,500,MemDC,0,0,100,120,SRCCOPY);
이 코드에서는 500,500의 폭을 가지는 영역을 100, 120 영역에 복사했으므로 비트맵이 두배의 크기로 확장된다.
>비트맵 만들기
![](https://t1.daumcdn.net/cfile/tistory/22131839564BD92E23)
![](https://t1.daumcdn.net/cfile/tistory/2308DC36564BDA461E)
![](https://t1.daumcdn.net/cfile/tistory/21174D36564BDA4715)
>게임 만들기
![](https://t1.daumcdn.net/cfile/tistory/26125B33564BDB4D2B)
![](https://t1.daumcdn.net/cfile/tistory/277E7D33564BDB4E32)
![](https://t1.daumcdn.net/cfile/tistory/2125F633564BDB4F22)
![](https://t1.daumcdn.net/cfile/tistory/23138033564BDB522B)
![](https://t1.daumcdn.net/cfile/tistory/2503C833564BDB5832)
![](https://t1.daumcdn.net/cfile/tistory/27278A49564BDB9D26)