반응형
Recent Posts
Recent Comments
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- ControlGetText
- IfWinExist
- SetTitleMatchMode
- EnvSub
- 배열
- if
- autohotkey
- StringGetPos
- 함수
- IfInString
- Menu
- EnvMult
- API
- SetMouseDelay
- 식
- EnvDiv
- EnvSet
- EnvAdd
- Var:=식
- DetectHiddenWindows
- Blocks
- SetKeyDelay
- SetEnv
- MouseClick
- SetControlDelay
- 식의 설명
- if(식)
- Threads
- ControlSend
- IF (식)
Archives
- Today
- Total
ㄴrㅎnㅂrㄹrㄱi
[강좌 8] 메뉴1 본문
반응형
[API]강좌(8)<--메뉴1 |
메뉴를 만드는 방법에 대해 알아 보겠습니다. 윈도우즈용 프로그램
중에 메뉴 없는 프로그램을 본적이 없죠? 도스용 프로그램도 팝업메뉴를 전
부 가지고 있는데 그 구현이 그렇게 간단하지 않았을 겁니다. 물론 라이브러
리를 이용하면 간단하게 되지만요....
윈도우즈에서 메뉴를 구현하는 방법은 상당히 간단합니다. 어렵지 않죠.
함수를 이용해서 구현하는 방법도 있고 또 리소스를 이용해서 메뉴를 구현할수도
있습니다. 우리는 앞으로 이 두가지 방법을 다 알아보도록 하겠습니다.
윈도우즈에서 메뉴는 크게 두가지로 나눌수 있습니다. 바로 시스템 메뉴와 유저가
만든 메뉴로 나눌수 있는데 시스템 메뉴는 윈도우즈 생성시 속성값을 주기만
하면 자동으로 생성되죠. 이 시스템 메뉴에 유저가 메뉴를 추가하는 방법도 있습
니다. 참 할게 많죠? 툴바의 구현도 이번에 배워보도록 하죠.
먼저 함수를 이용해서 메뉴를 만드는 방법부터 알아 봅시다. 리소스를 이용해서
메뉴를 만드는 방법을 많이 사용하기 때문에 잘 쓰이지는 않지만 그래도 알아
두면 쓸일이 있겠죠.
HMENU CreateMenu(VOID);
메뉴를 만들기 위해서는 메뉴 핸들이 필요합니다. 그러기 때문에 위 함수를 이용해
서 새로운 메뉴 핸들을 얻어야 합니다.
BOOL AppendMenu(
HMENU hmenu,
UINT fuFlags,
UINT idNewItem,
LPCTSTR lpszNewItem
);
첫번째 파라미터는 CreateMenu() 함수를 이용해서 얻은 메뉴 핸들을 지정하면 되고
두번째 파라미터는 메뉴의 성격을 지정해 주면 됩니다. 메뉴로 문자열을 많이 사용
하는데 이러한 속성을 여기에다 지정해 주면 됩니다. 또한 메뉴를 선택될수 없게
하는 그레이 기능이나 체크표시 기능도 여기에 속성을 지정함으로서 할수 있습니
다. 이 부분에 지정할수 있는 속성은 다음과 같습니다.
MF_BITMAP 그림을 가지고 메뉴를 만들때 지정하는 속성입니다.
MF_CHECKED 체크 표시가 되어 있는 메뉴 아이템을 생성할때 사용합니다.
MF_GRAYED 그레이된 메뉴 아이템을 생성할때 사용합니다.
MF_POPUP 팝업메뉴를 가진 메뉴를 생성할때 사용합니다.
MF_SEPARATOR 메뉴를 생성하지 않고 경계선을 표시할때 사용합니다.
MF_STRING 문자열을 메뉴로 사용할때 사용합니다.
만약에 메뉴 아이템이 여러가지 있다고 가정합시다. 이중에 유저가 하나의 아
이템을 선택하면 어떤 아이템을 선택했는지 알아야 할 필요가 있죠? 그래야
해당 작업을 수행할테니까요. 세번째 파라미터에 아이디를 지정해 주면 됩니다.
나중에 이 아이드를 가지고 어떤 아이템이 선택되었는지 판단하게 됩니다. 마지막
파라미터는 실제로 유저에게 보여지는 메뉴 아이템의 이름입니다. 예를들어
"File open" 같은것이 되겠죠?
HMENU GetMenu(
HWND hwnd
);
현재 윈도우의 메뉴 핸들을 얻을때 위 함수를 사용합니다.
BOOL EnableMenuItem(
HMENU hmenu,
UINT uItem,
UINT fuFlags
);
얻은 메뉴 핸들을 이용해서 메뉴 아이템에 그레이 기능을 넣거나 해제할때 위 함수
를 사용합니다. 두번째 파라미터는 해당 메뉴 아이템의 아이디이고 세번째 파라미터
는 다음의 예약어를 지정하면 됩니다.
MF_GRAYED 그레이 기능을 넣을때 사용합니다.
MF_ENABLED 그레이 기능을 해제할때 사용합니다.
DWORD CheckMenuItem(
HMENU hmenu,
UINT idCheckItem,
UINT fuFlags
);
위 함수는 메뉴 아이템에 체크표시를 하거나 해제할때 사용하는 함수입니다.
두번째 파라미터는 역시 메뉴 아이템의 아이디를 의미하고 세번째 파라미터는 다음
과 같은 예약어를 사용할수 있습니다.
MF_CHECKED 체크표시를 넣을때 사용합니다.
MF_UNCHECKED 체크표시를 해제할때 사용합니다.
자 그러면 예제 소스를 보도록 하죠. 어떤 기능을 하는지 한번 짐작해 보시기 바랍
니다.
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain
(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszArg, int nCmdShow)
{
HWND hWnd;
MSG msg;
WNDCLASS WndClass;
char szAppName[] = "This program is to create menu";
HMENU hMenu, hPopup;
WndClass.style = NULL;
WndClass.lpfnWndProc = WndProc;
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = 0;
WndClass.hInstance = hInstance;
WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
WndClass.hbrBackground = GetStockObject(WHITE_BRUSH);
WndClass.lpszMenuName = NULL;
WndClass.lpszClassName = szAppName;
if(!RegisterClass(&WndClass)) return NULL;
hMenu = CreateMenu();
hPopup = CreateMenu();
AppendMenu(hPopup, MF_STRING, 100, "&Open");
AppendMenu(hPopup, MF_STRING, 110, "&Save As");
AppendMenu(hPopup, MF_SEPARATOR, NULL, NULL);
AppendMenu(hPopup, MF_STRING|MF_CHECKED, 120, "&Check");
AppendMenu(hPopup, MF_STRING|MF_GRAYED, 130, "&Grayed");
AppendMenu(hMenu, MF_STRING|MF_POPUP, (UINT)hPopup, "&File");
hPopup = CreateMenu();
AppendMenu(hPopup, MF_STRING, 140, "C&opy");
AppendMenu(hPopup, MF_STRING, 150, "&Paste");
AppendMenu(hMenu, MF_STRING|MF_POPUP, (UINT)hPopup, "&Edit");
hWnd = CreateWindow(
szAppName,
szAppName,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
hMenu,
hInstance,
NULL
);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT mesg, WPARAM wParam, LPARAM lParam)
{
HMENU hMenu;
static BOOL bGrayed, bCheck;
switch(mesg)
{
case WM_COMMAND :
switch(LOWORD(wParam))
{
case 100 :
case 110 :
hMenu = GetMenu(hWnd);
if(!bGrayed)
EnableMenuItem(hMenu, 130, MF_ENABLED);
else
EnableMenuItem(hMenu, 130, MF_GRAYED);
bGrayed = !bGrayed;
break;
case 120 :
hMenu = GetMenu(hWnd);
if(!bCheck)
CheckMenuItem(hMenu, 120, MF_UNCHECKED);
else
CheckMenuItem(hMenu, 120, MF_CHECKED);
bCheck = !bCheck;
break;
}
return FALSE;
case WM_DESTROY :
PostQuitMessage(0);
return FALSE;
}
return DefWindowProc(hWnd, mesg, wParam, lParam);
}
자 소스를 보니 어떼요? 어떤 기능을 하는지 짐작할수 있겠습니까? 먼저 윈 메인
함수부분부터 보도록 하죠. 윈 메인함수에는 메뉴를 생성해서 그 메뉴를 등록하는
과정이 있습니다.
hMenu = CreateMenu();
hPopup = CreateMenu();
두개의 메뉴 핸들을 얻는군요. 왜 두개의 메뉴 핸들이 필요할까요? 그것은 메뉴가
팝업 메뉴일수 있기 때문입니다. 팝업메뉴가 뭔지 알죠? 팝업메뉴는 어떤 메뉴를
클릭했을때 또 다른 서브 메뉴들이 나오는 것을 의미합니다.
AppendMenu(hPopup, MF_STRING, 100, "&Open");
AppendMenu(hPopup, MF_STRING, 110, "&Save As");
AppendMenu(hPopup, MF_SEPARATOR, NULL, NULL);
AppendMenu(hPopup, MF_STRING|MF_CHECKED, 120, "&Check");
AppendMenu(hPopup, MF_STRING|MF_GRAYED, 130, "&Grayed");
메뉴 핸들을 이용해서 메뉴를 생성하는 과정을 보여 주고있습니다. 이미 앞 함수부
분에서 다 설명드린것입니다. 그런데 이상한게 하나 있죠? 문자열에'&' 표시가 되
어있네요. 이것은 뭘까요? 메뉴가 있는 윈도우즈 프로그램을 구동하고 Alt키를 눌
러보면 메뉴가 활성화 될것입니다. 그런데 제일 오른쪽 메뉴를 선택하려면 어떻게
합니까? 오른쪽 방향키로 끝까지 이동해야죠. 만약에 제일끝에 있는 메뉴가 다음과
같은 방법으로 만들어져 있다면 이때 'h'키를 누르면 바로 제일 끝으로 이동됩니다.
AppendMenu(hPopup, MF_STRING|MF_GRAYED, 100, "&Help");
어떤 의미인지 알겠습니까? 메뉴를 보게되면 H에 밑줄이 쳐져 있을 겁니다. 한번
아무 윈도우즈 프로그램을 구동해서 확인해 보시기 바랍니다.
AppendMenu(hMenu, MF_STRING|MF_POPUP, (UINT)hPopup, "&File");
두번째 파라미터를 보시기 바랍니다. MF_POPUP이라고 속성이 정의 되어 있네요.
이것은 팝업 메뉴를 갖는다는 것을 의미합니다. 세번째 파라미터는 팝업메뉴의
핸들을 지정하고 있습니다. 이것으로 보아 우리는 File이라는 메뉴에 클릭하면
위에서 만들어준 메뉴 아이템이 보일것이라는것을 짐작할수 있을 겁니다.
hPopup = CreateMenu();
또 다른 팝업 메뉴를 생성하기 위해서 다시 메뉴핸들을 얻는 과정입니다.
AppendMenu(hPopup, MF_STRING, 140, "C&opy");
AppendMenu(hPopup, MF_STRING, 150, "&Paste");
뚜개의 메뉴 아이템을 생성하는 과정을 보여주고 있습니다.
AppendMenu(hMenu, MF_STRING|MF_POPUP, (UINT)hPopup, "&Edit");
역시 같은 과정이 되풀이 되고 있습니다.
hWnd = CreateWindow(
szAppName,
szAppName,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
hMenu,
hInstance,
NULL
);
윈도우를 생성하는 위 함수의 파라미터를 잘 보시기 바랍니다. hMenu라고 메뉴
의 핸들을 지정하는 부분이 있죠? 메뉴가 있기 때문에 메뉴가 있다는것을 알려
주고 있는 것입니다.
자 한번 상상해 봅시다. 어떤 식으로 메뉴가 생성되었는지를....
우선 처음에 File, Edit라는 두개의 메뉴가 보일겁니다. Edit메뉴에 마우스
의 왼쪽 버튼을 클릭하면 Copy, Paste라는 메뉴 아이템이 나옵니다. 바로 팝업
메뉴네요. 대충 어떻게 돌아가는지 감 잡으셨죠? 자 이번에는 위에서 배운 함수를
이용해서 실제로 메뉴를 다루는 부분입니다.
case WM_COMMAND :
메뉴를 마우스로 클릭하면 위와같이 WM_COMMAND라는 메시지가 발생되고 구체적으로
어떤 메뉴를 클릭했는지는 WPARAM에 의해서 전달됩니다. 이 값은 우리가 메뉴를 생
성할때 지정해준 메뉴 아이템의 아이디입니다.
switch(LOWORD(wParam))
{
case 100 :
case 110 :
hMenu = GetMenu(hWnd);
if(!bGrayed)
EnableMenuItem(hMenu, 130, MF_ENABLED);
else
EnableMenuItem(hMenu, 130, MF_GRAYED);
bGrayed = !bGrayed;
break;
메뉴의 핸들을 얻은후에 번갈아가면서 그레이 시키고 해제하는 과정을 보여주고 있
는 것입니다.
case 120 :
hMenu = GetMenu(hWnd);
if(!bCheck)
CheckMenuItem(hMenu, 120, MF_UNCHECKED);
else
CheckMenuItem(hMenu, 120, MF_CHECKED);
bCheck = !bCheck;
break;
메뉴의 핸들을 얻은후에 번갈아가면서 체크표시하고 없애고 하는 과정을 보여 주고
있습니다.
}
return FALSE;
전체적으로 프로그램이 어떤식으로 돌아가는지 어렵지 않죠? 정말 메뉴 만들기가 쉽
다는 것을 알수 있습니다.
자 그러면 이번에는 더 쉬운 방법을 알아봅시다. 바로 리소스 파일을 이용해서 하는
방법인데 보면 알겠지만 너무 쉽습니다.
MyMenu MENU
우선 메뉴를 작성하려면 그 메뉴에 고유의 이름을 부여합니다. MENU는 메뉴를 만들
겠다는 표시입니다.
BEGIN
POPUP "&File"
BEGIN
MENUITEM "&Open", 100
MENUITEM "&Save As", 110
MENUITEM SEPARATOR
MENUITEM "&Check", 120, CHECKED
MENUITEM "&Grayed", 130, GRAYED
END
POPUP "&Edit"
BEGIN
MENUITEM "C&opy", 140
MENUITEM "&Paste", 150
END
END
바로 전 예제와 똑같은 메뉴를 만드는것인데 리소스 파일을 이용한 경우입니다. 딱
보면 눈에 확 들어오죠? POPUP은 메뉴 아이템을 가진다는 것을 의미하는 것입니다.
바로 이처럼 메뉴를 리소스 파일을 이용해서 만드는 경우가 많습니다. 훨씬 쉽기
때문이죠. 여러분들이 봐도 쉽죠?
아래는 리소스 파일의 소스입니다.
#include <windows.h>
MyMenu MENU
BEGIN
POPUP "&File"
BEGIN
MENUITEM "&Open", 100
MENUITEM "&Save As", 110
MENUITEM SEPARATOR
MENUITEM "&Check", 120, CHECKED
MENUITEM "&Grayed", 130, GRAYED
END
POPUP "&Edit"
BEGIN
MENUITEM "C&opy", 140
MENUITEM "&Paste", 150
END
END
아래는 프로그램 소스입니다. 처음 예제랑 똑같은 역할을 하는 것이죠.
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain
(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszArg, int nCmdShow)
{
HWND hWnd;
MSG msg;
WNDCLASS WndClass;
char szAppName[] = "This program is to create menu";
WndClass.style = NULL;
WndClass.lpfnWndProc = WndProc;
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = 0;
WndClass.hInstance = hInstance;
WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
WndClass.hbrBackground = GetStockObject(WHITE_BRUSH);
WndClass.lpszMenuName = "MyMenu";
WndClass.lpszClassName = szAppName;
if(!RegisterClass(&WndClass)) return NULL;
hWnd = CreateWindow(
szAppName,
szAppName,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL
);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT mesg, WPARAM wParam, LPARAM lParam)
{
HMENU hMenu;
static BOOL bGrayed, bCheck;
switch(mesg)
{
case WM_COMMAND :
switch(LOWORD(wParam))
{
case 100 :
case 110 :
hMenu = GetMenu(hWnd);
if(!bGrayed)
EnableMenuItem(hMenu, 130, MF_ENABLED);
else
EnableMenuItem(hMenu, 130, MF_GRAYED);
bGrayed = !bGrayed;
break;
case 120 :
hMenu = GetMenu(hWnd);
if(!bCheck)
CheckMenuItem(hMenu, 120, MF_UNCHECKED);
else
CheckMenuItem(hMenu, 120, MF_CHECKED);
bCheck = !bCheck;
break;
}
return FALSE;
case WM_DESTROY :
PostQuitMessage(0);
return FALSE;
}
return DefWindowProc(hWnd, mesg, wParam, lParam);
}
위 소스에서 두가지 점을 유의 깊게 봐야합니다. 먼저 윈도우 클래스 등록하는
부분입니다.
WndClass.lpszMenuName = "MyMenu";
리소스 파일에서 정의해준 메뉴 이름을 꼭 등록시켜 줘야 합니다.
hWnd = CreateWindow(
szAppName,
szAppName,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL
);
메뉴를 만들때 핸들을 이용해서 만든것이 아니기 때문에 윈도우 생성시에 메뉴 핸
들을 등록할 필요는 없습니다.
리소스 파일이 있을때 어떻게 컴파일하는지 이미 설명드렸을 겁니다. 한번 컴파일
해서 실행시켜 보시기 바랍니다.
메뉴에 관련된 예제 세개를 알아 보겠습니다. 하나는 메뉴에 비트맵
그림을 넣는 방법이고 두번째 예제는 시스템 메뉴에 새로운 메뉴 아이템을 추가
하는 것입니다. 워드 프로세서에 보면 최근에 편집한 문서가 이 시스템메뉴에 추가
되어 있는 것을 많이 보았을 겁니다. 방법이 그렇게 어렵지 않습니다. 그리고 마지
막 세번째 예제는 좀 생소한 단어일지 모르지만 엑셀레이터에 대한 것입니다.
먼저 메뉴에 그림파일을 넣는 방법부터 알아봅시다. 메뉴에 그림파일을 넣을때의
그림파일 포맷은 비트맵을 사용합니다. 우리는 앞에서 비트맵 파일을 다룰때 리소
스 파일을 이용해서 했을 겁니다. 역시 메뉴에서 그림 파일을 이용하려면 이 리소
스 파일이 필요합니다.
FileBitmap BITMAP file.bmp
OpenBitmap BITMAP open.bmp
SaveBitmap BITMAP save.bmp
세개의 그림파일을 리소스 파일에서 정의해 주는 부분입니다. 별로 낯설지는 않죠?
이미 앞에서 해봤을 겁니다. 우리가 정의한 FileBitmap같은 문자열은 어디서 사용했
는지 기억이 납니까? 바로 윈 메인함수에서 LoadBitmap() 함수를 이용해서 비트맵
파일의 핸들을 얻을때 사용했습니다. 기억나죠.
AppendMenu(hPopup, MF_BITMAP, 100, (const char *)hOpenBitmap);
우리는 메뉴를 생성할때 위 함수를 이용해서 생성을 했습니다. 두번째 파라미터에
비트맵 메뉴를 사용한다고 속성을 지정하고 마지막 파라미터에 그 비트맵 핸들을
지정하면 간단하게 그림 파일을 메뉴로 이용할수 있는것입니다. 간단하죠.
그러면 소스를 보도록 합시다.
아래는 리소스 파일의 소스입니다.
#include <windows.h>
FileBitmap BITMAP file.bmp
OpenBitmap BITMAP open.bmp
SaveBitmap BITMAP save.bmp
아래는 실제 프로그램 메인 소스입니다.
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain
(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszArg, int nCmdShow)
{
HWND hWnd;
MSG msg;
WNDCLASS WndClass;
char szAppName[] = "This program is to create menu";
HMENU hMenu, hPopup;
HBITMAP hFileBitmap, hOpenBitmap, hSaveBitmap;
WndClass.style = NULL;
WndClass.lpfnWndProc = WndProc;
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = 0;
WndClass.hInstance = hInstance;
WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
WndClass.hbrBackground = GetStockObject(WHITE_BRUSH);
WndClass.lpszMenuName = NULL;
WndClass.lpszClassName = szAppName;
if(!RegisterClass(&WndClass)) return NULL;
hFileBitmap = LoadBitmap(hInstance, "FileBitmap");
hOpenBitmap = LoadBitmap(hInstance, "OpenBitmap");
hSaveBitmap = LoadBitmap(hInstance, "SaveBitmap");
hMenu = CreateMenu();
hPopup = CreateMenu();
AppendMenu(hPopup, MF_BITMAP, 100, (const char *)hOpenBitmap);
AppendMenu(hPopup, MF_BITMAP, 110, (const char *)hSaveBitmap);
AppendMenu(hMenu, MF_BITMAP|MF_POPUP, (UINT)hPopup,
(const char *)hFileBitmap);
hWnd = CreateWindow(
szAppName,
szAppName,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
hMenu,
hInstance,
NULL
);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT mesg, WPARAM wParam, LPARAM lParam)
{
switch(mesg)
{
case WM_DESTROY :
PostQuitMessage(0);
return FALSE;
}
return DefWindowProc(hWnd, mesg, wParam, lParam);
}
뭐 그렇게 특별히 설명드릴부분은 없네요. 직접해서 눈으로 확인하시기 바랍니다.
이번에는 시스템 메뉴에 새로운 메뉴 아이템을 추가하는 방법입니다. 우리는 함수를
이용해서 메뉴를 만들때 메뉴 핸들을 생성해서 했고 또 메뉴의 속성을 바꿀때에는
현재 메뉴의 핸들을 얻어서 작업을 했습니다. 시스템 메뉴도 마찬가지입니다. 시스
켐 메뉴를 다루기 위해서는 시스템 메뉴의 핸들이 필요한데 이 핸들은 아래 함수를
이용해서 얻으면 됩니다.
HMENU GetSystemMenu(
HWND hwnd,
BOOL fRevert
);
두번째 파라미터는 보통 FALSE를 많이 사용합니다.
메뉴 아이템에 마우스가 클릭되면 WM_COMMAND 메시지가 발생되고 WPARAM에 구체
적인 메뉴 아이템의 아이디가 들어간다고 앞에서 알아보았는데 시스템 메뉴가 클릭
되면 아래 메시지가 발생됩니다.
WM_SYSCOMMAND
처리하는 방식은 같죠. 그러면 실제적인 소스를 보도록 합시다.
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain
(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszArg, int nCmdShow)
{
HWND hWnd;
MSG msg;
WNDCLASS WndClass;
char szAppName[] = "This program is to create menu";
HMENU hMenu;
WndClass.style = NULL;
WndClass.lpfnWndProc = WndProc;
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = 0;
WndClass.hInstance = hInstance;
WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
WndClass.hbrBackground = GetStockObject(WHITE_BRUSH);
WndClass.lpszMenuName = NULL;
WndClass.lpszClassName = szAppName;
if(!RegisterClass(&WndClass)) return NULL;
hWnd = CreateWindow(
szAppName,
szAppName,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL
);
hMenu = GetSystemMenu(hWnd, FALSE);
AppendMenu(hMenu, MF_SEPARATOR, NULL, NULL);
AppendMenu(hMenu, MF_STRING, 100, "c:\\hnc\\doc\\test1.hwp");
AppendMenu(hMenu, MF_STRING, 200, "c:\\hnc\\doc\\test2.hwp");
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT mesg, WPARAM wParam, LPARAM lParam)
{
switch(mesg)
{
case WM_SYSCOMMAND :
switch(LOWORD(wParam))
{
case 100 :
case 200 :
break;
}
break;
case WM_DESTROY :
PostQuitMessage(0);
return FALSE;
}
return DefWindowProc(hWnd, mesg, wParam, lParam);
}
hMenu = GetSystemMenu(hWnd, FALSE);
AppendMenu(hMenu, MF_SEPARATOR, NULL, NULL);
AppendMenu(hMenu, MF_STRING, 100, "c:\\hnc\\doc\\test1.hwp");
AppendMenu(hMenu, MF_STRING, 200, "c:\\hnc\\doc\\test2.hwp");
시스템 메뉴를 얻고 새로운 메뉴 아이템을 생성하는 과정입니다. 핸들 얻는 방법만
다르지 추가하는 부분은 같음을 알수 있습니다.
case WM_SYSCOMMAND :
switch(LOWORD(wParam))
{
case 100 :
case 200 :
break;
}
break;
이미 앞에서 설명 드린부분입니다. 그런데 여기서 잘 보셔야 하는 부분이 있습니다.
바로 마지막 break문인데 다른곳의 메뉴 처리부분을 보면 전부 return을 사용했는데
여기서는 break를 사용했습니다. 한번 전체 소스를 보시기 바랍니다. 정말 그렇죠?
만약에 여기서 return문을 사용해서 DefWindowProc() 함수가 실행되지 않으면 프로
그램이 다운됩니다. 꼭 주의하시기 바랍니다. 이 부분 많이 틀리는 부분이거든요.
자 이번에는 엑셀레이터라는 것에 대해 알아보겠습니다. 여러분들중에서 엑셀레이터
라는 말을 들어 보신분이 있습니까? 예를들어서 설명 하면 이해하기가 쉬울겁니다.
아래아 한글에서 Alt+X키를 누르면 어떻게 됩니까? 프로그램이 바로 종료되죠? 메뉴
아이템중 '종료' 아이템을 선택한것과 같은 효과를 보는 것입니다. 이것이 바로 엑
셀레이터입니다. 유저가 직접 메뉴를 선택하는 불편함을 없애고 간단한 키 조합으
로 같은 효과를 얻을수 있도록 하는 것입니다. 이 엑셀레이터를 사용하기 위해서는
정의를 해주어야 하는데 역시 리소스 파일에서 정의 해 주면 됩니다.
MyAccel ACCELERATORS
BEGIN
"O", 100, VIRTKEY, ALT
"S", 110, VIRTKEY, CONTROL
VK_F2, 120, VIRTKEY
END
위와 같은 형식으로 정의를 해주는데 MyAccel이라는 문자열은 윈 메인 함수에서
엑셀레이터 핸들을 얻기 위해서 유저가 정의해주는 부분입니다.
"O", 100, VIRTKEY, ALT
예를들어서 파일을 여는 메뉴 아이템의 아이디가 100이면 그것에 해당하는 엑셀
레이터 아이디도 100으로 설정해주어야만 합니다. 두번째가 그 아이디가 되겠네요.
조합하는 과정이 시스템 키가 들어가서 조합되는 경우이면 세번째에 VIRTKEY라는
정의된 문자열을 지정해 주어야 합니다. 마지막은 조합할때 많이 사용하는 ALT,
CONTROL, SHIFT같은 문자열을 지정해 주면 됩니다. 위에 보면 세개가 나와 있는
데 하나씩 해석해 볼까요?
첫번째 : Alt+O키를 누르면 100이라는 아이디를 가진 메뉴 아이템이 실행
두번째 : Ctrl+S키를 누르면 110이라는 아이디를 가진 메뉴 아이템이 실행
세번째 : F2키를 누르면 120이라는 아이디를 가진 메뉴 아이템이 실행
별로 어렵지 않죠? 그러면 이번에는 윈 메인 함수부분을 보도록 합시다.
HACCEL LoadAccelerators(
HINSTANCE hinst,
LPCTSTR lpTableName
);
리소스에서 정의한 엑셀레이터 핸들을 얻기 위해서 위 함수를 사용합니다. 꼭 비트
맵 핸들을 얻는것과 같죠? 두번째 파라미터에 우리가 정의해준 엑셀레이터 문자열
을 지정하면 됩니다.
int TranslateAccelerator(
HWND hwnd,
HACCEL haccl,
LPMSG lpmsg
);
자 중요한 함수가 하나 나왔습니다. 만약에 Alt+O키를 누르면 분명히 키가 눌렸다는
메시지가 발생될 것입니다. 그러나 우리는 이 키가 눌렸다는 것을 사용하는 것이
아니라 엑셀레이터 키로 인식을 하게 하여만 합니다. 어떻게 해야 할까요? 메시지
큐에서 메시지를 가져왔을때 이 메시지가 엑셀레이터 키인지 구분해야하는 작업이
필요할겁니다. 위 함수가 바로 그 역할을 합니다. 만약에 엑셀레이터 키이면 메뉴
가 선택 되었다는 메시지로 변환을 하는 것입니다.원리는 간단합니다.
자 그러면 먼저 리소스 파일부터 보도록 합시다.
#include <windows.h>
MyMenu MENU
BEGIN
POPUP "&File"
BEGIN
MENUITEM "&Open", 100
MENUITEM "&Save As", 110
MENUITEM SEPARATOR
MENUITEM "&Check", 120, CHECKED
MENUITEM "&Grayed", 130, GRAYED
END
POPUP "&Edit"
BEGIN
MENUITEM "C&opy", 140
MENUITEM "&Paste", 150
END
END
MyAccel ACCELERATORS
BEGIN
"O", 100, VIRTKEY, ALT
"S", 110, VIRTKEY, CONTROL
VK_F2, 120, VIRTKEY
END
아래는 프로그램 소스입니다.
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain
(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszArg, int nCmdShow)
{
HWND hWnd;
MSG msg;
WNDCLASS WndClass;
char szAppName[] = "This program is to create menu";
HACCEL hAccel;
WndClass.style = NULL;
WndClass.lpfnWndProc = WndProc;
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = 0;
WndClass.hInstance = hInstance;
WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
WndClass.hbrBackground = GetStockObject(WHITE_BRUSH);
WndClass.lpszMenuName = "MyMenu";
WndClass.lpszClassName = szAppName;
if(!RegisterClass(&WndClass)) return NULL;
hWnd = CreateWindow(
szAppName,
szAppName,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL
);
hAccel = LoadAccelerators(hInstance, "MyAccel");
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
while(GetMessage(&msg, NULL, 0, 0))
{
if(!TranslateAccelerator(hWnd, hAccel, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT mesg, WPARAM wParam, LPARAM lParam)
{
HMENU hMenu;
static BOOL bGrayed, bCheck;
switch(mesg)
{
case WM_COMMAND :
switch(LOWORD(wParam))
{
case 100 :
case 110 :
hMenu = GetMenu(hWnd);
if(!bGrayed)
EnableMenuItem(hMenu, 130, MF_ENABLED);
else
EnableMenuItem(hMenu, 130, MF_GRAYED);
bGrayed = !bGrayed;
break;
case 120 :
hMenu = GetMenu(hWnd);
if(!bCheck)
CheckMenuItem(hMenu, 120, MF_UNCHECKED);
else
CheckMenuItem(hMenu, 120, MF_CHECKED);
bCheck = !bCheck;
break;
}
return FALSE;
case WM_DESTROY :
PostQuitMessage(0);
return FALSE;
}
return DefWindowProc(hWnd, mesg, wParam, lParam);
}
자 핵심적인 부분만 봅시다. 핵심부분은 전부 윈 메인 함수에 있군요.
hAccel = LoadAccelerators(hInstance, "MyAccel");
엑셀레이터 핸들을 가져오는 구문입니다. 앞에서 설명드렸죠?
while(GetMessage(&msg, NULL, 0, 0))
{
if(!TranslateAccelerator(hWnd, hAccel, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
메시지를 가죠오고 난 다음에 조건문이 하나 더 있죠? 엑셀레이터를 처리하기 위한
구문입니다.
자 이번에도 역시 메뉴에 관련된 예제 두개 알아 보겠습니다. 첫번째 예제는
생성된 윈도우를 유저가 움직이지 못하게 하는 예제인데 왜 이번에 알아보느냐 하
면 이 예제가 시스템 메뉴와 관계가 있기 때문입니다. 그리고 두번째 예제는 팝업
메뉴에 관련된 것입니다. 여기서 말하는 팝업 메뉴는 유저가 마우스의 오른쪽 버튼
을 눌렀을때 생성되는 메뉴를 말하는 것입니다. 윈도우즈 데스크탑위에서 마우스의
오른쪽 버튼을 클릭하면 팝업메뉴가 생성되죠?이런것을 말하는 것입니다.
자 먼저 윈도우를 움직이지 못하게 하는 예제부터 알아 보도록 하죠.
기본적으로 윈도우를 생성하게 되면 유저가 어떤 특별한 처리가 없어도 생성된 윈도
우를 사용자가 옮길수 있게 되어 있습니다. 그러나 프로그램상에서 한가지 작업만
으로 윈도우를 움직이지 못하게 할수 있습니다. 조금 전에도 언급했지만 이 작업
은 시스템 메뉴를 이용해서 하는데 시스템 메뉴를 보면 '이동'이라는 메뉴 아이템
이 있을 겁니다. 자 그러면 소스를 보도록 합시다.
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain
(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszArg, int nCmdShow)
{
HWND hWnd;
MSG msg;
WNDCLASS WndClass;
char szAppName[] = "This program is to create menu";
WndClass.style = NULL;
WndClass.lpfnWndProc = WndProc;
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = 0;
WndClass.hInstance = hInstance;
WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
WndClass.hbrBackground = GetStockObject(WHITE_BRUSH);
WndClass.lpszMenuName = NULL;
WndClass.lpszClassName = szAppName;
if(!RegisterClass(&WndClass)) return NULL;
hWnd = CreateWindow(
szAppName,
szAppName,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL
);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT mesg, WPARAM wParam, LPARAM lParam)
{
switch(mesg)
{
case WM_SYSCOMMAND :
if((LOWORD(wParam)&0xfff0) == SC_MOVE)
return FALSE;
break;
case WM_DESTROY :
PostQuitMessage(0);
return FALSE;
}
return DefWindowProc(hWnd, mesg, wParam, lParam);
}
자 그러면 WM_SYSCOMMAND 메시지 부분을 보도록 합시다.
case WM_SYSCOMMAND :
if((LOWORD(wParam)&0xfff0) == SC_MOVE)
return FALSE;
break;
아주 간단하죠? 앤드 연산한 결과가 SC_MOVE값과 같으면 리턴하는 것입니다.
자 이번에는 팝업 메뉴에 대해 알아 봅시다. 팝업 메뉴를 생성하기 위해서는
역시 팝업 메뉴 핸들을 얻어야 합니다.
HMENU CreatePopupMenu(VOID);
위 함수를 이용해서 팝업 메뉴의 핸들을 얻으면 됩니다. 이 핸들을 이용해서
앞에서 배운 AppendMenu() 함수로 메뉴를 만들어 나가면 되죠. 별로 새로운게 없
죠?
BOOL ClientToScreen(
HWND hwnd,
LPPOINT lppt
);
자 새로운 함수를 배워 보도록 합시다. 바로 위 ClientToScreen()이라는 함수인데
이 함수는 작업영역의 좌표를 윈도우즈 데스크탑 화면 좌표로 바꾸어 주는 역할을
하는 것입니다. 그런데 왜 이 함수가 필요할까요? 그것은 아래 함수때문입니다.
BOOL TrackPopupMenu(
HMENU hmenu,
UINT fuFlags,
int x,
int y,
int nReserved,
HWND hwnd,
LPCRECT lprc
);
위 함수를 이용해서 팝업메뉴를 만들수 있습니다. 각 파라미터의 의미를 알아 보도
록 합시다. 첫번째 파라미터는 물론 메뉴의 핸들을 의미하는 것입니다. 두번째 파라
미터는 사용되지 않아 보통 NULL을 지정하면 되고 세번째, 네번째 파라미터는 생성
할 좌표를 의미합니다. 그런데 여기서 좌표는 다른 함수때와는 다릅니다. 이 좌표는
윈도우즈 전체 좌표를 의미하거든요. 다섯번째 파라미터는 NULL을 지정하면 되고
여섯번째 파라미터는 윈도우의 핸들, 일곱번째 파라미터는 NULL을 지정하면 됩니다.
NULL값을 지정하는 부분이 많아서 사용하는데는 크게 어려움이 없죠.
자 그러면 예제 소스를 보도록 합시다. 아래 예제는 마우스의 오른쪽 버튼이 눌리
면 팝업 메뉴를 생성하는 예제입니다.
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain
(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszArg, int nCmdShow)
{
HWND hWnd;
MSG msg;
WNDCLASS WndClass;
char szAppName[] = "This program is to create popup menu";
WndClass.style = NULL;
WndClass.lpfnWndProc = WndProc;
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = 0;
WndClass.hInstance = hInstance;
WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
WndClass.hbrBackground = GetStockObject(WHITE_BRUSH);
WndClass.lpszMenuName = NULL;
WndClass.lpszClassName = szAppName;
if(!RegisterClass(&WndClass)) return NULL;
hWnd = CreateWindow(
szAppName,
szAppName,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL
);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT mesg, WPARAM wParam, LPARAM lParam)
{
HDC hDC;
POINT pt;
static HMENU hMenu;
switch(mesg)
{
case WM_RBUTTONDOWN :
hMenu = CreatePopupMenu();
AppendMenu(hMenu, MF_STRING, 100, "Popup&1");
AppendMenu(hMenu, MF_STRING, 200, "Popup&2");
AppendMenu(hMenu, MF_SEPARATOR, NULL, NULL);
AppendMenu(hMenu, MF_STRING, 300, "Popup&3");
pt.x = LOWORD(lParam);
pt.y = HIWORD(lParam);
ClientToScreen(hWnd, &pt);
TrackPopupMenu(hMenu, NULL, pt.x, pt.y, NULL, hWnd, NULL);
return FALSE;
case WM_COMMAND :
hDC = GetDC(hWnd);
switch(LOWORD(wParam))
{
case 100 :
TextOut(hDC, 0, 0, "Select Popup1", 14);
break;
case 200 :
TextOut(hDC, 0, 0, "Select Popup2", 14);
break;
case 300 :
TextOut(hDC, 0, 0, "Select Popup3", 14);
break;
}
ReleaseDC(hWnd, hDC);
return FALSE;
case WM_DESTROY :
PostQuitMessage(0);
return FALSE;
}
return DefWindowProc(hWnd, mesg, wParam, lParam);
}
우리가 이 예제에서 볼 부분은 팝업메뉴를 생성하는 부분만 보면 됩니다. 나머지 부
분 처리하는것은 다른 일반 메뉴 처리와 다를바가 없거든요. 자 그러면 마우스의 오
른쪽 버튼이 눌리면 어떤일이 발생하는지 알아봅시다.
case WM_RBUTTONDOWN :
hMenu = CreatePopupMenu();
AppendMenu(hMenu, MF_STRING, 100, "Popup&1");
AppendMenu(hMenu, MF_STRING, 200, "Popup&2");
AppendMenu(hMenu, MF_SEPARATOR, NULL, NULL);
AppendMenu(hMenu, MF_STRING, 300, "Popup&3");
메뉴 핸들을 생성하고 메뉴를 만들고 있군요. 뭐 특별한 부분이 없습니다.
pt.x = LOWORD(lParam);
pt.y = HIWORD(lParam);
마우스가 눌린 좌표값을 변수에 넣어서 이 위치에 팝업메뉴를 만들려고 하고 있습니
다.
ClientToScreen(hWnd, &pt);
TrackPopupMenu(hMenu, NULL, pt.x, pt.y, NULL, hWnd, NULL);
return FALSE;
마우스가 눌린 좌표는 윈도우를 기준으로 한 좌표기 때문에 먼저 이 좌표값을 전체
윈도우즈의 좌표값으로 변환해주어야 합니다. 그래야 지정한 위치에 정확하게 팝업
메뉴가 생성되겠죠? 앞에서도 안급했지만 TrackPopupMenu() 함수에서 요구하는 좌
표는 전체 윈도우즈의 좌표입니다.
반응형
'API 관련 > API 강좌모음' 카테고리의 다른 글
[강좌 10] 그래픽1 (0) | 2007.10.26 |
---|---|
[강좌 9] 메뉴2 (0) | 2007.10.26 |
[강좌 7] 마우스, 타이머 (0) | 2007.10.26 |
[강좌 6] 출력부분 (0) | 2007.10.26 |
[강좌 5] 입력부분 (0) | 2007.10.26 |
Comments