2011년9월5일_winAPI_리소스, 메뉴만들기 예제



● 리소스(Resource)란?

코드의 논리와 무관한 데이터들로 메뉴, 비트맵, 액셀러레이터, 문자열, 아이콘, 커서등등을 말하고,
리소스들은 별도의 편집기로 만들어져 컴파일되며 링크시에 실행파일에 결합된다.

image

소스파일 *.cpp와 헤더파일 *.h를 컴파일하여 기계어 코드 *.obj를 만들고,
아이콘파일 *.ico와 그림파일 *.bmp와 리소스파일 *.rc를 컴파일하여 *.res를 만들어,
링크과정에서 exe파일(실행파일)에 결합됨.

★ 윈도우즈 프로그래밍이 도스에서와 구별되는 큰 차이점 중 하나가 바로 리소스가 코드와 분리되어 있다는 점이다.


● 리소스를 소스와 분리하였을 때의 장점

① 디자이너와 프로그래머가 분담하여 작업하기 편리하다.
② 소스와 리소스를 따로 컴파일하니 개발시간이 단축된다.
③ 만들어진 리소스를 다른 프로젝트에서 쉽게 재사용이 가능하다.
④ 리소스는 실행 중에 교체가 가능한 모듈로 상황에 따라 다른 형태의 리소스를 사용할 수 있다.

암튼 좋은 것이니 꼭 알아둬야 겠다.


● 메뉴 리소스 작성 과정

image

VS6.0을 실행한 뒤 Ctrl + N키를 눌러 새로운 프로젝트를 만든다.


image

프로젝트명은 Menu로 해야 하나 보고서 작성을 위해 새로운 프로젝트를 만드니 2를 붙였다.


image

당연히 빈 프로젝트 선택하고, 내용은 9월2일 금요일에 작성한 소스코드를 그대로 사용한다.




image image

http://sumanaki.tistory.com/188 <- 여기에 소스코드가 있다.
새로 생성된 프로젝트의 폴더에 복사한 뒤에 소스파일과 헤더파일을 등록한다. (팝업메뉴 - Add file to Folder)


image

Ctrl + N키를 눌러 새로운 파일을 생성하는데 Resource Script를 선택하여 Menu.rc파일을 만들자.



image 
 
새로운 창이 뜨고 Menu.rc폴더모양이 보인다.
왼쪽의 Workspace를 보니 리소스파일인데 소스파일폴더에 들어가 버렸다. 이상한데…


image image

rc파일 위에서 오른클릭하여 Insert를 선택하면 오른쪽과 같이 컴포넌트를 선택할 수 있는 창이 뜬다.
Menu를 선택하여 새로 추가하자.


imageimage 

메뉴를 편집할 수 있는 창이 뜬다.
키보드로 “File"이라고 치면 입력이 되고 속성창이 하나 더 뜬다.



image image

메뉴1과 2를 추가한다.
ID는 ID로 시작하고 FILE메뉴의 MENU1이라는 뜻에서 ID_FILE_MENU1이라고 한다. 메뉴2도 마찬가지로 한다.


image image

마지막으로 종료메뉴를 추가하고 ID는 ID_FILE_EXIT로 한다.
이번 프로젝트에 Menu.rc와 resource.h파일이 추가되었다.

image 

제대로 적용되었는지 확인하기 위해 Rebuild All을 한 후에 resource.h파일을 보면,

   1:  //{{NO_DEPENDENCIES}}
   2:  // Microsoft Developer Studio generated include file.
   3:  // Used by Menu.rc
   4:  //
   5:  #define IDR_MENU1                       101
   6:  #define ID_FILE_MENU1                   40001
   7:  #define ID_FILE_MENU2                   40002
   8:  #define ID_FILE_EXIT                    40003
   9:   
  10:  // Next default values for new objects
  11:  // 
  12:  #ifdef APSTUDIO_INVOKED
  13:  #ifndef APSTUDIO_READONLY_SYMBOLS
  14:  #define _APS_NEXT_RESOURCE_VALUE        102
  15:  #define _APS_NEXT_COMMAND_VALUE         40004
  16:  #define _APS_NEXT_CONTROL_VALUE         1000
  17:  #define _APS_NEXT_SYMED_VALUE           101
  18:  #endif
  19:  #endif

