posted by nsakura 2010. 7. 15. 15:40


윈도우즈는 세가지 동적 연결 라이브러리 (DLL)로 구성되어있다.

KERNEL(시스템적인)

USER(유저레벨)

GDI(그래픽)

DC(Device Context)는 출력에 필요한 모든 정보를 가지는 데이터 구조체

GDI 모듈에 의해 관리.

자 이제 예제를 보자

문자열 출력하는 프로그램이다.

#include <windows.h>

LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);

HINSTANCE g_hInst;

LPCTSTR lpszClass=TEXT("GFP NSAKURA OUTPUT");

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd){

 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=lpszClass;
 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,nShowCmd);

 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){
 HDC hdc;
 
 switch(iMessage){
 case WM_DESTROY:
  PostQuitMessage(0);
  return 0;
 case WM_LBUTTONDOWN:
  hdc=GetDC(hWnd);
  TextOut(hdc,100,100,TEXT("Welcome to GFP"),14);
  ReleaseDC(hWnd,hdc);
 }
 return (DefWindowProc(hWnd,iMessage,wParam,lParam));
}

WinMain같은경우 그전 예제 프로그램 써도 됩니다.



그러나 문제가 있다. 화면크기를 변경 하거나 만들어진 프로그램 위에 또달은 임의의 프로그램이 올라가있는경우.. 버그가 생긴다.

이유는 운영체제가 현 윈도의 화면을 보관 및 복구를 해주지 않기 때문이다.

윈도우즈는 가려졌던 윈도우의 화면을 보관 및 복구하는 채김을 지지 않는다 지워진 화면을 복구하는 책임은 전적으로 프로그램 자신에게

있다.

그렇다면 어떻게 해야할까?

예제를 수정하여 화면이 지워지면 다시 복구하도록 만들어야 하지 않겠는가?

지금 현재는 마우스 왼쪽 버튼을 누르면 문자열이 출력하게 하는게 아니라 화면이 지워질때마다 문자열을 출력하는건 어떨까? 운영체제는

개별 윈도의 화면을 보관해 주지는 않지만 대신 윈도의 일부가 지워졌다는 사실을 프로그램으로 즉각 알려줄수는 있다.그 방법은

WM_PAIN 메세지를 주는것이다.

말그대로

운영체제가

"야 너 일부 날랐어 다시 그려" 라고 알려주는 것이다.

그럼 코드를 바꿔보자.

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("Welcome to GFP"),14);
  ReleaseDC(hWnd,hdc);
 }
 return (DefWindowProc(hWnd,iMessage,wParam,lParam));
}

이 부분을
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("Welcome to GFP"),14);
  EndPaint(hWnd,&ps);
  return 0;
 }
 return (DefWindowProc(hWnd,iMessage,wParam,lParam));
}

이렇게 바꾸면 화면창을 크기를 수정하거나 프로그램위에 다른 창이 떠도 글씨가 날라가지 않을 것 이다.

다음 내용은 좀 쉬다가 적겠습니다.

조금 쉬었으니까. 다시 적겠습니다.

DC를 얻는 방법

DC를 얻는 방법은 두가지가 있습니다.

한가지는 GetDC함수를 이용해서 DC를 얻고 사용후 ReleaseDC로 해제하는 방법.

  hdc=GetDC(hWnd);

  TextOut(hdc,100,100,TEXT("Welcome to GFP"),14);

  ReleaseDC(hWnd,hdc);

GetDC는 hWnd가 가리키는 윈도우에 적당한 DC를 만들어 그 핸들을 리턴한다.

GetDC에 의해 얻어진 핸들은 모두 사용하면 ReleaseDC로 함수로 해제해야 한다.

즉 메모리를 사용했으면 다시 반납해줘야하죠. 이건 기본.

왜냐고? 빌렸으면 돌려줘야지!

두번째 방법은 WM_PAINT 메세지 처리 루틴에서만 사용 가능하다.

GetDC말고 BeginPaint 함수로 얻으며 핸들을 해제할때는 EndPaint 함수를 사용한다.

HDC BeginPaint(HWND hwnd, LPPAINTSTRUCT lpPaint);

BOOL EndPaint(HWND hWnd, CONST PAINTSTRUCT lpPaint);

[다시 말하지만. WM_PAINT외에는 사용할수없다.]

BeginPaint 함수는 윈도우 핸들 외에도 페인트 정보 구조체를 인수로 요구한다.

그럼 페인트 정보 구조체(PAINTSTRUCT)를 살펴보죠.

typedef struct tagPAINTSTRUCT{
 HDC hdc;
 BOOL fErase;
 RECT rcPaint;
 BOOL fRestore;
 BOOL flncUpdate;
 BYTE rgbReserved[16];
}PAINTSTRUCT

앞에서 세 맴버는 사용자가 사용하지만 나머지 맴버는 윈두우즈가 내부적으로 사용함으로 건들지말자.

그럼 사용법을 보자
 
HDC hdc;
 PAINTSTRUCT ps;

case WM_PAINT:
  hdc=BeginPaint(hWnd,&ps);
  TextOut(hdc,100,100,TEXT("Welcome to GFP"),14);
  EndPaint(hWnd,&ps);
  return 0;

