MFC 트레이 아이콘(Tray Icon)

1. NOTIFYICONDATA 구조체
    상태바 영역에 필요한 메시지 정보를 내포한다.

     typedef struct _NOTIFYICONDATAA {
DWORD   cdSize; // 구조체의 크기(byte)
        HWND    hWnd;   // 트레이를 소유한 핸들
        UINT    uID;    // 구분할수 있는 ID 값
        UINT    uFlags; // 멤버들이 내포하고 있는 데이터의 추가 정보를 어떻게 나타낼지 결정
        UINT    uCallbackMessage;
HICON   hIcon;
 #if (NTDDI_VERSION NTDDI_WIN2K)
TCHAR szTip[64];
 #endif
    #if (NTDDI_VERSION >= NTDDI_WIN2K)
TCHAR   szTip[128];
DWORD   dwState;
DWORD   dwStateMask;
TCHAR   szInfo[256];
union {
UINT    uTimeout;
UINT    uVersion;   // Used with Shell_NotifyIcon flag NIM_SETVERSON.
        } DUMMYUNIONNAME;
TCHAR   szInfoTitle[64];
 DWORD   dwInfoFlags;
 #endif
    #if (NTDDI_VERSON >= NTDDI_WINXP)
GUID    guidItem;
#endif
    #if (NTDDI_VERSON >= NTDDI_VISTA)
HICON   hBalloonIcon;
#endif
    }


2. ::Shell_NotifyIcon() API 함수
    이 메서드는 작업표시줄에 메시지를 Send해주는 역할을 한다. 원형은 아래와 같다.

         BOOL Shell_NotifyIcon (
 DWORD           dwMessage,
PNOTIFYICONDATA lpdata;
);

    -. dwMessage : NIM_ADD, MIN_MODIFY, NIM_DELETE를 사용하여 트레이 아이콘을 
                    추가, 수정, 삭제 할 수 있게 해준다.
    -. lpdata : NOTIFYICONDATA 구조체를 통해 정보를 전달한다.

3. 트레이 아이콘 사용 예제
    -. ESC 키를 누를경우 트레이 아이콘으로 등록한다.
    -. 트레이 아이콘을 왼쪽버튼 더블클릭하면 정상적으로 활성화된다.
    -. 트레이 아이콘에서 마우스 오른쪽 버튼을 누를 경우 팝업 메뉴가 뜬다.
    
    1) PreTranslateMessage 메서드 사용해 ESC 키를 누를경우 트레이화

         BOOL CMainFrame::PreTranslateMessage(MSG* pMsg)
{
 if (pMsg->message == WM_KEYDOWN)
{
switch (pMsg->wParam)
{
case VK_ESCAPE :
{
// 트레이 아이콘
                            NOTIFYICONDATA nid;
 ZeroMemory(&nid, sizeof(nid));
nid.cdSize = sizeof(nid);
nid.uID = 0;
nid.hWnd = GetSafeHwnd();

nid.uFlags = NIF_ICON | NIF_TIP NIF_MESSAGE;
nid.hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
 lstrcpy(nid.szTip, L”HOme Networking”);
nid.uCallbackMessage = WM_TRAY_NOTIFICATION;

 BOOL bRet = ::Shell_NotifyIcon(NIM_ADD, &nid);

AfxGetApp()->m_pMainWnd->ShowWindow(SW_HIDE);
}
 break;
}
}
return CFrameWnd::PreTranslateMessage(pMsg);
}

        위 소스는 MainFrame에 구현한 소스이다. 그럼 PreTranslateMessage()의 설명을 생략하고
        트레이 아이콘을 추가하는 부분을 설명하도록 하겠다.

             // 트레이 아이콘
            NOTIFYICONDATA nid;
ZeroMemory(&nid, sizeof(nid));
nid.cdSize = sizeof(nid);
nid.uID = 0;
nid.hWnd = GetSafeHwnd();

nid.uFlags = NIF_ICON NIF_TIP NIF_MESSAGE;
nid.hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
 lstrcpy(nid.szTip, L”HOme Networking”);
