황동준님의 윈도우 32비트 프로그래밍 11~15

  윈도우 32비트 프로그래밍 11

이번에는 마우스에 관련된 예제 두가지에 대해 알아보겠습니다.

윈도우즈 프로그램은 메시지 프로그램이라고 이미 여러 번 설명드렸을 겁니다. 마우스도
마찬가지죠. 사용자가 마우스를 움직이다가 작업영역에 클릭하게 되면 마우스가 클릭되었다는 메시지와 함께 그 좌표가 메시지로 발생됩니다. 우리는 이
메시지를 받아서 원하는 루틴을 실행하기만 하면 됩니다.

도스에서 마우스를 처리해본 유저라면 이것이 얼마나 프로그램을 만드는 사람에게 편리성을
제공하는지 쉽게 알 수 있을 겁니다.

WM_LBUTTONDOWN

마우스의 왼쪽 버튼이 눌리면 위 메시지가 발생됩니다. 눌렸던 것이 띄어지면
WM_LBUTTONUP이라는 메시지가 발생되죠. 그러나 이 메시지는 잘 사용하지 않습니다. 마우스가 눌렸는지가 실제로 많이 사용되는
거죠.

WM_RBUTTONDOWN

오른쪽 버튼이 눌리면 위 메시지가 발생됩니다. 메시지 외우기가 쉽죠? 그러면 눌린 좌표는
어떻게 알 수 있을까요? 메시지의 마지막 파라미터인 LPARAM 값으로 알 수 있습니다.

하위워드에 눌린 X값이 상위워드에 눌린 Y값이 들어갑니다.

nX = LOWORD(lParam);
nY = HIWORD(lParam);

이런 식으로 처리해주면 nX에 X좌표가 nY에 Y좌표가 들어갑니다. 자 그러면 실제로 소스를
보도록 합시다. 아래 소스는 마우스가 눌렸을 때 눌린 좌표에 눌렸다는 것을 출력해주는 예제입니다.

#include <windows.h>
#include
<stdio.h>
#include <string.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 test mouse”;

        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;
    PAINTSTRUCT ps;

    static int nX, nY;
    static char
szMouse[80];

    switch(mesg)
    {
        case WM_PAINT
:

            hDC = BeginPaint(hWnd,
&ps);
            TextOut(hDC, nX, nY, szMouse,
strlen(szMouse));
            EndPaint(hWnd, &ps);
            return
FALSE;

        case WM_LBUTTONDOWN :

            nX = LOWORD(lParam);
            nY
= HIWORD(lParam);
            sprintf(szMouse, “Left Mouse Button Down = X :
%d, Y : %d”,
                nX, nY);
            InvalidateRect(hWnd,
NULL, FALSE);
            return FALSE;

        case WM_RBUTTONDOWN :

            nX = LOWORD(lParam);
            nY
= HIWORD(lParam);
            sprintf(szMouse, “Right Mouse Button Down = X :
%d, Y : %d”,
                nX, nY);
            InvalidateRect(hWnd,
NULL, FALSE);
            return FALSE;

        case WM_DESTROY :

            PostQuitMessage(0);
            return
FALSE;
    }
    return DefWindowProc(hWnd, mesg, wParam,
lParam);
}

case WM_PAINT :

    hDC = BeginPaint(hWnd,
&ps);
    TextOut(hDC, nX, nY, szMouse,
strlen(szMouse));
    EndPaint(hWnd, &ps);
    return FALSE;

윈도우가 그려질때 위 메시지가 발생된다고 했죠? 그런데 szMouse에 아무런 값이 없기
때문에 처음에는 아무 것도 출력되지 않을 겁니다.

case WM_LBUTTONDOWN :

마우스의 왼쪽 버튼이 눌렸을 때 처리해주는 구분입니다.

    nX = LOWORD(lParam);
    nY =