이런식으로 사용을 한다.

자 그럼 왜 WM_PAINT에서만 사용하는 것을 왜 설명하는가? 하시는 분 있을것이다.

PAINTSTRUCT 구조체에는 그리기 속도를 비약적으로 향상시킬수 있는 정보 들이 있다. 이 정보를 활용하는 방법은 나중에 언급하겠다.

그리고 GetDC와 ReleaseDC와 한짝이다.

BeginPaint와 EndPaint와 짝이다.

malloc/free와 new/delete를 혼합해서 사용할 수 없는 것과 같은 이치

문자열의 출력

c언어를 배우신분이라면 출력하면 생각나는 함수는 printf일것이다.

이와같이 winapi도 존재가 하는데 그것은 textout 함수이다.

BOOL TextOut(HDC hdc, int nXStart, int nYStart, LPCTSTR lpString, int cbString);

무엇인가를 출력하는 모든 함수들의 첫번 째 인수는 항상 hdc이다. nXStart,nYStart는  문자열이 출력되는 좌표이다.

lpString은 문자열을 담고있는 포인터 마지막 인수는 [cbString]은 문자열의 길이.

TextOut(hdc,100,100,TEXT("Welcome to GFP"),14);

하지만 이렇게 할경우 14글자 이상인 문자열은 에로사항이 꽃필것이다 그래서 보통은

TCHAR *str=TEXT("Welcome to GFP");

TextOut(hdc,100,100,str,lstrlen(str));

이렇게 사용하는 경우가 많다.

일단 출력에 관한 설명은 끝이 났다.

부과적으로 문자열의 정렬 방법을 변경하는 함수도 있다.

UINT SetTextAlign(HDC hdc, UINT fMode);

인자 설명은 HDC는 굳이 설명할 필요가 없을 것고

fMode는 지정하는 정렬 정보에 따라 hdc의 정렬 상태를 변경 하며 이후부터 hdc를 참조하여 출력되는 모든 문자열을

이 함수가 지정한 정렬 상태를 따른다.

fMode값은 다음과 같이 정의되어 있다. 역시 or로 연결가능 하다..

 TA_TOP  지정한 좌표가 상단 좌표가 된다.
 TA_BOTTOM  지정한 좌표가 하단 좌표가 된다
 TA_CENTER  지정한 좌표가 수평 중앙 좌표가 된다.
 TA_LEFT  지정한 좌표가 수평 왼쪽 좌표가 된다.
 TA_RIGHT  지정한 좌표가 수평 오른쪽 좌표가 된다.
 TA_UPDATECP  지정한 좌표대신 CP를 사용하며 문자열 출력 후에 CP를 변경한다.
 TA_NOUPDATECP  CP를 사용하지 않고 지정한 좌표를 사용하며 CP를 변경하지 않는다.

한번 실습햅보자 2개만 실습을 해보자.

 case WM_PAINT:
   hdc=BeginPaint(hWnd,&ps);
   SetTextAlign(hdc,TA_CENTER);
   TextOut(hdc,200,60,TEXT("어서오세요"),10);
   TextOut(hdc,200,80,TEXT("이곳은?"),7);
   TextOut(hdc,200,100,TEXT("GFP 입니다."),11);
   EndPaint(hWnd,&ps);
   return 0;


200을 기준으로 가운데 정렬이 된다.

  case WM_PAINT:
   hdc=BeginPaint(hWnd,&ps);
   SetTextAlign(hdc,TA_UPDATECP);
   TextOut(hdc,200,60,TEXT("어서오세요"),10);
   TextOut(hdc,200,80,TEXT("이곳은?"),7);
   TextOut(hdc,200,100,TEXT("GFP 입니다."),11);
   EndPaint(hWnd,&ps);
   return 0;


지정한 좌표를 무시하고 무조건 cp위치에서 시작한다.

c언어를 하셨던 분들은

printf("너 점수 %d \n",score);

그러면 저런식으로 어떻게 출력하는가?

아쉽게도

TextOut은 무조건 문자열만 처리가 가능하다

그래서 wsprintf나 sprintf와 같은 함수로 서식화 하여 사용해야한다.

TCHAR str[20];
int score=80;
wsprintf(str,TEXT("너 점수 %d"),score);
TextOut(hdc,10,10,str,lstrlen(str));

이렇게 해보자.


LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam,LPARAM lParam){
 HDC hdc;
 PAINTSTRUCT ps;
  TCHAR str[20];
 int score=80;

 switch(iMessage){
  case WM_DESTROY:
   PostQuitMessage(0);
   return 0;
  case WM_PAINT:
   hdc=BeginPaint(hWnd,&ps);
   wsprintf(str,TEXT("너 점수 %d"),score);
   TextOut(hdc,10,10,str,lstrlen(str));
   EndPaint(hWnd,&ps);
   return 0;
 }
 return (DefWindowProc(hWnd,iMessage,wParam,lParam));
}

'Computer > Win API' 카테고리의 다른 글

WinApi MessageBox  (0) 2010.07.17
WinApi 여러가지 출력  (0) 2010.07.16
WinApi DrawText  (0) 2010.07.15
WinApi 예제1의 확장  (0) 2010.07.15
WinApi 예제 1  (1) 2010.07.15