익스플로러나 오피스 등을 사용하다 보면 메뉴를 눌러서 해당 기능 선택할 때 가 있습니다. 그럼 그 메뉴들은 어떻게 만들어지는 걸까요? 오늘은 그 메뉴를 생성하는 방법에 대해서 알아 보려고 합니다.
윈도우 프로그래밍을 C# 윈폼으로만 만들어본 저로써는 어떻게 만드는지 전혀 감을 못잡았었죠. 그래서 책을 보는 순간 C#과는 많이 틀리다는걸 알았습니다. C#이야 그냥 열심히 폼에 붙여넣기를 하거나 컨트롤 객체 생성해서 소스로 넣어주면 끝이었죠. 하지만 WIN API로 생성할 때는 좀 다르게 리소스라는 녀석으로 작성하게 됩니다. 한마디로 메뉴 자체가 데이터로써 뽑아져 있다는 거지요. 그 리소스는 RC라는 파일명을 가지고 있고 Resource.h에 해당 리소스에 대해서 해더로 잡히게 됩니다.
그럼 리소스 작성하는 방법을 알아보도록 하겠습니다. 우선 MenuTest라는 프로젝트를 만들었습니다. 그리고 MenuTest.cpp 파일을 생성 후 다음과 같이 기본 골격을 만들겠습니다.
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hInst;
LPCTSTR lpszClass=TEXT("MenuTest");
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));
}
다음 리소스 파일을 만들어야 합니다. 새 항목 추가에서 리소스 파일을 선택후 만들어 주면 됩니다. 우선 이녀석은 MenuTest.rc라고 이름 짓도록 하죠. 그 다음 확인을 누르면 리소스 뷰라는게 튀어 나오게 됩니다. 이 리소스 뷰를 통해서 리소스의 종류나 추가등을 할 수 있게 됩니다. 우리는 우선 메뉴를 만들려고 하는 중이니 MenuTest.rc를 클릭후 마우스 오른쪽 키를 누르면 리소스 추가라는 녀석이 나올껍니다. 그걸 클릭하도록 하겠습니다. 여기서 메뉴를 선택하게 되면 다음과 같은 화면이 나오게 됩니다.
여기에 입력을 클릭하게 되면 글씨를 적을 수가 있게 됩니다. 그러면 다음과 같이 적어보도록 하죠. 우선 파일 저장과 화면캡쳐에 대한 메뉴를 만들려고 합니다.(화면 캡쳐한 것을 파일로 저장하기 위해서) 다음과 같이 디자인 해보도록 하죠.
File메뉴에는 다음과 같이 Save와 Exit 항목을 만들었습니다.
Capture 메뉴에는 다음과 같이 FullScreenCapture, FocusScreenCapture 항목을 만들었습니다.
메뉴 만드는 작업이 생각 했던거 처럼 어렵지는 않았습니다. 저는 막 API함수로 만들어야 되는줄 알았거든요. 찾아보니 상당히 심플하게 되는것이죠. 뭐 이것 저것 추가하는 것도 있지만 우선 이렇게 간단히 처리 되게 됩니다. 그럼 이 메뉴를 우리 윈도우에 추가 시켜보도록 하죠.
#include <windows.h>
#include "resource.h"
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hInst;
LPCTSTR lpszClass=TEXT("MenuTest");
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 = 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, NULL, 0, 0))
{
TranslateMessage(&Message);
DispatchMessage(&Message);
}
return (int)Message.wParam;
}
그리고 리소스 파일을 만들면 자동으로 resource.h 파일이 생성되는데 여기에는 우리가 만든 메뉴들에 대해서 ID가 포함됩니다.
#define IDR_MENU1 101
#define ID_FILE_SAVE40001 40001
#define ID_FILE_EXIT 40002
#define ID_CAPTURE_FULLSCREENCAPTURE 40003
#define ID_CAPTURE_FOCUSSCREENCAPTURE 40004
이 resource.h를 include 시켜주고 WndClass.lpszMenuName 에 우리가 만들어준 메뉴를 넣어주면 메뉴가 생성이 되는 것입니다. 여기서 MEKEINTRESOURCE는 숫자로 정의도니 메뉴 이름을 문자열 형태로 바꿔주는 메크로입니다. 오늘은 직접적으로 저장이나 화면 캡쳐하는 것 대신에 메시지를 띄우는 것으로 하겠습니다.
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
switch(iMessage)
{
case WM_COMMAND:
switch(LOWORD(wParam))
{
case ID_FILE_SAVE40001:
MessageBox(hWnd,TEXT("SAVE 하였습니다."), TEXT("Save"), MB_OK);
break;
case ID_FILE_EXIT:
DestroyWindow(hWnd);
break;
case ID_CAPTURE_FULLSCREENCAPTURE:
MessageBox(hWnd, TEXT("전체 화면 캡쳐하였습니다."), TEXT("FullScreenCapture"), MB_OK);
break;
case ID_CAPTURE_FOCUSSCREENCAPTURE:
MessageBox(hWnd, TEXT("포커스 화면 캡쳐하였습니다."), TEXT("FOCUSScreenCapture"), MB_OK);
break;
}
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}
WndProc에서 WM_COMMAND에 해당 메뉴 항목들의 아이디값에 메시지박스 출력하는 코드를 넣어주었습니다.
그러면 메뉴 항목을 누를 때마다 해당 메시지가 뜨게 될것입니다. 다음과 같이요
이 화면은 파일메뉴에서 SAVE 메뉴를 클릭했을때의 화면이고 다른것도 저것과 비슷한 화면을 보여주겠지요.EXIT를 누르면 DestroyWindow 함수를 호출해서 메인 윈도우를 파괴하게 됩니다. Save 메뉴에 40001이 붙은건 SAVE에 대한 메뉴가 이미 지정되어 자동으로 저렇게 생성을 해주더군요 ^^
어떠셨나요?? 생각보다 메뉴 생성은 어려운 작업은 아니었습니다. 저도 쉽게 된다는거에 참 놀랐었고요 ㅎ
원래는 단축키와 액셀러레이터에 대해서도 한번에 알아 보려고 했지만 포스팅이 너무 길어지는 것 같아 메뉴에 대해서는 우선 여기까지 하고 다음 포스팅에 이어가도록 하겠습니다.
그럼 다음 시간에 ^ㅡ^
감사합니다~
출처 : WIN API 정복 1권
'API' 카테고리의 다른 글
1초에 한번씩 캡쳐 화면 파일로 저장하기 (0) | 2010.03.27 |
---|---|
디렉토리 생성 (0) | 2010.03.27 |
Win API Timer (0) | 2010.03.21 |
캡쳐 화면 BMP로 저장하기 (0) | 2010.03.14 |
메뉴의 단축키, 액셀러레이터 생성 (0) | 2010.03.12 |
포커스가 있는 윈도우 캡쳐 (0) | 2010.03.10 |
API로 구현한 전체화면 캡쳐 소스 (0) | 2010.03.09 |
DC 구조체에 관해.... (1) | 2010.03.09 |
버튼 생성 (0) | 2010.03.06 |
API의 첫 시작 (0) | 2010.03.05 |