// Used by Menu.rc 아래에,
추가한 메뉴들에 대한 값들이 정의되어 있다.
MsgProc.cpp파일을 열어 OnCommand()의 내용을 넣자.

   1:  LRESULT OnCommand(HWND hWnd, WPARAM wParam, LPARAM lParam)
   2:  {
   3:      switch(LOWORD(wParam))
   4:      {
   5:      case ID_FILE_MENU1:
   6:          MessageBox(hWnd, TEXT("첫 번째 메뉴를 선택했습니다."), TEXT("Menu Demo"), MB_OK);
   7:          break;
   8:      case ID_FILE_MENU2:
   9:          MessageBox(hWnd, TEXT("두 번째 메뉴를 선택했습니다."), TEXT("Menu Demo"), MB_OK);
  10:          break;
  11:      case ID_FILE_EXIT:
  12:          DestroyWindow(hWnd);
  13:          break;
  14:      }
  15:   
  16:      return 0;
  17:  }



● 전체소스코드

○ main.cpp

   1: // 리소스.
   2: // 2011년 9월 5일 작성 
   3: #include <windows.h>        // 모든 API함수들의 원형과 사용하는 상수들이 정의되어 있음.
   4: #include "MsgProc.h"    //add
   5: #include "resource.h"    //자동으로 만들어지는 헤더파일.
   6:  
   7: LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);    //메시지처리함수
   8: HINSTANCE g_hlnst;    //핸들 인스턴스
   9: LPCTSTR lpszClass = TEXT("Menu");        //윈도우 제목으로 사용되는 const char * (문자열)
  10:  
  11: //add
  12: typedef struct MESSAGEMAP {
  13:     UINT iMessage;
  14:     LRESULT (*lpfnMsgProc)(HWND, WPARAM, LPARAM);
  15: } MESSAGEMAP;
  16:  
  17: //시작함수.
  18: int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdParam, int nCmdShow)
  19: {
  20:     HWND hWnd;
  21:     MSG Message;
  22:     WNDCLASS WndClass;
  23:     g_hlnst = hInstance;
  24:  
  25:     WndClass.cbClsExtra = 0;
  26:     WndClass.cbWndExtra = 0;
  27:     WndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
  28:     WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
  29:     WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  30:     WndClass.hInstance = hInstance;
  31:     WndClass.lpfnWndProc = WndProc;
  32:     WndClass.lpszClassName = lpszClass;
  33:     WndClass.lpszMenuName = MAKEINTRESOURCE(IDR_MENU1);
  34:     WndClass.style = CS_HREDRAW | CS_VREDRAW;
  35:     RegisterClass(&WndClass);
  36:  
  37:     
  38:     hWnd = CreateWindow(lpszClass, lpszClass, WS_OVERLAPPEDWINDOW,
  39:         CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,        
  40:         NULL, (HMENU)NULL, hInstance, NULL);
  41:     
  42:     ShowWindow(hWnd, nCmdShow);
  43:  
  44:     while(GetMessage(&Message, NULL, 0, 0)) {
  45:         TranslateMessage(&Message);
  46:         DispatchMessage(&Message);
  47:     }
  48:  
  49:     return (int)Message.wParam;
  50: }
  51:  
  52: //운영체제가 호출하는 CALLBACK함수 (메시지 처리)
  53: LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
  54: {
  55:     //add
  56:     int i;
  57:  
  58:     static MESSAGEMAP MessageMaps[] = {
  59:         {WM_CREATE, OnCreate},
  60:         {WM_COMMAND, OnCommand},
  61:         {WM_LBUTTONDOWN, OnLButtonDown},
  62:         {WM_PAINT, OnPaint},
  63:         {WM_DESTROY, OnDestroy}
  64:     };
  65:  
  66:     for(i = 0 ; i < sizeof(MessageMaps) / sizeof(MessageMaps[0]) ; ++i)
  67:     {
  68:         if(MessageMaps[i].iMessage == iMessage)
  69:         {
  70:             return (*MessageMaps[i].lpfnMsgProc)(hWnd, wParam, lParam);
  71:         }
  72:     }
  73:     /////////////////////////////////
  74:  
  75:     return(DefWindowProc(hWnd, iMessage, wParam, lParam));
  76: }