HIWORD(lParam);
    sprintf(szMouse, “Left Mouse Button Down = X : %d, Y :
%d”, nX, nY);

눌린 좌표와 문자열을 조합해서 szMouse라는 버퍼에 기억시키고 있습니다.

    InvalidateRect(hWnd, NULL, FALSE);

이 함수에 대해서 설명 드렸나요? 기억이 잘 안나네….. 이 함수는 지정한 작업영역의
일부를 다시 그리라는 메시지를 보내는 역할을 합니다. 두번째 파라미터가 NULL이면 전체 영역을 다시 그리라는 의미이죠. 이 함수를 이용해서
윈도우의 영역을 다시 그리면 어떤 메시지가 발생될까요? WM_PAINT라는 메시지가 발생되겠죠? 왜냐하면 이 메시지는 윈도우가 그려질 때
발생하기 때문이죠. szMouse에 빈 값이 들어가지 않고 sprintf()함수로 넣어준 값이 들어갔기 때문에 출력이 제대로 되겠네요.
그렇죠?

    return FALSE;

case WM_RBUTTONDOWN :

    nX = LOWORD(lParam);
    nY =
HIWORD(lParam);
    sprintf(szMouse, “Right Mouse Button Down = X : %d, Y :
%d”, nX, nY);
    InvalidateRect(hWnd, NULL, FALSE);
    return FALSE;

마우스의 오른쪽 버튼이 눌렸을 때의 처리 루틴입니다. 어때요? 마우스 처리하는 것이 별로
어렵지 않죠.

자 이번에는 마우스가 움직일 때 발생하는 메시지와 더블클릭했을 때 어떤 메시지가 발생하는
지에 대해 알아봅시다.

WndClass.style =
CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS;

마우스가 더블클릭되었을 때 메시지를 받아서 처리할려면 위와 같이 윈도우의 스타일에
CS_DBLCLKS를 지정해 주어야 합니다. CS_HREDRAW|CS_VREDRAW는 우리가 예전부터 지정해준 NULL과 같다고 생각하면
됩니다.

WM_MOUSEMOVE

마우스가 움직이면 위 메시지가 발생되고 lParam에 그 좌표가 저장됩니다.

WM_LBUTTONDBLCLK

마우스의 왼쪽 버튼이 더블클릭되면 위 메세지가 발생되고 오른쪽 버튼이 더블클릭되면 아래
메시지가 발생됩니다.

WM_RBUTTONDBLCLK

자 그러면 예제를 보도록 합시다. 아래 예제는 마우스가 움직일 현재좌표를 화면에 출력해주고
또 더블클릭되었을 때 그 좌표에 더블클릭되었다는 것을 표시해 주는 예제입니다.

#include <windows.h>
#include
<stdio.h>
#include <string.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 test mouse”;

        WndClass.style =
CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS;
        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;
    PAINTSTRUCT ps;

    static int nX, nY;
    static char
szMouse[80];

    switch(mesg)
    {
        case WM_PAINT
:

            hDC = BeginPaint(hWnd,
&ps);
            TextOut(hDC, nX, nY, szMouse,
strlen(szMouse));
            EndPaint(hWnd, &ps);
            return
FALSE;

        case WM_MOUSEMOVE :

            nX = LOWORD(lParam);
            nY
= HIWORD(lParam);
            sprintf(szMouse, “Mouse Position = X : %d, Y :
%d”, nX, nY);
            InvalidateRect(hWnd, NULL,
FALSE);
            return FALSE;

        case WM_LBUTTONDBLCLK :

            nX = LOWORD(lParam);
            nY
= HIWORD(lParam);
            sprintf(szMouse, “Left Mouse Double Click = X :
%d, Y : %d”,
                nX, nY);
            InvalidateRect(hWnd,
NULL, FALSE);
            return FALSE;

        case WM_RBUTTONDBLCLK :

            nX = LOWORD(lParam);
            nY
= HIWORD(lParam);
            sprintf(szMouse, “Right Mouse Double Click = X
: %d, Y : %d”,
                nX, nY);
            InvalidateRect(hWnd,
NULL, FALSE);
            return FALSE;

        case WM_DESTROY :

            PostQuitMessage(0);
            return
FALSE;
    }
    return DefWindowProc(hWnd, mesg, wParam,
lParam);
}

