소개
쉘 트레이를 이용하는 S/W 를 개발하거나, 이용하는 입장에서
프로세스가 죽었음에도 트레이 영역에 남아 있는 아이콘을 제거하기 위해
만든 함수 입니다 !
쉘 트레이 아이콘 ?
위처럼 어느 윈도우에서나 볼수 있는 쉘 트레이는 Toolbar 컨트롤로서 어플리케이션 아이콘을 트레이
영역에 생성하여 이벤트를 처리하는 영역입니다.
MSDN - Toolbar Controls
쓰레기 Tray 아이콘이란 ?
< ActiveSync 가 강제 종료되어 쓰레기 아이콘이 3개나 보인다 >
트레이에 아이콘을 생성해둔 프로그램이 강제로 종료되었을때, 만들어진 트레이 아이콘을
삭제하지 못하고
종료된 경우 입니다.
개발자 측에서는 트레이 아이콘을 생성하는 기능이 들어갈 경우 쓰레기와 더불어
지저분해진 트레이 아이콘이 같이 보이게 되는데 이러한 현상을 없애기 위해 1회
정도 아래의 함수를 실행하여
깔끔한 트레이를 유지하는 방법입니다
쓰레기 아이콘의 제거는 어떻게 ?
1. 윈도우에서 트레이 아이콘의 윈도우 핸들을 찾는다
2. 해당 윈도우에서 Toolbar 컨트롤을 찾는다
3. 컨트롤에서 트레이 아이콘의 개수를 얻는다
4. TB_BUTTONCOUNT 을 이용하여 현재 트레이에 등록된 아이콘의 개수를 얻는다
5. 해당 개수만큼 TB_GETBUTTON 을 툴바 윈도우에 요청하기 위하여
5.1 해당 프로세서의 PID 를 구한다
5.2 해당 프로세서 내에 가상 메모리를 생성
5.3 TB_GETBUTTON 에서 리턴받을 메모리를 TRAY 프로세서의 가상 메모리에 할당한다
5.4 TB_GETBUTTON 요청하여 TRAYDATA 라는 구조체에 넣는다
6. TRAY 구조체 내에 ProcessID 가 NULL 일 경우 해당 아이콘을 삭제
7. 5의 위치로 아이콘만큼 반복
8. 가상 메모리 해제 및 끝
가상 메모리는 왜 ?
시스템 트레이 아이콘이 위치한 윈도우 핸들도 하나의 프로세스 내에 위치합니다
TB_BUTTONCOUNT 는 리턴이 int 형이라 관련이 없지만
TB_GETBUTTON 와 같은 데이터의 리턴값을 분석해야 할 필요가 있는 메세지는
해당 프로세서내의 메모리에서 생성된 값으로 리턴받아야 합니다.
왜.. 서로 다른 프로세서끼리는 일반적인 변수나 할당한 메모리는 공유가 안되기에.. (다들 알죠?)
그리하여 해당 프로세서를 이용하여 가상의 메모리를 만들어 해당 데이터를 리턴받게 됩니다.
관련 구조체소스
TBBUTTON(commctrl.h)
typedef struct _TBBUTTON {
int iBitmap;
int idCommand;
BYTE fsState;
BYTE fsStyle;
#ifdef _WIN64
BYTE bReserved[6] // padding for alignment
#elif defined(_WIN32)
BYTE bReserved[2] // padding for alignment
#endif
DWORD_PTR dwData;
INT_PTR iString;
} TBBUTTON, NEAR *PTBBUTTON *LPTBBUTTON;
NOTIFYICONDATA(shellapi.h)
typedef struct _NOTIFYICONDATA {
DWORD cbSize;
HWND hWnd;
UINT uID;
UINT uFlags;
UINT uCallbackMessage;
HICON hIcon;
TCHAR szTip[64];
DWORD dwState;
DWORD dwStateMask;
TCHAR szInfo[256];
union {
UINT uTimeout;
UINT uVersion;
};
TCHAR szInfoTitle[64];
DWORD dwInfoFlags;
GUID guidItem;
HICON hBalloonIcon;
} NOTIFYICONDATA, *PNOTIFYICONDATA;
소스
/*******************************************************************
FileName: TeleShellTrayRefresh.cpp
Description: 사라진 트레이 아이콘을 정리한다!
CREATED: 2006-09-12
LAST MODIFIED 2006-09-12
BY: KwangHee Yoo
cpueblo@cpueblo.com, yurchi@hanmail.net
http://www.cpueblo.com
***********************************************************************/
//=========================================================
// INCLUDE 헤더 정의. MFC 용과 빌더용, 또는 자신의 Preheader 해더 파일로 대체
//=========================================================
#include ;
#include
//=========================================================
// 구조체 정의
//=========================================================
struct TRAYDATA
{
HWND hwnd;
UINT uID;
UINT uCallbackMessage;
DWORD Reserved[2];
HICON hIcon;
};
//=========================================================
// Tray 아이콘의 프로세스 이름을 얻기 위해 윈도우 핸들을 얻는 함수
//=========================================================
static HWND FindTrayToolbarWindow()
{
HWND hWnd_ToolbarWindow32 = NULL;
HWND hWnd_ShellTrayWnd;
hWnd_ShellTrayWnd = ::FindWindow(_T("Shell_TrayWnd"), NULL);
if(hWnd_ShellTrayWnd)
{
HWND hWnd_TrayNotifyWnd = ::FindWindowEx(hWnd_ShellTrayWnd,NULL,_T("TrayNotifyWnd"), NULL);
if(hWnd_TrayNotifyWnd)
{
HWND hWnd_SysPager = ::FindWindowEx(hWnd_TrayNotifyWnd,NULL,_T("SysPager"), NULL); // WinXP
// WinXP 에서는 SysPager 까지 추적
if(hWnd_SysPager)
{
hWnd_ToolbarWindow32 = ::FindWindowEx(hWnd_SysPager, NULL,_T("ToolbarWindow32"), NULL);
}
// Win2000 일 경우에는 SysPager 가 없이 TrayNotifyWnd -> ToolbarWindow32 로 넘어간다
else
{
hWnd_ToolbarWindow32 = ::FindWindowEx(hWnd_TrayNotifyWnd, NULL,_T("ToolbarWindow32"), NULL);
}
}
}
return hWnd_ToolbarWindow32;
}
//=========================================================
// 생성자
//=========================================================
TeleShellTrayRefresh::TeleShellTrayRefresh()
{
}
//=========================================================
// 소멸자
//=========================================================
TeleShellTrayRefresh::~TeleShellTrayRefresh()
{
}
//=========================================================
// Refresh 의 메인
//=========================================================
bool TeleShellTrayRefresh::Refresh()
{
try
{
HANDLE m_hProcess;
LPVOID m_lpData;
TBBUTTON tb;
TRAYDATA tray;
DWORD dwTrayPid;
int TrayCount;
// Tray 의 윈도우 핸들 얻기
HWND m_hTrayWnd = FindTrayToolbarWindow();
if (m_hTrayWnd == NULL)
return false;
// Tray 의 개수를 구하고
TrayCount = (int)::SendMessage(m_hTrayWnd, TB_BUTTONCOUNT, 0, 0);
// Tray 윈도우 핸들의 PID 를 구한다
GetWindowThreadProcessId(m_hTrayWnd, &dwTrayPid);
// 해당 Tray 의 Process 를 열어서 메모리를 할당한다
m_hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwTrayPid);
if (!m_hProcess)
return false;
// 해당 프로세스 내에 메모리를 할당
m_lpData = VirtualAllocEx(m_hProcess, NULL, sizeof (TBBUTTON), MEM_COMMIT, PAGE_READWRITE);
if (!m_lpData)
return false;
// Tray 만큼 뺑뺑이
for(int i = 0; i < TrayCount; i++)
{
::SendMessage(m_hTrayWnd, TB_GETBUTTON, i, (LPARAM)m_lpData);
// TBBUTTON 의 구조체와 TRAYDATA 의 내용을 얻기
ReadProcessMemory(m_hProcess, m_lpData, (LPVOID)&tb, sizeof (TBBUTTON), NULL);
ReadProcessMemory(m_hProcess, (LPCVOID)tb.dwData, (LPVOID)&tray, sizeof (tray), NULL);
// 각각 트레이의 프로세스 번호를 얻어서
DWORD dwProcessId = 0;
GetWindowThreadProcessId(tray.hwnd, &dwProcessId);
// Process 가 없는 경우 TrayIcon 을 삭제한다
if (dwProcessId == 0)
{
NOTIFYICONDATA icon;
icon.cbSize = sizeof(NOTIFYICONDATA);
icon.hIcon = tray.hIcon;
icon.hWnd = tray.hwnd;
icon.uCallbackMessage = tray.uCallbackMessage;
icon.uID = tray.uID;
Shell_NotifyIcon(NIM_DELETE, &icon);
}
}
// 가상 메모리 해제와 프로세스 핸들 닫기
VirtualFreeEx(m_hProcess, m_lpData, NULL, MEM_RELEASE);
CloseHandle(m_hProcess);
return true;
}
catch (...)
{
return false;
}
}
</XMP>
관련 링크
MSDN - TB_BUTTONCOUNT
MSDN - TB_GETBUTTON
예제와 소스 다운로드
다운로드
작성자
http://cpueblo.com
유광희