Notice
Recent Posts
Recent Comments
Link
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

codingfarm

5. 리소스 - 메뉴 본문

Windows/윈도우즈 API

5. 리소스 - 메뉴

scarecrow1992 2021. 5. 1. 19:09

0. 메뉴

  • 윈도우즈용 프로그램이 제공하는 가장 표준적인 UI
  • 구조와 기능, 내부적인 운용 방법은 간단치 않지만, 개발툴을 활용하면 비교적 쉽게 구현 가능하다.

 

 

프로젝트 내의 리소스파일 폴더를 우클릭하고 새항목을 클릭한다

 

좌측의 리소스 탭에서 리소스 파일(rc)을 선택한다.

 

솔루션 탐색기에서 생성된 rc파일을 볼 수 있으며, 리소스 뷰에서 프로젝트에 포함되어 있는 리소스들의 목록을 계층적으로 보여준다.

 

리소스뷰의 Menu.rc 폴더를 우클릭하여 Menu 리소스를 추가해본다.

 

솔루션 탐색기 툴바에서 Menu.rc와 resource.h가 추가되었으며

 

리소스 뷰 툴바에서 메뉴 리소스가 추가된것을 볼 수 있다.

메뉴 리소스를 더블클릭하여 파일을 열면 작업영역 메뉴창의 모습을 볼 수 있다.

 

 

작업영역을 아래처럼 수정한다.

Menu2를 선택하면 우측하단에서 선택한 항목의 속성을 볼 수 있다.

ID가 활성화 되어있는데, 이는 최말단의 항목에서만 설정 가능하다.

 

가령 Menu1은 하위에 다른 항목들이 더 있으므로 속성창에서 ID의 기입이 불가능하다.

 

 

 

이제 솔루션 탐색기의 resource.h에 대해 살펴보자

resource.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++에서 생성한 포함 파일입니다.
// Menu.rc에서 사용되고 있습니다.
//
#define IDR_MENU1                       101
#define ID_FILE_MENU1                   40001
#define ID_FILE_MENU2                   40002
#define ID_FILE_EXIT                    40003
#define ID_MENU1_ADD                    40004
#define ID_MENU1_CONFIG                 40005
 
// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        102
#define _APS_NEXT_COMMAND_VALUE         40006
#define _APS_NEXT_CONTROL_VALUE         1001
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif
 
cs

이전에 메뉴의 작업영역에서 생성했던 각 메뉴 항목들에 대응되는 ID가 자동으로 설정된것을 볼 수 있다.

이제 resource.h를 포함하면 메뉴 리소스와 연동하여 코드를 작성할 수 있다.

 

source.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <windows.h>
#include "resource.h"
 
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hInst;
LPCTSTR lpszClass = TEXT("Menu");
 
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)WndProc;
    WndClass.lpszClassName = lpszClass;
    WndClass.lpszMenuName = MAKEINTRESOURCE(IDR_MENU1);
    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, 000)) {
        TranslateMessage(&Message);
        DispatchMessage(&Message);
    }
    return Message.wParam;
}
 
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
    switch (iMessage) {
    case WM_COMMAND:
        switch (LOWORD(wParam)) {
        case ID_FILE_MENU1:
            MessageBox(hWnd, TEXT("첫 번째 메뉴를 선택했습니다."), TEXT("Menu Demo"), MB_OK);
            break;
        case ID_FILE_MENU2:
            MessageBox(hWnd, TEXT("두 번째 메뉴를 선택했습니다."), TEXT("Menu Demo"), MB_OK);
            break;
        case ID_FILE_EXIT:
            DestroyWindow(hWnd);
            break;
        }
        return 0;
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}
 
cs

이전에 확인했듯이 Menu1은 최말단 항목이 아니므로 자체적으로 할당된 ID가 없다.

그러므로 윈도우창에서 클릭하더라도 아무 효과도 없다

 

 

 

resource.h는 리소스 편집기가 리소스 편집시에 작성하며 리소스를 편집하면 자동으로 같이 수정되므로, 사용자가 편집할 필요가 없다. 이 예제프로젝트에서는 Menu.rc를 만들면 생성되고, 내부의 IDR_MENU1을 수정하면 내부 코드도 수정된다.

source.cpp는 window를 띄우기위한 기본적인 코드지만 몇가지 추가사항들이 있다.

2 : 메뉴 항목의 ID는 정수로 정의하되 사람이 일일이 정수값을 기억하기 곤란하므로 매크로 상수를 사용하여 접근할 수 있도록 resource.h를 포함함

25 : WndClass의 lpszMenuName 멤버에 우리가 작성한 메뉴 이름인 IDR__MENU1을 대입함, 숫자로 정의된 메뉴 이름을 문자열 형태로 바꾸기 위해 MAKEINTRESOURCE 매크로를 사용함

여기까지 작성하고 프로그램을 실행해보면 메뉴가 활성화 된것을 볼 수 있다.

 

 

 

WM_COMMAND

  • 메시지 전송 조건
    • 메뉴 항목 선택, 엑셀러 레이터 누름, 버튼, 에디트 박스 등의 컨트롤
  • 메시지 정보
설명
lParam 통지 메시지를 발생시킨 컨트롤의 윈도우 핸들
LOWORD(wParam) 메뉴나 액셀러레이터, 컨트롤의 ID
HIWORD(wParam) 컨트롤이 보내는 통지 메시지,
메뉴가 선택된 경우 : 0
액셀러레이터가 선택된 경우 : 1

 

 

 

MAKEINTRESOURCE

예제의 24번째 줄을 보면 정수 IDR_MENU1을 문자열로 치환하기위해 MAKEINTRESOURCE라는 매크로 함수를 사용한다.

가령 IDR_MENU1로 이름을 지정하면 resource.h에 정수형의 매크로로 정의하고 사용되지 않는 정수를 배정한다. 이 정수 타입의 리소스 ID를 문자열 포인터에 대입할 수 없으므로 적당히 캐스팅 해야하는데 이 캐스팅을 대신하는 매크로가 MAKEINTRESOURCE이다.

매크로함수는 아래와 같고, WinUser.h에 선언되어있다.

1
2
3
4
#define MAKEINTRESOURCEW(i) ((LPWSTR)((ULONG_PTR)((WORD)(i))))
#ifdef UNICODE
#define MAKEINTRESOURCE  MAKEINTRESOURCEW
#else
cs

간단히 기술하면 결국 (TCHAR *)IDR_MENU1과 같다. 즉, 주소 101에 지정된 문자열이 전달되는 셈이다.

 

 

자동화 편집기를 쓰는 요즘엔 리소스에 이름을 붙이는 경우가 드물기에 리소스에 이름을 직저 붙이는 경우는 드물지만 아래처럼 직접 지정도 가능하다.

WndClass.lpszMenuName = TEXT("MyMenu");

문자열이므로 겹따옴표를 붙여줘야한다.

위처럼 IDR_MENU1의 속성창의 ID를 수정해주고, 25번째 줄의 코드도 수정해주면 메뉴가 이상없이 작동하는것을 확인할 수 있다.

 

'Windows > 윈도우즈 API' 카테고리의 다른 글

5. 리소스 - 문자열 테이블(string table)  (0) 2021.05.01
5. 리소스 - 액셀러레이터(Accelerator)  (0) 2021.05.01
5. 리소스  (0) 2021.05.01
4. 윈도우 관리 메시지  (0) 2021.04.29
4. 타이머, 콜백함수  (0) 2021.01.10
Comments