처음 예제와 크게 다른 점이 없어서 설명드릴 것이 없네요.. 그렇죠?

오늘은 여기까지
끝~~~~~~~~~~~~~~~~~~~~~~~~~~

 

  윈도우 32비트 프로그래밍 12

안녕하세요…..돌팔이 황동준입니다…

이번 시간에는 타이머에 대해서 알아봅시다. 이 타이머는 게임을 만들거나 또는 시계 프로그램을
만들 때 많이 사용하는 것입니다.

원리는 간단합니다.

프로그래머가 10초에 한번씩 신호를 보내라고 코딩하면 윈도우즈는 10초마다 정해진 메시지를
보냅니다. 그러면 그 루틴에서 원하는 작업을 하면 되죠. 결과적으로 그 프로그램은 10초마다 어떤 작업을 수행하는 프로그램이 되겠네요.
그렇죠?

자 그러면 어떤 함수를 이용하고 어떤 메시지를 이용해서 하는지 알아봅시다.

UINT SetTimer(
    HWND  hwnd,
    UINT
 idTimer,
    UINT  uTimeout,
    TIMERPROC  tmprc
   );   

위 함수를 이용해서 타이머를 생성할 수 있습니다. 두번째 파라미터는 생성한 타이머의 아이디를
의미합니다. 타이머를 여러개 생성할 수 있으니 각 타이머를 구분할 때 이 아이디를 참조하면 되겠네요. 세번째 파라미터는 타이머 발생 시간 주기를
지정해주는 것입니다. 예를 들어 1000이라고 지정하면 1초에 한번씩 타이머 발생을 의미하는 것입니다. 500이면 0.5초가 되겠죠? 마지막
파라미터는 보통 NULL을 지정해서 사용합니다.  NULL을 지정하지 않고 유저가 만든 함수이름을 지정해 주면 지정된 시간마다 이 함수를
수행합니다.

WM_TIMER

지정된 시간마다 타이머가 발생되면 WM_TIMER라는 메시지가 발생됩니다.이 메시지가
발생되려면 SetTimer() 함수의 마지막 파라미터에 NULL을 지정해야 합니다. 타이머의 아이디는 LPARAM값으로 구분하죠. 아래 예제를
보면 알 수 있을 겁니다.

BOOL KillTimer(
    HWND  hwnd,
    UINT
 idEvent
   );

타이머를 소멸시킬 때 위 함수를 사용합니다. 두번째 파라미터는 소멸시킬 타이머의
아이디입니다.

#include <windows.h>
#include
<stdio.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 test timer”;

        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;
    static int
n;
    static char szTimer[80];

    switch(mesg)
    {
        case WM_CREATE
:

            SetTimer(hWnd, 100, 1000,
NULL);
            return FALSE;

        case WM_TIMER :

            switch(LOWORD(wParam))
            {
                case
100 :

                    n++;
                    hDC
= GetDC(hWnd);
                    TextOut(hDC, 100, 100, szTimer,
sprintf(szTimer,
                        “Count : %d”,
n));
                    ReleaseDC(hWnd,
hDC);
                    break;
            }
            return
FALSE;

        case WM_DESTROY :

            KillTimer(hWnd,
100);
            PostQuitMessage(0);
            return
FALSE;
    }
    return DefWindowProc(hWnd, mesg, wParam,
lParam);
}

자 그러면 소스를 분석해 봅시다.

case WM_CREATE :

    SetTimer(hWnd, 100, 1000, NULL);
    return
FALSE;

