.시작 프로젝트

ApiDraw01 기능이 간단하기 때문에 소스도 아주 짧게 작성되어 있다. 메시지 처리 함수가 switch 문으로 되어 있어 어떤 메시지들이 어떻게 처리되는지를 한눈에 알아 있어서 좋기는 하지만 이런 방식은 확장에 불리하다. 앞으로 많은 메시지들을 처리하게 것인데 코드들이 모두 WndProc이나 CanvasProc 작성된다면 함수가 너무 길어져 소스를 편집하기 어려워진다. 뿐만 아니라 메시지를 처리하기 위한 지역 변수들도 많이 필요해지는데 변수들이 모두 메시지 처리 함수의 지역 변수가 된다면 속도도 많이 느려질 것이다.

아주 간단한 예제 수준에서는 switch문도 나쁘지 않지만 계속 확장할 프로젝트라면 메시지별로 전담 함수를 하나씩 만드는 것이 좋다. 이런 개념이 바로 메시지 크래커인데 굳이 메시지 크래커까지 동원하지 않더라도 함수만 분리해도 상당히 편리해진다. ApiDraw02 프로젝트는 ApiDraw01 프로젝트와 동일한 기능을 유지하면서 프로젝트의 형태만 변경했다. 전담 함수를 두면 함수가 너무 길어지지 않아서 좋고 지역 변수도 자유롭게 사용할 있다.

그리고 실습의 편의를 위해 앞으로 처리하게될 메시지들에 대해서도 미리 전담 함수를 작성해 두기로 하자. 메인 윈도우의 메시지 처리 함수는 Main_On으로 시작되며 캔버스의 메시지 처리 함수는 전치사 On으로만 시작하도록 했다. 작업은 누가 해도 동일하기 때문에 조금이라도 실습 시간을 벌기 위해 내가 미리 작성해 놓았다. 여러분들은 부지런히 논리만 생각하고 실습만 하면 된다. 전체 소스는 다음과 같다.

 

#include <windows.h>

#include "resource.h"

 

// 타입 전역 변수

HINSTANCE g_hInst;

HWND hWndMain;

LPCTSTR lpszClass=TEXT("ApiDraw");

HWND hCanvas;

enum DTool { DT_SELECT, DT_LINE, DT_ELLIPSE, DT_RECTANGLE, DT_TEXT,

   DT_BITMAP, DT_META };

enum DMode { DM_NONE, DM_DRAW, DM_MOVE, DM_SIZE };

DTool NowTool;

DMode DragMode;

int sx,sy,oldx,oldy;

 

// 함수 원형

LRESULT Main_OnCreate(HWND hWnd,WPARAM wParam,LPARAM lParam);

LRESULT Main_OnDestroy(HWND hWnd,WPARAM wParam,LPARAM lParam);

LRESULT Main_OnSize(HWND hWnd,WPARAM wParam,LPARAM lParam);

LRESULT Main_OnCommand(HWND hWnd,WPARAM wParam,LPARAM lParam);

LRESULT Main_OnInitMenu(HWND hWnd,WPARAM wParam,LPARAM lParam);

LRESULT Main_OnSetFocus(HWND hWnd,WPARAM wParam,LPARAM lParam);

 

LRESULT OnCreate(HWND hWnd,WPARAM wParam,LPARAM lParam);

LRESULT OnDestroy(HWND hWnd,WPARAM wParam,LPARAM lParam);

LRESULT OnPaint(HWND hWnd,WPARAM wParam,LPARAM lParam);

LRESULT OnCommand(HWND hWnd,WPARAM wParam,LPARAM lParam);

LRESULT OnLButtonDown(HWND hWnd,WPARAM wParam,LPARAM lParam);

LRESULT OnMouseMove(HWND hWnd,WPARAM wParam,LPARAM lParam);

LRESULT OnLButtonUp(HWND hWnd,WPARAM wParam,LPARAM lParam);

LRESULT OnKeyDown(HWND hWnd,WPARAM wParam,LPARAM lParam);

LRESULT OnSetCursor(HWND hWnd,WPARAM wParam,LPARAM lParam);

LRESULT OnSize(HWND hWnd,WPARAM wParam,LPARAM lParam);

LRESULT OnContextMenu(HWND hWnd,WPARAM wParam,LPARAM lParam);

