COLORREF Set Pixel(hdc, nXPos, nYPos, clrref)
지정한 좌표에 clrref색 하나의 점을 찍는다.

DWORD MoveToEX(hdc, x, y, lpPoint);
BOOL LineTo(hdc,xEnd,yEnd);

LineTo는 CP부터 지정한 좌표까지 선을 긋는다. 그리고 CP를 지정한 좌표로 업데이트한다. 따라서 LineTo를 계속 호출하면 연속된 선을 그을수 있다.
MoveToEx는 CP를 지정한 좌표로 이동시키고 필요한경우 이전 CP의 좌표를 lpPoint에 담는다. 필요하지 않을경우(현재수준에서는 계속해서) NULL값을 주면 된다.

Rectangle(hdc, nLeftRect,nTopRect,nRightRect,nBotomRect);
Ellipse(hdc, nLeftRect,nTopRect,nRightRect,nBottomRect);

Rectangle은 네모를
Ellipse는 동그라미를 그린다.
둘다 왼쪽 위x,y, 오른쪽 아래,x,y좌표 순서대로 입력하면 된다.

SetWindowText(HWND hWnd,LPCWSTR lpString);
윈도우 타이틀바에 표시할때 사용. 뭔가 문자열 테스트할때도 많이 사용한다.
이것또한 wsprintf문을 사용할수 있다.

'Programming > Visual C++' 카테고리의 다른 글

입력1. WM_CHAR  (0) 2011.06.29
출력5. MessageBox & MessageBeep  (0) 2011.06.28
출력3. DrawText  (0) 2011.06.28
출력2. WM_Paint  (0) 2011.06.28
출력1. TextOut  (0) 2011.06.28
TextOut에서 발전하면서도 디테일한 것을 만질수 있는 함수다.
int DrawText(HDC hDC, LPCTSTR lpString, int nCount, LPRECT lpRect, UINT uFormat);

뭐 DC는 알테고 그 다음은 출력할 글자,
그 다음에 있는 nCount는 문자열 안에 있는 만자의 갯수를 나태내는 int형 변수다. 만약 이 것이 -1이면 DrawText는 마지막에 null 값을 가지는 포인터가 있는 것으로 가정하고 문자의 갯수는 자동으로 카운트 한다.
그다음은 Rect의 주소값. 즉, 글자가 들어갈 사각형을 표시해준다.
uFormat은 출력 형식인데,
DT_LEFT 수평 왼쪽정렬
DT_Right 수평 오른쪽 정렬
DT_CENTER 수평 중앙정렬
DT_BOTTOM 사각영역의 바닥에 문자열 출력
DT_VCENTER 사각영역의 수직 중앙에 문자열 출력
DT_WORDBREAK 사각영역의 오른쪽 끝에서 자동 개행
DT_SINGLELINE 한줄로 출력
DT_NOCLIP 사각영역의 경계를 벗어나도 문자열을 자르지않고 그대로 출력
등이 있다.

'Programming > Visual C++' 카테고리의 다른 글

출력5. MessageBox & MessageBeep  (0) 2011.06.28
출력4. 기타  (0) 2011.06.28
출력2. WM_Paint  (0) 2011.06.28
출력1. TextOut  (0) 2011.06.28
How to Programming by Unicode in windows programming - 3  (1) 2011.06.25

자, 이번엔 Paint에 대해서 알아보도록 하겠다.
dc의 경우엔 사실 Invalidate()가 자동적으로 이루어지지 않았다.(2010, windows7에서는 자동으로 되는듯 싶지만) WM_Paint를 사용하면 Uncover가 자동적으로 이루어진다. 음... 정확하진 않다.
일단 사용방법은.

 PAINTSTRUCT 구조체를 사용하는 방법이다.

자, 자세한 예제를 보면,
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;

switch(iMessage)
{
case WM_DESTROY:PostQuitMessage(0);return 0;
case WM_PAINT :
hdc = BeginPaint(hWnd, &ps);
TextOut(hdc, 100,100,TEXT("Beautiful Korea"),15);
EndPaint(hWnd,&ps);
return 0;
}
return(DefWindowProc(hWnd,iMessage,wParam,lParam);
}

자, 여기서 새로나온걸 보자면 PAINTSTRUCT 구조체와 BeginPaint, EndPaint다.
PAINTSTRUCT의 안을 들여다보면
typedef struct tagPAINTSTRUCT
{
HDC hdc;
BOOL fErase;
RECT rcPaint;
BOOL fRestore;
BOOL flncUpdate;
BYTE rgbReserved[16];
} PAINTSTRUCT;