윈도우가 생성될때 1초에 한번씩 타이머를 발생시키도록 하고 있군요. 아이디는 100
이네요.

case WM_TIMER :

    switch(LOWORD(wParam))
    {
        case
100 :

            n++;
            hDC =
GetDC(hWnd);
            TextOut(hDC, 100, 100,
szTimer,
                sprintf(szTimer, “Count : %d”,
n));
            ReleaseDC(hWnd,
hDC);
            break;
    }
    return FALSE;

타이머의 아이디가 100이면 현재 카운트를 출력하는 간단한 예제죠? 크게 어려운 점은 없을
겁니다.

아래 예제를 보십시요. 이것은 간단하게 디지털 시계를 만든 것인데 윈도우즈 프로그램에 시계가
내장되어 있는 것을 보면 대부분 아래처럼 구현한 것입니다. 통신 프로그램을 보면 이런게 많이 들어가죠. 소스 설명은 생략하겠습니다. 한번
보세요.

어렵지는 않을 겁니다.

#include <windows.h>
#include
<stdio.h>
#include <time.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 test timer”;

        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;
}

char* TimeCal(void)
{
    static int
i;
    static struct tm *newtime;
    static time_t t;
    static char
cTime[50];
    static char *Total;
    t = time(NULL);
    newtime =
localtime(&t);
    strcpy(cTime,
asctime(newtime));
    cTime[strlen(cTime)-1] = ”;
    Total =
strtok(cTime, ” “);
    for(i=0; i<3; i++)
        Total = strtok(NULL,
” “);
    return Total;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT mesg,
WPARAM wParam, LPARAM lParam)
{
    HDC hDC;
    static char
szTimer[100];

    switch(mesg)
    {
        case WM_CREATE
:

            SetTimer(hWnd, 100, 1000,
NULL);
            return FALSE;

        case WM_TIMER :

            switch(LOWORD(wParam))
            {
                case
100 :

                    hDC =
GetDC(hWnd);
                    TextOut(hDC, 100, 100,
szTimer,
                        sprintf(szTimer, “Current Time : %s”,
TimeCal()));
                    ReleaseDC(hWnd,
hDC);
                    break;
            }
            return
FALSE;

        case WM_DESTROY :

            KillTimer(hWnd,
100);
            PostQuitMessage(0);
            return
FALSE;
    }
    return DefWindowProc(hWnd, mesg, wParam,
lParam);
}

오늘은 여기까지
끝~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 

  윈도우 32비트 프로그래밍 13

안녕하세요…..돌팔이 황동준입니다…………

자 오늘도 계속 공부를 해 보도록 합시다. 오늘은 메뉴를 만드는 방법에 대해 알아
보겠습니다. 윈도우즈용 프로그램 중에 메뉴 없는 프로그램을 본 적이 없죠? 도스용 프로그램도 팝업메뉴를 전부 가지고 있는데 그 구현이 그렇게
간단하지 않았을 겁니다. 물론 라이브러리를 이용하면 간단하게 되지만요….

윈도우즈에서 메뉴를 구현하는 방법은 상당히 간단합니다. 어렵지 않죠.

함수를 이용해서 구현하는 방법도 있고 또 리소스를 이용해서 메뉴를 구현할 수도있습니다.
우리는 앞으로 이 두가지 방법을 다 알아보도록 하겠습니다.

윈도우즈에서 메뉴는 크게 두가지로 나눌 수 있습니다. 바로 시스템 메뉴와 유저가 만든 메뉴로
나눌 수 있는데 시스템 메뉴는 윈도우즈 생성시 속성값을 주기만하면 자동으로 생성되죠. 이 시스템 메뉴에 유저가 메뉴를 추가하는 방법도 있습니다.
참 할게 많죠? 툴바의 구현도 이번에 배워보도록 하죠.