LRESULT OnLButtonDblclk(HWND hWnd,WPARAM wParam,LPARAM lParam);

 

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

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

 

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=NULL;

   WndClass.hCursor=LoadCursor(NULL,IDC_ARROW);

   WndClass.hIcon=LoadIcon(hInstance,MAKEINTRESOURCE(IDI_APIDRAW));

   WndClass.hInstance=hInstance;

   WndClass.lpfnWndProc=WndProc;

   WndClass.lpszClassName=lpszClass;

   WndClass.lpszMenuName=MAKEINTRESOURCE(IDR_MENU1);

   WndClass.style=0;

   RegisterClass(&WndClass);

 

   WndClass.hbrBackground=(HBRUSH)(COLOR_WINDOW+1);

   WndClass.lpfnWndProc=CanvasProc;

   WndClass.lpszClassName="Canvas";

   WndClass.lpszMenuName=NULL;

   WndClass.style=CS_DBLCLKS;

   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,0,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_CREATE:

      return Main_OnCreate(hWnd,wParam,lParam);

   case WM_DESTROY:

      return Main_OnDestroy(hWnd,wParam,lParam);

   case WM_SIZE:

      return Main_OnSize(hWnd,wParam,lParam);

   case WM_COMMAND:

      return Main_OnCommand(hWnd,wParam,lParam);

   case WM_INITMENU:

      return Main_OnInitMenu(hWnd,wParam,lParam);

   case WM_SETFOCUS:

      return Main_OnSetFocus(hWnd,wParam,lParam);

   }

   return(DefWindowProc(hWnd,iMessage,wParam,lParam));

}

 

LRESULT Main_OnCreate(HWND hWnd,WPARAM wParam,LPARAM lParam)

{

   hWndMain=hWnd;

   hCanvas=CreateWindow("Canvas",NULL,WS_CHILD | WS_VISIBLE,

      0,0,0,0,  hWnd,(HMENU)0,g_hInst,NULL);

   return 0;

}

 

LRESULT Main_OnDestroy(HWND hWnd,WPARAM wParam,LPARAM lParam)

{

   PostQuitMessage(0);

   return 0;

}

 

LRESULT Main_OnSize(HWND hWnd,WPARAM wParam,LPARAM lParam)

{

   if (wParam != SIZE_MINIMIZED) {

      MoveWindow(hCanvas,0,0,LOWORD(lParam),HIWORD(lParam),TRUE);

   }

   return 0;

}

 

LRESULT Main_OnCommand(HWND hWnd,WPARAM wParam,LPARAM lParam)

{

   switch(LOWORD(wParam)) {

   case IDM_FILE_EXIT:

      DestroyWindow(hWnd);

      break;

   case IDM_SHAPE_LINE:

      NowTool=DT_LINE;

      break;

   case IDM_SHAPE_ELLIPSE:

      NowTool=DT_ELLIPSE;

      break;

   case IDM_SHAPE_RECTANGLE:

      NowTool=DT_RECTANGLE;

      break;

   }

   return 0;

}

 

LRESULT Main_OnInitMenu(HWND hWnd,WPARAM wParam,LPARAM lParam)

{

   return 0;

}

 

LRESULT Main_OnSetFocus(HWND hWnd,WPARAM wParam,LPARAM lParam)

{

   return 0;

}

 

// 캔버스 윈도우의 메시지 처리 함수

LRESULT CALLBACK CanvasProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)

{

   switch(iMessage) {

   case WM_CREATE:

      return OnCreate(hWnd,wParam,lParam);

   case WM_DESTROY:

      return OnDestroy(hWnd,wParam,lParam);

   case WM_PAINT:

      return OnPaint(hWnd,wParam,lParam);

   case WM_COMMAND:

      return OnCommand(hWnd,wParam,lParam);

   case WM_LBUTTONDOWN:

      return OnLButtonDown(hWnd,wParam,lParam);

   case WM_MOUSEMOVE:

      return OnMouseMove(hWnd,wParam,lParam);

   case WM_LBUTTONUP:

      return OnLButtonUp(hWnd,wParam,lParam);

   case WM_KEYDOWN:

      return OnKeyDown(hWnd,wParam,lParam);

   case WM_SETCURSOR:

      return OnSetCursor(hWnd,wParam,lParam);

   case WM_SIZE:

      return OnSize(hWnd,wParam,lParam);

   case WM_CONTEXTMENU:

      return OnContextMenu(hWnd,wParam,lParam);

   case WM_LBUTTONDBLCLK:

      return OnLButtonDblclk(hWnd,wParam,lParam);

   }

   return(DefWindowProc(hWnd,iMessage,wParam,lParam));

}

 

LRESULT OnCreate(HWND hWnd,WPARAM wParam,LPARAM lParam)

{

   NowTool=DT_LINE;

   DragMode=DM_NONE;

   return 0;

}

 

LRESULT OnDestroy(HWND hWnd,WPARAM wParam,LPARAM lParam)