MSDN에서의 설명을 그냥 번역해보자면
hdc

A handle to the display DC to be used for painting.
그리기를 위해 사용될 DC를 표시할 핸들.

fErase

Indicates whether the background must be erased. This value is nonzero if the application should erase the background. The application is responsible for erasing the background if a window class is created without a background brush. For more information, see the description of the hbrBackground member of the WNDCLASS structure.
지시한다 배경이 지워져야만 할것인지 아닌지. 만약 이 어플이 배경을 지운다면 이 값은 0이 아니다. 만약 윈도우 클래스가 배경색 프러쉬없이 만들어진다면, 이 어플은 배경을 지울 책임이 있다. 뭔가 더 궁금하면 WNDCLASS구조체의 hbrBackground멤버 설명을 좀더 보면 이해가 될수도 있을껄?

rcPaint

A RECT structure that specifies the upper left and lower right corners of the rectangle in which the painting is requested, in device units relative to the upper-left corner of the client area.
그리기가 요청된 위 왼쪽, 아래, 오른쪽 코너들을 특정짓는 사각형(RECT) 구조. 디바이스 유닛에서는 클라이언트영역의 왼쪽위로 연결된다.

fRestore

Reserved; used internally by the system.
시스템 내부적으로 예약되어 있음. 건들지 마셈.

fIncUpdate

Reserved; used internally by the system.
시스템 내부적으로 예약되어 있음. 건들지 마셈.

rgbReserved

Reserved; used internally by the system.
시스템 내부적으로 예약되어 있음. 건들지 마셈.

 
자, 한마디로 말하자면, 알필요 없는 구조체네. 그저 BeginPaint를 사용하기 위한 전초전이라 생각해도 편할듯 싶다.
 
BeginPaint(HWND hWnd, LPPAINTSTRUCT lpPaint);
자, 그럼 이 코드를 보자면, BeginPaint다. 음.. GetDC, ReleaseDC와 마찬가지로
BeginPaint를 해주면, 반드시 EndPaint를 해줘야 한다.
자, 그리고 넘겨주는것에는 HWND와 LPPAINTSTRUCT. LP는 포인터를 의미한다. 따라서 저기 선언해 놓은 PAINTSTRUCT ps;의 주소값을 넘겨주면 된다.
 





















'Programming > Visual C++' 카테고리의 다른 글

출력4. 기타  (0) 2011.06.28
출력3. DrawText  (0) 2011.06.28
출력1. TextOut  (0) 2011.06.28
How to Programming by Unicode in windows programming - 3  (1) 2011.06.25
윈도우즈 프로그래밍에서 유니코드로 코딩법 2  (1) 2011.06.25
자, 이제 공부를 더 하기 위해서 음.. 뭔가에 대해서 깊숙히 들어가기 보단, 기록과 기억의 측면에서 포스팅을 해보기로 하자.
 이번에 공부할것은 출력. 몇일 전에 했던 LPCTSTR의 문자열 출력이 아닌 이상에야 뭐 그리 어려울건 없다. 따라서 윈도우즈 툴에서 출력하는 방법과 출력하는 함수들의 원형 및 사용방법을 익히면 될것이다.

시작해보자.

우선, 출력이라는 것에 있어서의 필요한것에 대해 알아보자, 일단 출력이란, 우리가 가장 간단하게 상상할수 있는 console style의 문자열이 있겠다. console style만 보더라도, 흰색글자에 검은색 바탕화면, 맨 왼쪽 위부터 출력이 차례대로 진행된다. 하지만 이거슨 도스시절의 이야기. 윈도우와 도스의 가장큰 차이점중 하나를 꼽으라면 뭐가 있을까? 일단 멀티테스킹이 있겠다. 그러면서 윈도우창이 바뀌고, 윈도우창이 움직일수도 있다.
 자, 그러면 두개의 프로그램을 실행중에 있는데 한나의 프로그램에서 출력하고 있던것은 다른 프로그램이 활성화가 된다면 비활성화가 된 프로그램과 같이 보이지 않아야하고 다시 그 프로그램을 활성화 시킨다면 다시 보여져야 한다.
 그리고 윈도우 창을 움직인다면 그 출력이 따라서 움직여야 할것이다.
 그리고 도스시절부터 있을수 있었던 이야기인, 함수의 원형에 무엇이 들어가야 하는가?에 대한 문제이다. 일단, 글자의 시작위치가 들어갈수 있겠다. 도스에서는 그냥저냥 첫줄부터 했지만, 윈도우에서는 어디서부터라도 글자나 그림의 위치가 실행이 가능하다. 글자의 크기, 폰트, 색깔, 등등등 여러가지가 있다. 음... 하지만 정작 생각을 해보면, 시작위치, 프로그램의 핸들만 알려주면 그냥 디폴트 정해주면 안되? 라고 해서 만든것이 DC(Device Context)다. 이 DC에 기타 여러가지의 기능들을 디폴트로 해놓든가 따로 수정을 해주면 알아서 해줄수 있도록 편하게 만들어 놓았다.