nid.uCallbackMessage = WM_TRAY_NOTIFICATION;

BOOL bRet = ::Shell_NotifyIcon(NIM_ADD, &nid);

 AfxGetApp()->m_pMainWnd->ShowWindow(SW_HIDE);

        Shell_NotifyIcon에서 사용한 NOTIFYICONDATA 구조체 선언 및 초기화를 해준다.
            -. nid.uFlags는 NIF_ICON | NIF_TIP | NIF_MESSAGE등의 아이콘, 툴팁, 메시지 사용을 지정.
            -. nid.hIcon은 App의 아이콘을 로드.
            -. nid.szTip은 툴팁으로 나올 문자열을 담는다.
            -. nid.uCallbackMessage는 사용자 지정 메시지인 WM_TRAY_NOTIFICATION으로 지정.
        ::Shell_NotifyIcon에 NIM_ADD(아이콘 추가) 속성을 지정하고, 설정한 NOTIFYICONDATA 구조체 정보를 넘긴다.
        마지막으로 현재 윈도우를 숨긴다.

    2) 사용자 메시지를 추가

         -. MainFram.h
protected :
                afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg LRESULT OnTrayNotifycaton(WPARAMLPARAM);
DECLARE_MESSAGE_MAP()

-. MainFram.cpp
#define WM_TRAY_NOTIFYCATON WM_APP + 1

// CMainFrame

IMPLEMENT_DYNCREATE(CMainFrameCFrameWnd)

BEGIN_MESSAGE_MAP(CMainFrameCFrameWnd)
ON_WM_CREATE()
ON_MESSAGE(WM_TRAY_NOTIFYCATONOnTrayNotifycaton)
END_MESSAGE_MAP()

        정의하는 WM_APP부터 시작하면 내부에서는 유일한 메시지 값이어야한다.
        그렇기 때문에 WM_TRAY_NOTIFYCATON은 WM_APP + 1 치환한다.
        그리고 사용자 메시지의 경우 WM_COMMNAD를 사용하기 때문에 메시지 맵에서 ONMESSAGE 를 사용한다.
        그리고 OnTraynotifycaton(WPARAM wParam, LPARAM lParam)의 워ㅕㄴ형을 가진 메서드를 생성하고 지정한다.
        WPARAM과 LPARAM은 SendMessage()나 PostMessage()를 사용해 메시지를 받을때 사용한다.

             LRESULT CMainFrame::OnTrayNotifycation(WPARAM wParam, LPARAM lParam)
{
switch (lParam)
{
case WM_RBUTTONDOWN :
{
CPoint ptMouse;
::GetCursorPos(&ptMouse);

CMenu menu;
menu.LoadMenu(IDR_MAINFRAME);
CMenu *pMenu = menu.GetSubMenu(0);
pMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON,
ptMouse.x, ptMouse.y, AfxGetMainWnd());
}
break;
case WM_LBUTTONDBLCLK :
{
NOTIFYICONDATA nid;
ZeroMemory(&nid, sizeof(nid));
nid.cbSize = sizeof(nid);
nid.uID = 0;
nid.hWnd = GetSafeHwnd();

BOOL bRet = ::Shell_NotifyIcon(NIM_DELETE, &nid);
if (!bRet)
{
AfxMessageBox(L”트레이 아이콘 제거 실패!”);
return -1;
}
AfxGetApp()->m_pMainWnd->ShowWindow(SW_SHOW);
}
break;
}
return 1;
}
}

        -. WM_RBUTTONDOWN 메시지는 현재의 커서 위치를 알아와 트레이 아이콘 위에 이벤트가 일어날 경우 
            생성되어 있는 첫번째 인덱스 메뉴를 활성화 시킨다.
        -. WM_LBUTTONDBLCLK 메시지는 트레이 아이콘의 uID를 지정하고 핸들을 얻어와 더블클릭시 트레이아이콘을 
            제거하고 윈도우를 활성화 시켜준다.