{

   return 0;

}

 

LRESULT OnPaint(HWND hWnd,WPARAM wParam,LPARAM lParam)

{

   HDC hdc;

   PAINTSTRUCT ps;

 

   hdc=BeginPaint(hWnd, &ps);

   EndPaint(hWnd, &ps);

   return 0;

}

 

LRESULT OnCommand(HWND hWnd,WPARAM wParam,LPARAM lParam)

{

   return 0;

}

 

LRESULT OnLButtonDown(HWND hWnd,WPARAM wParam,LPARAM lParam)

{

   sx=LOWORD(lParam);

   sy=HIWORD(lParam);

   oldx=sx;

   oldy=sy;

   DragMode=DM_DRAW;

   SetCapture(hWnd);

   return 0;

}

 

LRESULT OnMouseMove(HWND hWnd,WPARAM wParam,LPARAM lParam)

{

   int ex,ey;

   HDC hdc;

 

   ex=(int)(short)LOWORD(lParam);

   ey=(int)(short)HIWORD(lParam);

   if (DragMode==DM_DRAW) {

      hdc=GetDC(hWnd);

      SetROP2(hdc,R2_NOTXORPEN);

      SelectObject(hdc,GetStockObject(NULL_BRUSH));

      switch (NowTool) {

      case DT_LINE:

         MoveToEx(hdc,sx,sy,NULL);

          LineTo(hdc,oldx,oldy);

          MoveToEx(hdc,sx,sy,NULL);

          LineTo(hdc,ex,ey);

          break;

      case DT_ELLIPSE:

          Ellipse(hdc,sx,sy,oldx,oldy);

          Ellipse(hdc,sx,sy,ex,ey);

          break;

      case DT_RECTANGLE:

          Rectangle(hdc,sx,sy,oldx,oldy);

          Rectangle(hdc,sx,sy,ex,ey);

          break;

      }

      oldx=ex;

      oldy=ey;

      ReleaseDC(hWnd,hdc);

   }

   return 0;

}

 

LRESULT OnLButtonUp(HWND hWnd,WPARAM wParam,LPARAM lParam)

{

   DragMode=DM_NONE;

   ReleaseCapture();

   return 0;

}

 

LRESULT OnKeyDown(HWND hWnd,WPARAM wParam,LPARAM lParam)

{

   return 0;

}

 

LRESULT OnSetCursor(HWND hWnd,WPARAM wParam,LPARAM lParam)

{

   return(DefWindowProc(hWnd,WM_SETCURSOR,wParam,lParam));

}

 

LRESULT OnSize(HWND hWnd,WPARAM wParam,LPARAM lParam)

{

   return 0;

}

 

LRESULT OnContextMenu(HWND hWnd,WPARAM wParam,LPARAM lParam)

{

   return 0;

}

 

LRESULT OnLButtonDblclk(HWND hWnd,WPARAM wParam,LPARAM lParam)

{

   return 0;

}

 

WndProc CanvasProc 전달된 메시지에 따라 전담 함수를 호출할 뿐이다. 메시지의 핸들러에 메시지에 반응하는 실제 코드들이 작성되어 있다. 시점에서 비어있는 핸들러들도 있는데 앞으로 핸들러에 코드를 채워 나갈 것이다.

ApiDraw02 프로젝트의 다른 특징은 실습에 필요한 리소스가 모두 작성되어 있다는 점이다. 메뉴나 대화상자 리소스도 누가 만들어도 비슷할 수밖에 없으므로 예비 동작을 취해 두었으며 실습중에 리소스를 가져다 쓰기만 하면 된다. 리소스 뷰를 열어 어떤 리소스들이 작성되어 있는지 구경해 보도록 하자. 예제를 실행하면 이미 완전한 메뉴가 작성되어 있다. 물론 아직 기능은 구현되어 있지 않다.

전역 변수의 목록에는 변화가 없지만 DTool 열거형과 DMode 열거형의 멤버가 추가되었다. 그리기 도구에 텍스트, 비트맵, 메타 파일 등이 있고 드래그 모드에는 이동, 크기 변경이 추가되었다. 멤버들도 앞으로의 실습에 사용될 것이다.

이후 실습을 때는 ApiDraw02 프로젝트를 시작 프로젝트로 사용하여 프로젝트의 사본으로부터 실습을 진행하면 된다. 프로젝트는 비주얼 C++ 6.0 2003에서 모두 있도록 되어 있으며 6.0 컴파일 결과가 Debug6, Release6 디렉토리에 저장되고 2003 Debug, Release 디렉토리에 저장된다.