먼저 함수를 이용해서 메뉴를 만드는 방법부터 알아 봅시다. 리소스를 이용해서 메뉴를 만드는
방법을 많이 사용하기 때문에 잘 쓰이지는 않지만 그래도 알아두면 쓸 일이 있겠죠.

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
);

메뉴를 만들 때 핸들을 이용해서 만든 것이 아니기 때문에 윈도우 생성시에 메뉴 핸들을 등록할
필요는 없습니다.

리소스 파일이 있을 때 어떻게 컴파일하는지 이미 설명드렸을 겁니다. 한번 컴파일해서 실행시켜
보시기 바랍니다. 재미 있죠?

자 오늘은 여기까지
끝~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 

  윈도우 32비트 프로그래밍 14

안녕하세요…………..돌팔이 황동준입니다………….

이번에도 역시 메뉴에 관련된 예제 세개를 알아 보겠습니다. 하나는 메뉴에 비트맵 그림을 넣는
방법이고 두번째 예제는 시스템 메뉴에 새로운 메뉴 아이템을 추가하는 것입니다. 워드 프로세서에 보면 최근에 편집한 문서가 이 시스템메뉴에
추가되어 있는 것을 많이 보았을 겁니다. 방법이 그렇게 어렵지 않습니다. 그리고 마지막 세번째 예제는 좀 생소한 단어일지 모르지만 엑셀레이터에
대한 것입니다.

먼저 메뉴에 그림파일을 넣는 방법부터 알아봅시다. 메뉴에 그림파일을 넣을 때의 그림파일
포맷은 비트맵을 사용합니다. 우리는 앞에서 비트맵 파일을 다룰때 리소스 파일을 이용해서 했을 겁니다. 역시 메뉴에서 그림 파일을 이용하려면 이
리소스 파일이 필요합니다.

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:hncdoctest1.hwp”);
        AppendMenu(hMenu, MF_STRING, 200,
“c:hncdoctest2.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:hncdoctest1.hwp”);
AppendMenu(hMenu, MF_STRING, 200,
“c:hncdoctest2.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);
    }
}

메시지를 가죠오고 난 다음에 조건문이 하나 더 있죠? 엑셀레이터를 처리하기 위한 구문입니다.
별로 어렵지 않네요.

오늘은 여기까지
끝~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 

  윈도우 32비트 프로그래밍 15

안녕하세요……돌팔이 황동준입니다…………………..

자 이번에도 역시 메뉴에 관련된 예제 두개 알아 보겠습니다. 첫번째 예제는 생성된 윈도우를
유저가 움직이지 못하게 하는 예제인데 왜 이번에 알아보느냐 하면 이 예제가 시스템 메뉴와 관계가 있기 때문입니다. 그리고 두번째 예제는 팝업
메뉴에 관련된 것입니다. 여기서 말하는 팝업 메뉴는 유저가 마우스의 오른쪽 버튼을 눌렀을 때 생성되는 메뉴를 말하는 것입니다. 윈도우즈
데스크탑위에서 마우스의 오른쪽 버튼을 클릭하면 팝업메뉴가 생성되죠? 이런것을 말하는 것입니다.

자 먼저 윈도우를 움직이지 못하게 하는 예제부터 알아 보도록 하죠.

기본적으로 윈도우를 생성하게 되면 유저가 어떤 특별한 처리가 없어도 생성된 윈도우를 사용자가
옮길 수 있게 되어 있습니다. 그러나 프로그램상에서 한가지 작업만으로 윈도우를 움직이지 못하게 할 수 있습니다. 조금 전에도 언급했지만 이
작업은 시스템 메뉴를 이용해서 하는데 시스템 메뉴를 보면 ‘이동’이라는 메뉴 아이템이 있을 겁니다. 자 그러면 소스를 보도록 합시다.

#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()
함수에서 요구하는 좌표는 전체 윈도우즈의 좌표입니다.

자 오늘은 여기까지
끝~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

zemna

Programmer/Web/Mobile/Desktop

You may also like...