277 lines
7.4 KiB
C++
277 lines
7.4 KiB
C++
|
// File: taskbar.cpp
|
||
|
|
||
|
#include "precomp.h"
|
||
|
#include "taskbar.h"
|
||
|
|
||
|
static HWND g_hwndHidden = NULL;
|
||
|
const TCHAR g_cszHiddenWndClassName[] = _TEXT("MnmSrvcHiddenWindow");
|
||
|
BOOL g_fTaskBarIconAdded = FALSE;
|
||
|
BOOL g_fTimerRunning = FALSE;
|
||
|
extern INmManager * g_pMgr;
|
||
|
extern int g_cPersonsInConf;
|
||
|
extern DWORD g_dwMainThreadID;
|
||
|
|
||
|
// This routine starts a timer to periodically retry adding the taskbar icon.
|
||
|
// This is necessary in case the taskbar is not showing at the time the
|
||
|
// service is launched, or the taskbar is destroyed by a logoff-logon sequence.
|
||
|
|
||
|
VOID StartTaskbarTimer(VOID)
|
||
|
{
|
||
|
if ( !g_fTimerRunning)
|
||
|
{
|
||
|
ASSERT(g_hwndHidden);
|
||
|
SetTimer(g_hwndHidden, 0, 5000, NULL);
|
||
|
g_fTimerRunning = TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
VOID KillTaskbarTimer(VOID)
|
||
|
{
|
||
|
if ( g_fTimerRunning )
|
||
|
{
|
||
|
KillTimer ( g_hwndHidden, 0 );
|
||
|
g_fTimerRunning = FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
LRESULT CALLBACK HiddenWndProc( HWND hwnd, UINT uMsg,
|
||
|
WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
switch(uMsg)
|
||
|
{
|
||
|
case WM_USERCHANGED:
|
||
|
case WM_ENDSESSION:
|
||
|
// A user is logging on or off... We don't know which but
|
||
|
// since the desktop is changing we assume our taskbar icon
|
||
|
// is toast. Start a timer to periodically try to add it back
|
||
|
// until it succeeds.
|
||
|
g_fTaskBarIconAdded = FALSE;
|
||
|
StartTaskbarTimer();
|
||
|
break;
|
||
|
|
||
|
case WM_TASKBAR_NOTIFY:
|
||
|
{
|
||
|
if (WM_RBUTTONUP == lParam)
|
||
|
{
|
||
|
::OnRightClickTaskbar();
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case WM_TIMER:
|
||
|
AddTaskbarIcon();
|
||
|
break;
|
||
|
|
||
|
case WM_DESTROY:
|
||
|
{
|
||
|
// NULL the global variable:
|
||
|
g_hwndHidden = NULL;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
||
|
}
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL AddTaskbarIcon(VOID)
|
||
|
{
|
||
|
BOOL bRet = FALSE;
|
||
|
|
||
|
ASSERT( NULL == g_hwndHidden );
|
||
|
|
||
|
{
|
||
|
// Register hidden window class:
|
||
|
WNDCLASS wcHidden =
|
||
|
{
|
||
|
0L,
|
||
|
HiddenWndProc,
|
||
|
0,
|
||
|
0,
|
||
|
GetModuleHandle(NULL),
|
||
|
NULL,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
g_cszHiddenWndClassName
|
||
|
};
|
||
|
|
||
|
if (!RegisterClass(&wcHidden))
|
||
|
{
|
||
|
ERROR_OUT(("Could not register hidden wnd classes"));
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// Create a hidden window for event processing:
|
||
|
g_hwndHidden = ::CreateWindow( g_cszHiddenWndClassName,
|
||
|
_TEXT(""),
|
||
|
WS_POPUP, // not visible!
|
||
|
0, 0, 0, 0,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
GetModuleHandle(NULL),
|
||
|
NULL);
|
||
|
}
|
||
|
|
||
|
if (NULL == g_hwndHidden)
|
||
|
{
|
||
|
ERROR_OUT(("Could not create hidden windows"));
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// Place a 16x16 icon in the taskbar notification area:
|
||
|
NOTIFYICONDATA tnid;
|
||
|
|
||
|
tnid.cbSize = sizeof(NOTIFYICONDATA);
|
||
|
tnid.hWnd = g_hwndHidden;
|
||
|
tnid.uID = ID_TASKBAR_ICON;
|
||
|
tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
|
||
|
tnid.uCallbackMessage = WM_TASKBAR_NOTIFY;
|
||
|
tnid.hIcon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_SM_WORLD));
|
||
|
|
||
|
::LoadString(GetModuleHandle(NULL), IDS_MNMSRVC_TITLE,
|
||
|
tnid.szTip, CCHMAX(tnid.szTip));
|
||
|
|
||
|
// Attempt to add the icon. This may fail because there is no taskbar
|
||
|
// (no user desktop shown). Warn if this is so... We will retry on
|
||
|
// a periodic timer.
|
||
|
|
||
|
if (FALSE == (bRet = Shell_NotifyIcon(NIM_ADD, &tnid)))
|
||
|
{
|
||
|
#ifdef DEBUG
|
||
|
if ( !g_fTimerRunning )
|
||
|
WARNING_OUT(("Could not add notify icon!"));
|
||
|
#endif // DEBUG
|
||
|
|
||
|
// Start the taskbar timer to periodically retry until this succeeds
|
||
|
StartTaskbarTimer();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
g_fTaskBarIconAdded = TRUE;
|
||
|
KillTaskbarTimer(); // Kill timer if necessary
|
||
|
}
|
||
|
|
||
|
if (NULL != tnid.hIcon)
|
||
|
{
|
||
|
DestroyIcon(tnid.hIcon);
|
||
|
}
|
||
|
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
BOOL RemoveTaskbarIcon(VOID)
|
||
|
{
|
||
|
NOTIFYICONDATA tnid;
|
||
|
BOOL ret;
|
||
|
|
||
|
if ( !g_fTaskBarIconAdded || NULL == g_hwndHidden )
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
tnid.cbSize = sizeof(NOTIFYICONDATA);
|
||
|
tnid.hWnd = g_hwndHidden;
|
||
|
tnid.uID = ID_TASKBAR_ICON;
|
||
|
|
||
|
ret = Shell_NotifyIcon(NIM_DELETE, &tnid);
|
||
|
|
||
|
g_fTaskBarIconAdded = FALSE;
|
||
|
|
||
|
if (g_hwndHidden)
|
||
|
{
|
||
|
DestroyWindow(g_hwndHidden);
|
||
|
g_hwndHidden = NULL;
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL OnRightClickTaskbar()
|
||
|
{
|
||
|
TRACE_OUT(("OnRightClickTaskbar called"));
|
||
|
|
||
|
POINT ptClick;
|
||
|
if (FALSE == ::GetCursorPos(&ptClick))
|
||
|
{
|
||
|
ptClick.x = ptClick.y = 0;
|
||
|
}
|
||
|
|
||
|
// Get the menu for the popup from the resource file.
|
||
|
HMENU hMenu = ::LoadMenu(GetModuleHandle(NULL), MAKEINTRESOURCE(IDR_TASKBAR_POPUP));
|
||
|
if (NULL == hMenu)
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// Get the first menu in it which we will use for the call to
|
||
|
// TrackPopup(). This could also have been created on the fly using
|
||
|
// CreatePopupMenu and then we could have used InsertMenu() or
|
||
|
// AppendMenu.
|
||
|
HMENU hMenuTrackPopup = ::GetSubMenu(hMenu, 0);
|
||
|
|
||
|
// Draw and track the "floating" popup
|
||
|
// According to the font view code, there is a bug in USER which causes
|
||
|
// TrackPopupMenu to work incorrectly when the window doesn't have the
|
||
|
// focus. The work-around is to temporarily create a hidden window and
|
||
|
// make it the foreground and focus window.
|
||
|
|
||
|
HWND hwndDummy = ::CreateWindow(_TEXT("STATIC"), NULL, 0,
|
||
|
ptClick.x,
|
||
|
ptClick.y,
|
||
|
1, 1, HWND_DESKTOP,
|
||
|
NULL, GetModuleHandle(NULL), NULL);
|
||
|
if (NULL != hwndDummy)
|
||
|
{
|
||
|
HWND hwndPrev = ::GetForegroundWindow(); // to restore
|
||
|
|
||
|
TPMPARAMS tpmp;
|
||
|
tpmp.cbSize = sizeof(tpmp);
|
||
|
tpmp.rcExclude.right = 1 + (tpmp.rcExclude.left = ptClick.x);
|
||
|
tpmp.rcExclude.bottom = 1 + (tpmp.rcExclude.top = ptClick.y);
|
||
|
|
||
|
::SetForegroundWindow(hwndDummy);
|
||
|
::SetFocus(hwndDummy);
|
||
|
|
||
|
int iRet = ::TrackPopupMenuEx( hMenuTrackPopup,
|
||
|
TPM_RETURNCMD | TPM_HORIZONTAL | TPM_RIGHTALIGN |
|
||
|
TPM_RIGHTBUTTON | TPM_LEFTBUTTON,
|
||
|
ptClick.x,
|
||
|
ptClick.y,
|
||
|
hwndDummy,
|
||
|
&tpmp);
|
||
|
|
||
|
// Restore the previous foreground window (before destroying hwndDummy).
|
||
|
if (hwndPrev)
|
||
|
{
|
||
|
::SetForegroundWindow(hwndPrev);
|
||
|
}
|
||
|
|
||
|
::DestroyWindow(hwndDummy);
|
||
|
|
||
|
switch (iRet)
|
||
|
{
|
||
|
case IDM_TBPOPUP_STOP:
|
||
|
{
|
||
|
PostThreadMessage(g_dwMainThreadID, WM_QUIT, 0, 0);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// We are finished with the menu now, so destroy it
|
||
|
::RemoveMenu(hMenu, 0, MF_BYPOSITION);
|
||
|
::DestroyMenu(hMenuTrackPopup);
|
||
|
::DestroyMenu(hMenu);
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|