자, 그럼 간단한 코드를 보도록 하겠다.

LRESULT CALLBACK WndProc(HWND, hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
HDC hdc;

switch(iMessage)
{
case WM_DESTROY: PostQuitMessage(0);return 0;
case WM_LBUTTONDOWN:
hdc=GetDC(hWnd);
TextOut(hdc,100,100,Text("Beautiful Korea"),15);
ReleaseDC(hWnd,hdc);
return 0;  
}
return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}

자, 여기서 중요한건, HDC와 GetDC, TextOut, ReleaseDC
뭐, 다중요하네 ㅋㅋ

일단 HDC는 DC의 식별번호(Handle)를 담을수 있는 거고, GetDC는 hWnd를 GetDC 함수에 넘겨줘서 리턴값을 hdc로 대입한다. 핸들은 카테고리처럼 프로그램마다 지정되는거니까 :)
자, 그리고 TextOut을 보기전에 오선 ReleaseDC를 보자하면, 이거슨 생성자와 소멸자, 아니, new와 delete의 관계에 가깝다. 반드시 해줘야한다는 것이지.
 자, 그러면 Text Out을 보도록 하자, TextOut의 경우는
 TextOut(HDC hdc, int x, int y, LPCWSTR lpString, int c);
요롷게 된다. 맨처음은, dc를 사용할 핸들값이겠고, x,y는 출력할 xy의 좌표이다. 이 xy좌표의 원점. 즉, 0,0은 메뉴 부분(non-client area)를 제외한 가장 왼쪽 위부터 시작된다.따라서 오른쪽으로 1만큼 아래로 1만큼 움직이려면, 1, -1이 되는것이다.
 그리고 나오는 LPCWSTR은 물론 LPCTSTR도 사용할수 있으며 출력할 값을 나타낸다.
 그리고 그 뒤에오는 int c는 lpString의 최대길이를 나타낸다.
 만약 마지막의 이것을 쓰기 귀찮은 경우에는

void MyTextOut(HDC hdc, int x, int y, LPCTSTR Text)
{TextOut(hdc,x,y,Text,lstrlen(Text));}

이것과 같이 마지막을 lstrlen(Text)를 해줘서 없애고 MyTextOut이란 함수를 만들면 된다.


그리고 아래는 좌표에 따른 정렬방법.

SetTextAlign(HDC hdc, UINT align);

align::
TA_TOP 지정한 좌표가 상단 좌표
TA_BOTTOM  " 하단 좌표
TA_CENTER   " 중앙 좌표
TA_LEFT " 왼쪽 자좌표
TA_RIGHT " 오른쪽 좌표.
TAUPDATECP  지정한 좌표 대신CP(Current Position)를 사용하여 문자열 출력후에 CP를 저장하고 다음에 출력할 경우 그 CP 다음에 출력한다.
TA_NOUPDATECP CP를 사용하지 않고 지정한 좌표를 사용하며 CP를 변경하지 않는다.

보통 CP는 TA_LEFT|TA_TOP 에 Default로 있다가 어느 한 Text아웃이 발생하면 그 Text의 끝에 붙는다.
따라서 만약 이 CP를 옮기고 싶지 않을때는 TA_NOUPDATECP를 해주어야 한다.

그리고 중간에 변수를 넣을 경우에는 wsprintf나 sprintf를 사용해주면 된다.
wsprintf(LPWSTR, LPWSTR,...);
이것인데, 처음에는 LPWSTR은 버퍼.
그다음 LPWSTR은 버퍼에 넣을 서식이고, ... 는 인수값들이다.


How to Programming by Unicode in windows programming - 3

자, 이제 실전이다. 뭐, 다들 책이나 기본적인 윈도우즈 프레임 툴은 어떻게 짜는지 알것이다.

내가 배운것은

#include <windows.h>

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

 return (int)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));
}

요코드다. 자, 그렇다면 유니코드를 해놓고 비주얼 스튜디오 2010에서 돌리면 어디가 에러가 나는가?
바로 hWnd=CreateWindow 요 줄에 있는 두번째 배개변수 lpszClass가 문제다. 왜냐하면 윈도우즈 함수에서는 분명히 유니코드를 이용하겠다고 했는데 이 lpszClass는 아직 유니코드가 아닌갑다. 그래서 나는 이 lpszClass의 유니코드명이 뭔지 한참이나 찾았으나 인터넷에는 도저히 나와있지 않다. 왜냐하면!! lpszClass는 내가 코드안에서 생성해준 변수명이잖아-_ -;;;;

자, 5번줄을 보면
LPCSTR lpszClass="First";
요게 있었어..
복습을 해보자.
LPCSTR은 Const하고 long pointer하고 str인것이다. 유니코드의 흔적조차 없다.
그러면 어떻게 해야할까?

그거야 바로 LPC와 STR사이에
W나 T를 넣어주면 된다. 다른 코드들과의 이식성을 위해 일부러 연습을 위해서라도 LPCTSTR을 사용해주기로 한다.
자, 이제 해결?!

5번줄만 바꿔서
LPCTSTR lpszClass="First";
자, 디버깅 해보자.

으악! 에러다. 에러다. 에러다. ㅠㅜㅠ 아 도대체 왜!!!
자, 읽어보자면
error C2440: '초기화 중' : 'const char [6]'에서 'LPCTSTR'(으)로 변환할 수 없습니다.
라고 뜬다.
즉슨, 그럼 "First" 가 5자리+NULL의 문자열 상수일테고 왼쪽은 LPCTSTR이다.
음.. LPCTSTR은 틀릴리가 없고, 그럼 const char [6]이 틀렸다는 건데.
일단 내가 짠것은 유니코드니 wchar_t const * != const char[6] 라는 소리겠지.
틀린말은 아니다, "First"는 분명한 문자열 상수고 그것을 다른 자료형에 넣을순 없는거니까.
자, 그러면 형변환이다!

자, 그럼 유니코드 안에서 LPCTSTR == wchar_t const * 이니까
LPCTSTR lpszClass == (wchar_t const *)"First";

라고 고 치고 해보자.... 와우!!! 디버깅 에러 없고, 컴파일 제대로 되..... 었지만 출력물이 이상하다. 무슨 이상한 한자하고 잡스러운게 많이 나온다. ㅠㅠㅠㅠㅠㅠ 앙데 ㅠㅠ

음... 이것의 이유는 찾아본 결과 일단, 강제 형변환에 있다. 일반적인 char 형과 유니코드의 공간 할당 크기가 다르고 이것을 강제적으로 형변환을 할때 그 할당공간 크기 차이의 빈자리를 쓰레기값이 들어가 쓰레기 글자가 나오게 되는 것이다.

 그래서 자연스레 형변환을 해주는 접두어가 있었으니 그거슨 바로 L이다.
 라이토는 아니고, 음... 정확히 찾아보진 못했지만. L이다.

 사용방법은. 그냥 일반 접두어처럼 사용하면 된다.
 
LPCTSTR lpszClass=L"First";

요롷게 하면 디버깅도 되고 컴파일도 되고 실행도 된다. 제대로 출력도 된다.
하지만 이렇게 하면 사실 LPCTSTR을 쓴 의미가 없어진다. 왜냐하면 이 L은 유니코드형으로의 형변환이다. 선택적 형변환을 하기 위해선 TEXT()함수를 사용해야한다. 이 TEXT한수를 사용할 경우엔 이것도 T와 마찬가지로 알아서 유니코드면 유니코드, 일반 ANSI면 일반 char로 알아서 변환한다.

다시 말해

LPCTSTR lpszClass=TEXT("First");

가 최종 답안이라는거지 :)
아.. 여기까지 달려오시느라 참 수고 많으셨고(다 읽었을리는 없겠지만)
최종 코드를 공개하겠다.(거창하진 않지만)

아래 코드로 컴파일 하여 실행한다면 유니코드든 멀티바이트든 에러없이 실행 된다:)

#include <windows.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hInst;
LPCTSTR lpszClass=TEXT("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;
 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);
 
 //MessageBox(hWnd,L"하이",TEXT("Test"),NULL);

 while(GetMessage(&Message,NULL,0,0))
 {
  TranslateMessage(&Message);
  DispatchMessage(&Message);
 }

 return (int)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));
}


+ Recent posts