○ MsgProc.h

   1: //MsgProc.h
   2: #ifndef __MSGPROC_H_
   3: #define    __MSGPROC_H_
   4:  
   5: #include <windows.h>
   6:  
   7: LRESULT OnCreate(HWND hWnd, WPARAM wParam, LPARAM lParam);
   8: LRESULT OnCommand(HWND hWnd, WPARAM wParam, LPARAM lParam);
   9: LRESULT OnLButtonDown(HWND hWnd, WPARAM wParam, LPARAM lParam);
  10: LRESULT OnPaint(HWND hWnd, WPARAM wParam, LPARAM lParam);
  11: LRESULT OnDestroy(HWND hWnd, WPARAM wParam, LPARAM lParam);
  12:  
  13: extern HINSTANCE g_hInst;
  14:  
  15: #endif


○ MsgProc.cpp

   1: //MsgProc.cpp
   2:  
   3: #include "MsgProc.h"
   4: #include "resource.h"
   5:  
   6: LRESULT OnCreate(HWND hWnd, WPARAM wParam, LPARAM lParam)
   7: {
   8:     return 0;
   9: }
  10:  
  11: LRESULT OnCommand(HWND hWnd, WPARAM wParam, LPARAM lParam)
  12: {
  13:     switch(LOWORD(wParam))
  14:     {
  15:     case ID_FILE_MENU1:
  16:         MessageBox(hWnd, TEXT("첫 번째 메뉴를 선택했습니다."), TEXT("Menu Demo"), MB_OK);
  17:         break;
  18:     case ID_FILE_MENU2:
  19:         MessageBox(hWnd, TEXT("두 번째 메뉴를 선택했습니다."), TEXT("Menu Demo"), MB_OK);
  20:         break;
  21:     case ID_FILE_EXIT:
  22:         DestroyWindow(hWnd);
  23:         break;
  24:     }
  25:  
  26:     return 0;
  27: }
  28:  
  29: LRESULT OnLButtonDown(HWND hWnd, WPARAM wParam, LPARAM lParam)
  30: {
  31:  
  32:     return 0;
  33: }
  34:  
  35: LRESULT OnPaint(HWND hWnd, WPARAM wParam, LPARAM lParam)
  36: {
  37:     HDC hdc;
  38:     PAINTSTRUCT ps;
  39:  
  40:     hdc = BeginPaint(hWnd, &ps);
  41:  
  42:     EndPaint(hWnd, &ps);
  43:     return 0;
  44: }
  45:  
  46: LRESULT OnDestroy(HWND hWnd, WPARAM wParam, LPARAM lParam)
  47: {
  48:     PostQuitMessage(0);
  49:     return 0;
  50: }
  51:  



● 실행결과

imageimage  
파일메뉴의 Menu1을 선택하여 클릭하면 메시지박스가 하나 뜨고,
”첫 번째 메뉴를 선택했습니다”는 확인메시지를 보여 준다.


imageimage 

파일메뉴의 Menu2을 선택하여 클릭하면 메시지박스가 하나 뜨고,
”두 번째 메뉴를 선택했습니다”는 확인메시지를 보여 준다.

image

마지막으로 Exit를 선택하면 DestroyWindow()가 호출되어 현재 윈도우가 파괴되고 프로그램이 종료된다.