mirror of
https://github.com/reactos/reactos.git
synced 2025-08-02 13:27:11 +00:00
Initial implementation of a watcher for the systray (notification area) that removes icons if the owning process dies/terminates without removing it
This commit is contained in:
parent
37a39f1843
commit
d66c6890f7
2 changed files with 296 additions and 7 deletions
|
@ -28,10 +28,12 @@
|
||||||
#include <atlcom.h>
|
#include <atlcom.h>
|
||||||
#include <atlwin.h>
|
#include <atlwin.h>
|
||||||
#include <atlstr.h>
|
#include <atlstr.h>
|
||||||
|
#include <atlcoll.h>
|
||||||
#include <shellapi.h>
|
#include <shellapi.h>
|
||||||
#include <shlobj.h>
|
#include <shlobj.h>
|
||||||
#include <shlwapi.h>
|
#include <shlwapi.h>
|
||||||
#include <uxtheme.h>
|
#include <uxtheme.h>
|
||||||
|
#include <process.h>
|
||||||
#include <strsafe.h>
|
#include <strsafe.h>
|
||||||
|
|
||||||
#include <undocuser.h>
|
#include <undocuser.h>
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
* ReactOS Explorer
|
* ReactOS Explorer
|
||||||
*
|
*
|
||||||
* Copyright 2006 - 2007 Thomas Weidenmueller <w3seek@reactos.org>
|
* Copyright 2006 - 2007 Thomas Weidenmueller <w3seek@reactos.org>
|
||||||
|
* Copyright 2018 Ged Murphy <gedmurphy@reactos.org>
|
||||||
*
|
*
|
||||||
* This library is free software; you can redistribute it and/or
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
@ -33,6 +34,253 @@ typedef struct _SYS_PAGER_COPY_DATA
|
||||||
NOTIFYICONDATA nicon_data;
|
NOTIFYICONDATA nicon_data;
|
||||||
} SYS_PAGER_COPY_DATA, *PSYS_PAGER_COPY_DATA;
|
} SYS_PAGER_COPY_DATA, *PSYS_PAGER_COPY_DATA;
|
||||||
|
|
||||||
|
|
||||||
|
struct IconWatcherData
|
||||||
|
{
|
||||||
|
HANDLE hProcess;
|
||||||
|
DWORD ProcessId;
|
||||||
|
NOTIFYICONDATA IconData;
|
||||||
|
|
||||||
|
IconWatcherData()
|
||||||
|
{
|
||||||
|
IconWatcherData(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
IconWatcherData(NOTIFYICONDATA *iconData) :
|
||||||
|
hProcess(NULL), ProcessId(0)
|
||||||
|
{
|
||||||
|
IconData.cbSize = sizeof(NOTIFYICONDATA);
|
||||||
|
IconData.hWnd = iconData->hWnd;
|
||||||
|
IconData.uID = iconData->uID;
|
||||||
|
IconData.guidItem = iconData->guidItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
~IconWatcherData()
|
||||||
|
{
|
||||||
|
if (hProcess)
|
||||||
|
{
|
||||||
|
CloseHandle(hProcess);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CIconWatcher
|
||||||
|
{
|
||||||
|
CAtlList<IconWatcherData *> m_WatcherList;
|
||||||
|
CRITICAL_SECTION m_ListLock;
|
||||||
|
HANDLE m_hWatcherThread;
|
||||||
|
HANDLE m_WakeUpEvent;
|
||||||
|
HWND m_hwndSysTray;
|
||||||
|
bool m_Loop;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CIconWatcher() :
|
||||||
|
m_hWatcherThread(NULL),
|
||||||
|
m_WakeUpEvent(NULL),
|
||||||
|
m_hwndSysTray(NULL),
|
||||||
|
m_Loop(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
virtual ~CIconWatcher()
|
||||||
|
{
|
||||||
|
Uninitialize();
|
||||||
|
if (m_WakeUpEvent)
|
||||||
|
CloseHandle(m_WakeUpEvent);
|
||||||
|
if (m_hWatcherThread)
|
||||||
|
CloseHandle(m_hWatcherThread);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Initialize(_In_ HWND hWndParent)
|
||||||
|
{
|
||||||
|
m_hwndSysTray = hWndParent;
|
||||||
|
|
||||||
|
InitializeCriticalSection(&m_ListLock);
|
||||||
|
m_WakeUpEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
|
||||||
|
if (m_WakeUpEvent == NULL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_hWatcherThread = (HANDLE)_beginthreadex(NULL,
|
||||||
|
0,
|
||||||
|
WatcherThread,
|
||||||
|
(LPVOID)this,
|
||||||
|
0,
|
||||||
|
NULL);
|
||||||
|
if (m_hWatcherThread == NULL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Uninitialize()
|
||||||
|
{
|
||||||
|
m_Loop = false;
|
||||||
|
SetEvent(m_WakeUpEvent);
|
||||||
|
|
||||||
|
EnterCriticalSection(&m_ListLock);
|
||||||
|
|
||||||
|
POSITION Pos;
|
||||||
|
for (int i = 0; i < m_WatcherList.GetCount(); i++)
|
||||||
|
{
|
||||||
|
Pos = m_WatcherList.FindIndex(i);
|
||||||
|
if (Pos)
|
||||||
|
{
|
||||||
|
IconWatcherData *Icon;
|
||||||
|
Icon = m_WatcherList.GetAt(Pos);
|
||||||
|
CloseHandle(Icon->hProcess);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_WatcherList.RemoveAll();
|
||||||
|
|
||||||
|
LeaveCriticalSection(&m_ListLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AddIconToWatcher(_In_ NOTIFYICONDATA *iconData)
|
||||||
|
{
|
||||||
|
IconWatcherData *Icon = new IconWatcherData(iconData);
|
||||||
|
|
||||||
|
(void)GetWindowThreadProcessId(iconData->hWnd, &Icon->ProcessId);
|
||||||
|
|
||||||
|
Icon->hProcess = OpenProcess(SYNCHRONIZE, FALSE, Icon->ProcessId);
|
||||||
|
if (Icon->hProcess == NULL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
EnterCriticalSection(&m_ListLock);
|
||||||
|
|
||||||
|
m_WatcherList.AddTail(Icon);
|
||||||
|
SetEvent(m_WakeUpEvent);
|
||||||
|
|
||||||
|
LeaveCriticalSection(&m_ListLock);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RemoveIconFromWatcher(_In_ NOTIFYICONDATA *iconData)
|
||||||
|
{
|
||||||
|
EnterCriticalSection(&m_ListLock);
|
||||||
|
|
||||||
|
IconWatcherData *Icon;
|
||||||
|
Icon = GetListEntry(iconData, NULL, true);
|
||||||
|
|
||||||
|
SetEvent(m_WakeUpEvent);
|
||||||
|
LeaveCriticalSection(&m_ListLock);
|
||||||
|
|
||||||
|
delete Icon;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
IconWatcherData* GetListEntry(_In_opt_ NOTIFYICONDATA *iconData, _In_opt_ HANDLE hProcess, _In_ bool Remove)
|
||||||
|
{
|
||||||
|
IconWatcherData *Entry = nullptr;
|
||||||
|
POSITION NextPosition = m_WatcherList.GetHeadPosition();
|
||||||
|
POSITION Position;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
Position = NextPosition;
|
||||||
|
|
||||||
|
Entry = m_WatcherList.GetNext(NextPosition);
|
||||||
|
if (Entry)
|
||||||
|
{
|
||||||
|
if ((iconData && ((Entry->IconData.hWnd == iconData->hWnd) && (Entry->IconData.uID == iconData->uID))) ||
|
||||||
|
(hProcess && (Entry->hProcess == hProcess)))
|
||||||
|
{
|
||||||
|
if (Remove)
|
||||||
|
m_WatcherList.RemoveAt(Position);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Entry = nullptr;
|
||||||
|
|
||||||
|
} while (NextPosition != NULL);
|
||||||
|
|
||||||
|
return Entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
static UINT WINAPI WatcherThread(_In_opt_ LPVOID lpParam)
|
||||||
|
{
|
||||||
|
CIconWatcher* This = reinterpret_cast<CIconWatcher *>(lpParam);
|
||||||
|
|
||||||
|
This->m_Loop = true;
|
||||||
|
while (This->m_Loop)
|
||||||
|
{
|
||||||
|
HANDLE *WatchList;
|
||||||
|
DWORD Size;
|
||||||
|
|
||||||
|
EnterCriticalSection(&This->m_ListLock);
|
||||||
|
|
||||||
|
Size = This->m_WatcherList.GetCount() + 1;
|
||||||
|
WatchList = new HANDLE[Size];
|
||||||
|
WatchList[0] = This->m_WakeUpEvent;
|
||||||
|
|
||||||
|
POSITION Pos;
|
||||||
|
for (int i = 0; i < This->m_WatcherList.GetCount(); i++)
|
||||||
|
{
|
||||||
|
Pos = This->m_WatcherList.FindIndex(i);
|
||||||
|
if (Pos)
|
||||||
|
{
|
||||||
|
IconWatcherData *Icon;
|
||||||
|
Icon = This->m_WatcherList.GetAt(Pos);
|
||||||
|
WatchList[i + 1] = Icon->hProcess;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LeaveCriticalSection(&This->m_ListLock);
|
||||||
|
|
||||||
|
DWORD Status;
|
||||||
|
Status = WaitForMultipleObjects(Size,
|
||||||
|
WatchList,
|
||||||
|
FALSE,
|
||||||
|
INFINITE);
|
||||||
|
if (Status == WAIT_OBJECT_0)
|
||||||
|
{
|
||||||
|
// We've been kicked, we have updates to our list (or we're exiting the thread)
|
||||||
|
}
|
||||||
|
else if ((Status >= WAIT_OBJECT_0 + 1) && (Status < Size))
|
||||||
|
{
|
||||||
|
IconWatcherData *Icon;
|
||||||
|
Icon = This->GetListEntry(NULL, WatchList[Status], false);
|
||||||
|
|
||||||
|
int len = FIELD_OFFSET(SYS_PAGER_COPY_DATA, nicon_data) + Icon->IconData.cbSize;
|
||||||
|
PSYS_PAGER_COPY_DATA pnotify_data = (PSYS_PAGER_COPY_DATA)new BYTE[len];
|
||||||
|
pnotify_data->cookie = 1;
|
||||||
|
pnotify_data->notify_code = NIM_DELETE;
|
||||||
|
memcpy(&pnotify_data->nicon_data, &Icon->IconData, Icon->IconData.cbSize);
|
||||||
|
|
||||||
|
COPYDATASTRUCT data;
|
||||||
|
data.dwData = 1;
|
||||||
|
data.cbData = len;
|
||||||
|
data.lpData = pnotify_data;
|
||||||
|
|
||||||
|
BOOL Success = FALSE;
|
||||||
|
HWND parentHWND = ::GetParent(GetParent(This->m_hwndSysTray));
|
||||||
|
if (parentHWND)
|
||||||
|
Success = ::SendMessage(parentHWND, WM_COPYDATA, (WPARAM)&Icon->IconData, (LPARAM)&data);
|
||||||
|
|
||||||
|
delete pnotify_data;
|
||||||
|
|
||||||
|
if (!Success)
|
||||||
|
{
|
||||||
|
// If we failed to handle the delete message, forcibly remove it
|
||||||
|
This->RemoveIconFromWatcher(&Icon->IconData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (Status == WAIT_FAILED)
|
||||||
|
{
|
||||||
|
Status = GetLastError();
|
||||||
|
}
|
||||||
|
ERR("Failed to wait on process handles : %lu\n", Status);
|
||||||
|
This->m_Loop = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class CNotifyToolbar :
|
class CNotifyToolbar :
|
||||||
public CWindowImplBaseT< CToolbar<NOTIFYICONDATA>, CControlWinTraits >
|
public CWindowImplBaseT< CToolbar<NOTIFYICONDATA>, CControlWinTraits >
|
||||||
{
|
{
|
||||||
|
@ -55,7 +303,7 @@ public:
|
||||||
return m_VisibleButtonCount;
|
return m_VisibleButtonCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
int FindItemByIconData(IN CONST NOTIFYICONDATA *iconData, NOTIFYICONDATA ** pdata)
|
int FindItem(IN HWND hWnd, IN UINT uID, NOTIFYICONDATA ** pdata)
|
||||||
{
|
{
|
||||||
int count = GetButtonCount();
|
int count = GetButtonCount();
|
||||||
|
|
||||||
|
@ -65,8 +313,8 @@ public:
|
||||||
|
|
||||||
data = GetItemData(i);
|
data = GetItemData(i);
|
||||||
|
|
||||||
if (data->hWnd == iconData->hWnd &&
|
if (data->hWnd == hWnd &&
|
||||||
data->uID == iconData->uID)
|
data->uID == uID)
|
||||||
{
|
{
|
||||||
if (pdata)
|
if (pdata)
|
||||||
*pdata = data;
|
*pdata = data;
|
||||||
|
@ -94,6 +342,28 @@ public:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int FindItem(IN GUID& Guid, NOTIFYICONDATA ** pdata)
|
||||||
|
{
|
||||||
|
int count = GetButtonCount();
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
NOTIFYICONDATA * data;
|
||||||
|
|
||||||
|
data = GetItemData(i);
|
||||||
|
|
||||||
|
if (data->guidItem == Guid)
|
||||||
|
{
|
||||||
|
if (pdata)
|
||||||
|
*pdata = data;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
BOOL AddButton(IN CONST NOTIFYICONDATA *iconData)
|
BOOL AddButton(IN CONST NOTIFYICONDATA *iconData)
|
||||||
{
|
{
|
||||||
TBBUTTON tbBtn;
|
TBBUTTON tbBtn;
|
||||||
|
@ -107,7 +377,7 @@ public:
|
||||||
(iconData->dwState & NIS_HIDDEN) ? " HIDDEN" : "",
|
(iconData->dwState & NIS_HIDDEN) ? " HIDDEN" : "",
|
||||||
(iconData->dwState & NIS_SHAREDICON) ? " SHARED" : "");
|
(iconData->dwState & NIS_SHAREDICON) ? " SHARED" : "");
|
||||||
|
|
||||||
int index = FindItemByIconData(iconData, ¬ifyItem);
|
int index = FindItem(iconData->hWnd, iconData->uID, ¬ifyItem);
|
||||||
if (index >= 0)
|
if (index >= 0)
|
||||||
{
|
{
|
||||||
TRACE("Icon %d from hWnd %08x ALREADY EXISTS!", iconData->uID, iconData->hWnd);
|
TRACE("Icon %d from hWnd %08x ALREADY EXISTS!", iconData->uID, iconData->hWnd);
|
||||||
|
@ -188,7 +458,7 @@ public:
|
||||||
(iconData->dwState & NIS_HIDDEN) ? " HIDDEN" : "",
|
(iconData->dwState & NIS_HIDDEN) ? " HIDDEN" : "",
|
||||||
(iconData->dwState & NIS_SHAREDICON) ? " SHARED" : "");
|
(iconData->dwState & NIS_SHAREDICON) ? " SHARED" : "");
|
||||||
|
|
||||||
int index = FindItemByIconData(iconData, ¬ifyItem);
|
int index = FindItem(iconData->hWnd, iconData->uID, ¬ifyItem);
|
||||||
if (index < 0)
|
if (index < 0)
|
||||||
{
|
{
|
||||||
WARN("Icon %d from hWnd %08x DOES NOT EXIST!", iconData->uID, iconData->hWnd);
|
WARN("Icon %d from hWnd %08x DOES NOT EXIST!", iconData->uID, iconData->hWnd);
|
||||||
|
@ -273,7 +543,7 @@ public:
|
||||||
|
|
||||||
TRACE("Removing icon %d from hWnd %08x", iconData->uID, iconData->hWnd);
|
TRACE("Removing icon %d from hWnd %08x", iconData->uID, iconData->hWnd);
|
||||||
|
|
||||||
int index = FindItemByIconData(iconData, ¬ifyItem);
|
int index = FindItem(iconData->hWnd, iconData->uID, ¬ifyItem);
|
||||||
if (index < 0)
|
if (index < 0)
|
||||||
{
|
{
|
||||||
TRACE("Icon %d from hWnd %08x ALREADY MISSING!", iconData->uID, iconData->hWnd);
|
TRACE("Icon %d from hWnd %08x ALREADY MISSING!", iconData->uID, iconData->hWnd);
|
||||||
|
@ -529,7 +799,8 @@ public:
|
||||||
|
|
||||||
class CSysPagerWnd :
|
class CSysPagerWnd :
|
||||||
public CComObjectRootEx<CComMultiThreadModelNoCS>,
|
public CComObjectRootEx<CComMultiThreadModelNoCS>,
|
||||||
public CWindowImpl < CSysPagerWnd, CWindow, CControlWinTraits >
|
public CWindowImpl < CSysPagerWnd, CWindow, CControlWinTraits >,
|
||||||
|
public CIconWatcher
|
||||||
{
|
{
|
||||||
CNotifyToolbar Toolbar;
|
CNotifyToolbar Toolbar;
|
||||||
|
|
||||||
|
@ -563,6 +834,7 @@ public:
|
||||||
LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
||||||
{
|
{
|
||||||
Toolbar.Initialize(m_hWnd);
|
Toolbar.Initialize(m_hWnd);
|
||||||
|
CIconWatcher::Initialize(m_hWnd);
|
||||||
|
|
||||||
// Explicitly request running applications to re-register their systray icons
|
// Explicitly request running applications to re-register their systray icons
|
||||||
::SendNotifyMessageW(HWND_BROADCAST,
|
::SendNotifyMessageW(HWND_BROADCAST,
|
||||||
|
@ -572,6 +844,12 @@ public:
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
||||||
|
{
|
||||||
|
CIconWatcher::Uninitialize();
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
BOOL NotifyIconCmd(WPARAM wParam, LPARAM lParam)
|
BOOL NotifyIconCmd(WPARAM wParam, LPARAM lParam)
|
||||||
{
|
{
|
||||||
PCOPYDATASTRUCT cpData = (PCOPYDATASTRUCT) lParam;
|
PCOPYDATASTRUCT cpData = (PCOPYDATASTRUCT) lParam;
|
||||||
|
@ -591,12 +869,20 @@ public:
|
||||||
{
|
{
|
||||||
case NIM_ADD:
|
case NIM_ADD:
|
||||||
ret = Toolbar.AddButton(iconData);
|
ret = Toolbar.AddButton(iconData);
|
||||||
|
if (ret == TRUE)
|
||||||
|
{
|
||||||
|
AddIconToWatcher(iconData);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case NIM_MODIFY:
|
case NIM_MODIFY:
|
||||||
ret = Toolbar.UpdateButton(iconData);
|
ret = Toolbar.UpdateButton(iconData);
|
||||||
break;
|
break;
|
||||||
case NIM_DELETE:
|
case NIM_DELETE:
|
||||||
ret = Toolbar.RemoveButton(iconData);
|
ret = Toolbar.RemoveButton(iconData);
|
||||||
|
if (ret == TRUE)
|
||||||
|
{
|
||||||
|
RemoveIconFromWatcher(iconData);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
TRACE("NotifyIconCmd received with unknown code %d.\n", data->notify_code);
|
TRACE("NotifyIconCmd received with unknown code %d.\n", data->notify_code);
|
||||||
|
@ -713,6 +999,7 @@ public:
|
||||||
|
|
||||||
BEGIN_MSG_MAP(CSysPagerWnd)
|
BEGIN_MSG_MAP(CSysPagerWnd)
|
||||||
MESSAGE_HANDLER(WM_CREATE, OnCreate)
|
MESSAGE_HANDLER(WM_CREATE, OnCreate)
|
||||||
|
MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
|
||||||
MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
|
MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
|
||||||
MESSAGE_HANDLER(WM_SIZE, OnSize)
|
MESSAGE_HANDLER(WM_SIZE, OnSize)
|
||||||
MESSAGE_HANDLER(WM_CONTEXTMENU, OnCtxMenu)
|
MESSAGE_HANDLER(WM_CONTEXTMENU, OnCtxMenu)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue