Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2529 lines
80 KiB

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1998.
//
// File: T R A Y U I . C P P
//
// Contents: Tray window code for the CConnectionTray object.
//
// Notes:
//
// Author: jeffspr 13 Nov 1997
//
//----------------------------------------------------------------------------
#include "pch.h"
#pragma hdrstop
#include "foldinc.h" // Standard shell\tray includes
#include "ctrayui.h"
#include "cfutils.h"
#include "loadicon.h"
#include "ncmisc.h"
#include "oncommand.h"
#include "traymsgs.h"
#include "trayres.h"
#include "ndispnp.h"
#include "ntddndis.h"
#include <confold.h>
#include <smcent.h>
#include <smutil.h>
#include <ncperms.h>
#include "cfutils.h"
#include "ac_ctrayui.h"
const WCHAR c_szTrayClass[] = L"Connections Tray";
const WCHAR c_szTipTrailer[] = L" ...";
const WCHAR c_szDelayLoadKey[] = L"Software\\Microsoft\\Windows\\CurrentVersion\\ShellServiceObjectDelayLoad";
const WCHAR c_szDelayLoadName[] = L"Connections Tray";
const WCHAR c_szDelayLoadClassID[] = L"7007ACCF-3202-11D1-AAD2-00805FC1270E";
const DWORD c_dwBalloonTimeoutSeconds = 5;
const WCHAR c_szDotDotDot[] = L"..."; // For balloon tip
// don't change this unless you know better
const UINT_PTR c_unTimerIdDblClick = 1;
const INT c_idDefaultCMCommand = CMIDM_TRAY_STATUS;
const INT c_idDefaultDisconCMCommand = CMIDM_OPEN_CONNECTIONS_FOLDER;
const INT c_idDefaultDisconCMWirelessCommand = CMIDM_TRAY_WZCDLG_SHOW;
CTrayUI * g_pCTrayUI; // TrayUI object (not COM)
HWND g_hwndTray = NULL;
//---[ Prototypes ]-----------------------------------------------------------
VOID
GetInitialBalloonText(
INetStatisticsEngine* pnse,
PWSTR pszBuf,
DWORD dwSize);
LRESULT
CALLBACK
CTrayUI_WndProc (
HWND hwnd, // window handle
UINT uiMessage, // type of message
WPARAM wParam, // additional information
LPARAM lParam); // additional information
BOOL FInitFoldEnumerator(HWND hwnd, DWORD * pdwIconsAdded);
VOID OnTaskBarIconRButtonUp(HWND hwnd, UINT uiIcon);
VOID OnTaskBarIconBalloonClick(HWND hwnd, UINT uiIcon);
VOID OnTaskBarIconLButtonDblClk(HWND hwnd, UINT uiIcon);
HRESULT HrOpenContextMenu(HWND hwnd, POINT * pPoint, UINT uiIcon);
// Window message handlers
//
LRESULT OnTrayWmCreate(HWND hwnd);
LRESULT OnTrayWmDestroy(HWND hwnd);
LRESULT OnTrayWmCommand(HWND hwnd, UINT uiMessage, WPARAM wParam, LPARAM lParam);
LRESULT OnMyWMNotifyIcon(HWND hwnd, UINT uiMessage, WPARAM wParam, LPARAM lParam);
LRESULT OnMyWMOpenStatus(HWND hwnd, WPARAM wParam, LPARAM lParam);
LRESULT OnMyWMUpdateTrayIcon(HWND hwnd, WPARAM wParam, LPARAM lParam);
LRESULT OnMyWMShowTrayIconBalloon (HWND hwnd, WPARAM wParam, LPARAM lParam);
LRESULT OnMyWMFlushNoop(HWND hwnd, WPARAM wParam, LPARAM lParam);
//+---------------------------------------------------------------------------
//
// Function: CopyAndAdvanceIfSpaceAvailable
//
// Purpose: Helper routine for FormatToolTip. This manages the current
// pointer into the tooltip and the count of characters
// remaining in the buffer.
//
// Arguments:
// pchTip [in out] Current pointer into the tooltip.
// cchRemaining [in out] Count of characters remaining in its buffer.
// pszLine [in] New line to append to the tooltip.
// cchLine [in] Count of characters in the line to append.
// fInsertNewline [in] TRUE to insert a newline first.
//
// Returns: nothing
//
// Author: shaunco 7 Nov 1998
//
// Notes:
//
VOID
CopyAndAdvanceIfSpaceAvailable (
WCHAR*& pchTip,
INT& cchRemaining,
PCWSTR pszLine,
INT cchLine,
BOOL fInsertNewline)
{
TraceFileFunc(ttidSystray);
if (cchLine < cchRemaining - (fInsertNewline) ? 1 : 0)
{
if (fInsertNewline)
{
*pchTip = L'\n';
pchTip++;
cchRemaining--;
}
lstrcpyW(pchTip, pszLine);
pchTip += cchLine;
cchRemaining -= cchLine;
}
}
//+---------------------------------------------------------------------------
//
// Function: FormatToolTip
//
// Purpose: Format a tooltip for the connection with the matching
// icon id.
//
// Arguments:
// hwnd [in] Window handle of the tray.
// uiIcon [in] Icon id of the tray icon.
//
// Returns: nothing
//
// Author: shaunco 7 Nov 1998
//
// Notes:
//
VOID
FormatToolTip (
HWND hwnd,
UINT uiIcon)
{
TraceFileFunc(ttidSystray);
HRESULT hr = S_OK;
NETCON_STATUS ncs = NCS_CONNECTED;
NETCON_MEDIATYPE ncm = NCM_NONE;
NETCON_SUBMEDIATYPE ncsm = NCSM_NONE;
tstring strName;
WCHAR pszValue[64];
WCHAR pszLine[256];
INT cch;
GUID gdPcleGuid;
NOTIFYICONDATA nid;
ZeroMemory(&nid, sizeof(nid));
nid.cbSize = sizeof(NOTIFYICONDATA);
nid.hWnd = hwnd;
nid.uID = uiIcon;
nid.uFlags = NIF_TIP;
// Get the info on the connection so we know how to label the tooltip
//
ConnListEntry cle;
hr = g_ccl.HrFindConnectionByTrayIconId(uiIcon, cle);
if (hr == S_OK)
{
Assert(!cle.ccfe.empty());
ncs = cle.ccfe.GetNetConStatus();
gdPcleGuid = cle.ccfe.GetGuidID();
ncm = cle.ccfe.GetNetConMediaType();
ncsm = cle.ccfe.GetNetConSubMediaType();
}
else // Orphaned item -remove it.
{
NOTIFYICONDATA nid;
ZeroMemory (&nid, sizeof(nid));
nid.cbSize = sizeof(NOTIFYICONDATA);
nid.hWnd = g_hwndTray;
nid.uID = uiIcon;
hr = HrShell_NotifyIcon(NIM_DELETE, &nid);
TraceTag(ttidSystray, "WARNING: Removing Orphan Tray Icon: %d", uiIcon);
return;
}
// g_ccl.ReleaseLock();
// Media status based tool tip
if (fIsConnectedStatus(ncs))
{
WCHAR* pchTip = nid.szTip;
INT cchRemaining = celems(nid.szTip);
// Get a copy of the current stats and the connection's name.
//
STATMON_ENGINEDATA* pData;
hr = g_ccl.HrGetCurrentStatsForTrayIconId(uiIcon, &pData, &strName);
if (S_OK == hr && pData)
{
UINT unTransmitSpeed = pData->SMED_SPEEDTRANSMITTING;
UINT unRecieveSpeed = pData->SMED_SPEEDRECEIVING;
UINT64 u64Sent = pData->SMED_BYTESTRANSMITTING;
UINT64 u64Rcvd = pData->SMED_BYTESRECEIVING;
INT idsSent = IDS_TOOLTIP_LINE_BYTES_SENT;
INT idsRcvd = IDS_TOOLTIP_LINE_BYTES_RCVD;
INFRASTRUCTURE_MODE infraStructureMode = pData->SMED_INFRASTRUCTURE_MODE;
DWORD dwEncryption = pData->SMED_802_11_ENCRYPTION_ENABLED;
INT iSignalStrength = pData->SMED_802_11_SIGNAL_STRENGTH;
WCHAR szSSID[32];
wcsncpy(szSSID, pData->SMED_802_11_SSID, celems(szSSID));
WCHAR szNamePostFix[celems(nid.szTip)];
ZeroMemory(szNamePostFix, celems(nid.szTip));
if ((0 == u64Sent) && (0 == u64Rcvd))
{
// Use packets instead.
//
u64Sent = pData->SMED_PACKETSTRANSMITTING;
u64Rcvd = pData->SMED_PACKETSRECEIVING;
idsSent = IDS_TOOLTIP_LINE_PACKETS_SENT;
idsRcvd = IDS_TOOLTIP_LINE_PACKETS_RCVD;
}
CoTaskMemFree(pData);
pData = NULL;
BOOL fNewLine = FALSE;
// Speed
//
if ((unTransmitSpeed >0) || ( unRecieveSpeed >0))
{
FormatTransmittingReceivingSpeed (
unTransmitSpeed,
unRecieveSpeed,
pszValue);
cch = DwFormatString(SzLoadIds(IDS_TOOLTIP_LINE_SPEED), pszLine, celems(pszLine), pszValue);
CopyAndAdvanceIfSpaceAvailable(pchTip, cchRemaining, pszLine, cch, FALSE);
fNewLine = TRUE;
}
if ( (ncm == NCM_LAN) && (ncsm == NCSM_WIRELESS) )
{
switch (infraStructureMode)
{
case IM_NDIS802_11IBSS:
DwFormatString(SzLoadIds(IDS_NAME_NETWORK), szNamePostFix, celems(szNamePostFix), SzLoadIds(IDS_TOOLTIP_ADHOC));
break;
case IM_NDIS802_11INFRASTRUCTURE:
if (*szSSID)
{
DwFormatString(SzLoadIds(IDS_NAME_NETWORK), szNamePostFix, celems(szNamePostFix), szSSID);
}
else
{
DwFormatString(SzLoadIds(IDS_NAME_NETWORK), szNamePostFix, celems(szNamePostFix), SzLoadIds(IDS_TOOLTIP_INFRASTRUCTURE));
}
break;
case IM_NDIS802_11AUTOUNKNOWN:
case IM_NOT_SUPPORTED:
default:
break;
}
cch = DwFormatString(SzLoadIds(IDS_SIGNAL_STRENGTH), pszLine, celems(pszLine), PszGetRSSIString(iSignalStrength));
CopyAndAdvanceIfSpaceAvailable(pchTip, cchRemaining, pszLine, cch, fNewLine);
fNewLine = TRUE;
}
if ( IsMediaRASType(ncm) )
{
// Bytes or packets sent
//
Format64bitInteger(
u64Sent,
FALSE,
pszValue,
celems(pszValue));
cch = DwFormatString(SzLoadIds(idsSent), pszLine, celems(pszLine), pszValue);
CopyAndAdvanceIfSpaceAvailable(pchTip, cchRemaining, pszLine, cch, fNewLine);
// Bytes or packets received
//
Format64bitInteger(
u64Rcvd,
FALSE,
pszValue,
celems(pszValue));
cch = DwFormatString(SzLoadIds(idsRcvd), pszLine, celems(pszLine), pszValue);
CopyAndAdvanceIfSpaceAvailable(pchTip, cchRemaining, pszLine, cch, TRUE);
}
// Name
//
if ((INT)(strName.length() + 1) < cchRemaining)
{
WCHAR pszTip [celems(nid.szTip)];
lstrcpyW(pszTip, strName.c_str());
if (*szNamePostFix)
{
lstrcatW(pszTip, szNamePostFix);
}
lstrcatW(pszTip, L"\n");
lstrcatW(pszTip, nid.szTip);
lstrcpyW(nid.szTip, pszTip);
}
}
}
else // MEDIA_DISCONNECTED
{
WCHAR* pchTip = nid.szTip;
INT cchRemaining = celems(nid.szTip);
BOOL fNewLine = FALSE;
if (ncs == NCS_INVALID_ADDRESS)
{
UINT idString = IDS_CONTRAY_ADDRESS_INVALID_TOOLTIP;
STATMON_ENGINEDATA* pData = NULL;
tstring strName;
hr = g_ccl.HrGetCurrentStatsForTrayIconId(uiIcon, &pData, &strName);
if (S_OK == hr && pData)
{
if (STATIC_ADDR == pData->SMED_DHCP_ADDRESS_TYPE)
{
idString = IDS_CONTRAY_STATIC_ADDR_INVALID_TOOLTIP;
}
CoTaskMemFree(pData);
}
lstrcpynW(pszLine, SzLoadIds(idString), celems(pszLine));
cch = wcslen(pszLine);
}
else
{
if ( (ncm == NCM_LAN) && (ncsm == NCSM_WIRELESS) )
{
lstrcpynW(pszLine, SzLoadIds(IDS_CONTRAY_WIRELESS_DISCONN_BALLOON), celems(pszLine));
cch = wcslen(pszLine);
}
else
{
lstrcpynW(pszLine, SzLoadIds(IDS_CONTRAY_MEDIA_DISCONN_BALLOON), celems(pszLine));
cch = wcslen(pszLine);
}
}
CopyAndAdvanceIfSpaceAvailable(pchTip, cchRemaining, pszLine, cch, fNewLine);
fNewLine = TRUE;
hr = g_ccl.HrGetCurrentStatsForTrayIconId(uiIcon, NULL, &strName);
if (SUCCEEDED(hr))
{
// Name
//
if ((INT)(strName.length() + 1) < cchRemaining)
{
WCHAR pszTip [celems(nid.szTip)];
lstrcpyW(pszTip, strName.c_str());
lstrcatW(pszTip, L"\n");
lstrcatW(pszTip, nid.szTip);
lstrcpyW(nid.szTip, pszTip);
}
}
}
hr = HrShell_NotifyIcon(NIM_MODIFY, &nid);
}
//+---------------------------------------------------------------------------
//
// Member: CTrayUI::CTrayUI
//
// Purpose: Constructor for the CTrayUI class. Initialize the base junk
//
// Arguments:
// (none)
//
// Returns:
//
// Author: jeffspr 13 Nov 1997
//
// Notes:
//
CTrayUI::CTrayUI()
{
TraceFileFunc(ttidSystray);
// There should only be one of these objects
//
Assert(!g_pCTrayUI);
InitializeCriticalSection(&m_csLock);
m_uiNextIconId = 0;
m_uiNextHiddenIconId = UINT_MAX;
}
HRESULT CTrayUI::HrInitTrayUI(VOID)
{
TraceFileFunc(ttidSystray);
HRESULT hr = S_OK;
HWND hwnd;
// create a hidden window
//
WNDCLASS wndclass;
ZeroMemory (&wndclass, sizeof(wndclass));
wndclass.lpfnWndProc = CTrayUI_WndProc;
wndclass.hInstance = _Module.GetResourceInstance();
wndclass.lpszClassName = c_szTrayClass;
RegisterClass (&wndclass);
hwnd = CreateWindow(c_szTrayClass,
c_szTrayClass,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
_Module.GetResourceInstance(),
NULL);
if (hwnd)
{
// Assigned during WM_CREATE
//
Assert(hwnd == g_hwndTray);
ShowWindow(hwnd, SW_HIDE);
}
else
{
hr = E_OUTOFMEMORY;
}
TraceHr(ttidSystray, FAL, hr, FALSE, "CTrayUI::HrInitTrayUI");
return hr;
}
HRESULT CTrayUI::HrDestroyTrayUI(VOID)
{
TraceFileFunc(ttidSystray);
HRESULT hr = S_OK;
// Remove the tray icons before destroying ourselves
//
g_ccl.FlushTrayIcons();
if (g_hwndTray)
{
// Don't bother checking the return code here. Most likely, this window
// is already gone by the time the tray is calling our shutdown.. We'll
// still grab the return code for debugging purposes, though.
//
BOOL fReturn = DestroyWindow(g_hwndTray);
g_hwndTray = NULL;
}
TraceHr(ttidSystray, FAL, hr, FALSE, "CTrayUI::HrDestroyTrayUI");
return S_OK;
}
VOID SetIconFocus(HWND hwnd, UINT uiIcon)
{
TraceFileFunc(ttidSystray);
HRESULT hr;
NOTIFYICONDATA nid;
ZeroMemory (&nid, sizeof(nid));
nid.cbSize = sizeof(NOTIFYICONDATA);
nid.hWnd = hwnd;
nid.uID = uiIcon;
hr = HrShell_NotifyIcon(NIM_SETFOCUS, &nid);
TraceHr(ttidSystray, FAL, hr, FALSE, "SetIconFocus");
}
//+---------------------------------------------------------------------------
//
// Function: CheckMenuPermissions
//
// Purpose: Update the tray items based on system policy
//
// Arguments:
// hmenu [in] The tray context menu
//
// Returns:
//
// Author: jeffspr 8 Apr 1999
//
// Notes:
//
VOID CheckMenuPermissions(HMENU hmenu, const CONFOLDENTRY& ccfe)
{
TraceFileFunc(ttidSystray);
// Check the permissions for bringing up statistics. If no,
// then disable the context menu item
//
if (!FHasPermission(NCPERM_Statistics))
{
// Enable or disable the menu item, as appopriate
//
EnableMenuItem(
hmenu,
CMIDM_TRAY_STATUS,
MF_GRAYED);
}
// Check the permission to disconnect
BOOL fCanDisconnect = TRUE;
switch(ccfe.GetNetConMediaType())
{
case NCM_LAN:
case NCM_BRIDGE:
fCanDisconnect = FHasPermission(NCPERM_LanConnect);
if (!FHasPermission(NCPERM_Repair))
{
EnableMenuItem(
hmenu,
CMIDM_TRAY_REPAIR,
MF_GRAYED);
}
break;
case NCM_PPPOE:
case NCM_DIRECT:
case NCM_ISDN:
case NCM_PHONE:
case NCM_TUNNEL:
case NCM_NONE:
fCanDisconnect = FHasPermission(NCPERM_RasConnect);
break;
case NCM_SHAREDACCESSHOST_LAN:
case NCM_SHAREDACCESSHOST_RAS:
fCanDisconnect = TRUE; // group policy is enforced by the enumerator, if you can see it you can use it.
break;
default:
AssertSz(FALSE, "Need to add a switch for this connection type in the menuing code");
break;
}
if (!fCanDisconnect)
{
EnableMenuItem(
hmenu,
CMIDM_TRAY_DISCONNECT,
MF_GRAYED);
}
}
//+---------------------------------------------------------------------------
//
// Function: FAddMenuBranding
//
// Purpose: Process the CM branding tray menu extensions. Add them to the
// menu if needed
//
// Arguments:
// hmenu [in] Incoming hmenu
// cfe [in] Our cache entry
// IdMinMenuID [in] The smallest allowable menu ID to use
// pMenuData [in] Menu Data
// piIdCustomMin [out] Our custom range min
// piIdCustomMax [out] Our custom range max
//
// Returns: TRUE if we added anything, FALSE if we didn't
//
// Author: jeffspr 8 Apr 1999
//
// Notes:
//
BOOL FAddMenuBranding(
HMENU hmenu,
const ConnListEntry& cle,
INT IdMinMenuID,
INT * piIdCustomMin,
INT * piIdCustomMax)
{
TraceFileFunc(ttidSystray);
BOOL fBranded = FALSE;
int iIdCustomMin = -1;
int iIdCustomMax = -1;
HMENU hmenuTrack = NULL;
Assert(hmenu);
Assert(!cle.empty());
Assert(!cle.ccfe.empty());
if (cle.ccfe.GetCharacteristics() & NCCF_BRANDED)
{
// we may have custom menus for CM connections, merge them in
//
const CON_TRAY_MENU_DATA* pMenuData = cle.pctmd;
if (pMenuData)
{
Assert(pMenuData->dwCount);
int cMenuItems = GetMenuItemCount(hmenu);
if (-1 == cMenuItems)
{
TraceLastWin32Error("GetMenuItemCount failed on tray menu");
}
else
{
BOOL fRet;
MENUITEMINFO mii;
// add a separator bar
ZeroMemory(&mii, sizeof(mii));
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_TYPE;
mii.fType = MFT_SEPARATOR;
fRet = InsertMenuItem( hmenu,
cMenuItems++,
TRUE, // fByPosition
&mii);
if (fRet)
{
DWORD dwCount = pMenuData->dwCount;
CON_TRAY_MENU_ENTRY * pMenuEntry = pMenuData->pctme;
// this is the first id for our custom menu items
iIdCustomMin = CMIDM_FIRST+cMenuItems+1;
iIdCustomMin = iIdCustomMin < IdMinMenuID ? IdMinMenuID : iIdCustomMin;
iIdCustomMax = iIdCustomMin+dwCount;
int iMenu = 0;
while (dwCount)
{
Assert(pMenuEntry);
fRet = AppendMenu( hmenu,
MF_STRING,
iIdCustomMin+iMenu,
pMenuEntry->szwMenuText);
if (!fRet)
{
DWORD dwError = GetLastError();
TraceTag(ttidSystray, "Failed adding custom menu: %S, error: %d",
pMenuEntry->szwMenuText,
dwError);
}
// move to the next item
iMenu++;
dwCount--;
pMenuEntry++;
}
// Mark it as branded to say "hey, we actually added items"
//
fBranded = TRUE;
}
}
}
}
if (fBranded)
{
*piIdCustomMin = iIdCustomMin;
*piIdCustomMax = iIdCustomMax;
}
return fBranded;
}
//+---------------------------------------------------------------------------
//
// Function: HrProcessBrandedTrayMenuCommand
//
// Purpose: Perform the custom action specified in the selected
// branded menu
//
// Arguments:
// iMenuEntry [in] Our branded command
// pMenuData [in] Our branded menu struct
//
// Returns:
//
// Author: jeffspr 8 Apr 1999
//
// Notes:
//
HRESULT HrProcessBrandedTrayMenuCommand(
INT iMenuEntry,
const CON_TRAY_MENU_DATA * pMenuData)
{
TraceFileFunc(ttidSystray);
HRESULT hr = S_OK;
Assert(iMenuEntry != -1);
Assert(pMenuData);
DWORD dwCount = pMenuData->dwCount;
Assert(dwCount > 0);
CON_TRAY_MENU_ENTRY * pMenuEntry = pMenuData->pctme + iMenuEntry;
Assert(pMenuEntry);
SHELLEXECUTEINFO seiTemp = { 0 };
// Fill in the data structure
//
seiTemp.cbSize = sizeof(SHELLEXECUTEINFO);
seiTemp.fMask = SEE_MASK_DOENVSUBST;
seiTemp.hwnd = NULL;
seiTemp.lpVerb = NULL;
seiTemp.lpFile = pMenuEntry->szwMenuCmdLine;
seiTemp.lpParameters = pMenuEntry->szwMenuParams;
seiTemp.lpDirectory = NULL;
seiTemp.nShow = SW_SHOW;
seiTemp.hInstApp = NULL;
seiTemp.hProcess = NULL;
// Launch the tool
//
if (!::ShellExecuteEx(&seiTemp))
{
hr = ::HrFromLastWin32Error();
}
TraceHr(ttidSystray, FAL, hr, FALSE, "HrProcessBrandedMenuCommand");
return hr;
}
HRESULT HrOpenContextMenu(HWND hwnd, POINT * pPoint, UINT uiIcon)
{
TraceFileFunc(ttidSystray);
HRESULT hr = S_OK;
INT iCmd = 0;
HMENU hmenu = 0;
PCONFOLDPIDL pidlItem;
BOOL fSetIconFocus = TRUE;
INT iIdCustomMin = -1;
INT iIdCustomMax = -1;
BOOL fBranded = FALSE;
Assert(pPoint);
Assert(hwnd);
// Find the connection info based on the tray icon id.
//
ConnListEntry cle;
hr = g_ccl.HrFindConnectionByTrayIconId(uiIcon, cle);
if (hr == S_OK)
{
Assert(!cle.ccfe.empty());
if (!cle.ccfe.empty())
{
// Load the menu resource
//
INT iMenuToLoad = POPUP_CONTRAY_GENERIC_MENU_RAS;
if (cle.ccfe.GetNetConStatus() == NCS_MEDIA_DISCONNECTED)
{
if (IsMediaLocalType(cle.ccfe.GetNetConMediaType()) &&
(NCSM_WIRELESS == cle.ccfe.GetNetConSubMediaType()) )
{
iMenuToLoad = POPUP_CONTRAY_WIRELESS_DISCONNECTED_LAN;
}
else
{
iMenuToLoad = POPUP_CONTRAY_MEDIA_DISCONNECTED_MENU;
}
}
else if (IsMediaLocalType(cle.ccfe.GetNetConMediaType()) || NCM_SHAREDACCESSHOST_LAN == cle.ccfe.GetNetConMediaType())
{
if (NCSM_WIRELESS == cle.ccfe.GetNetConSubMediaType())
{
iMenuToLoad = POPUP_CONTRAY_GENERIC_MENU_WIRELESS_LAN;
}
else
{
iMenuToLoad = POPUP_CONTRAY_GENERIC_MENU_LAN;
}
}
hmenu = LoadMenu(_Module.GetResourceInstance(), MAKEINTRESOURCE(iMenuToLoad));
if (!hmenu)
{
hr = E_FAIL;
}
if (SUCCEEDED(hr))
{
// Get the first menu from the popup. For some reason, this hack is
// required instead of tracking on the outside menu
//
HMENU hmenuTrack = GetSubMenu(hmenu, 0);
//Repair is only availabe for LAN and Bridge adapters
if ( ((POPUP_CONTRAY_GENERIC_MENU_LAN == iMenuToLoad) ||
(POPUP_CONTRAY_GENERIC_MENU_WIRELESS_LAN == iMenuToLoad)) &&
(NCM_BRIDGE != cle.ccfe.GetNetConMediaType()) &&
(NCM_LAN != cle.ccfe.GetNetConMediaType()) )
{
DeleteMenu(hmenuTrack,
CMIDM_TRAY_REPAIR,
MF_BYCOMMAND);
}
// Don't drop out of the loop if we can't get this right.
//
CheckMenuPermissions(hmenuTrack, cle.ccfe);
fBranded = FAddMenuBranding(hmenuTrack, cle, CMIDM_TRAY_MAX+1, &iIdCustomMin, &iIdCustomMax);
// Set the default menu item
//
if (cle.ccfe.GetNetConStatus() == NCS_MEDIA_DISCONNECTED)
{
if (IsMediaLocalType(cle.ccfe.GetNetConMediaType()) &&
(NCSM_WIRELESS == cle.ccfe.GetNetConSubMediaType()) )
{
SetMenuDefaultItem(hmenuTrack, c_idDefaultDisconCMWirelessCommand, FALSE);
}
else
{
SetMenuDefaultItem(hmenuTrack, c_idDefaultDisconCMCommand, FALSE);
}
}
else
{
SetMenuDefaultItem(hmenuTrack, c_idDefaultCMCommand, FALSE);
}
// Set the owner window to be foreground as a hack so the
// popup menu disappears when the user clicks elsewhere.
//
SetForegroundWindow(hwnd);
// Part of the above hack. Bring up the menu and figure out the result
iCmd = TrackPopupMenu(hmenuTrack, TPM_RETURNCMD | TPM_NONOTIFY | TPM_RIGHTBUTTON,
pPoint->x, pPoint->y, 0, hwnd, NULL);
DestroyMenu(hmenu);
MSG msgTmp;
while (PeekMessage(&msgTmp, hwnd, WM_LBUTTONDOWN, WM_LBUTTONUP, PM_REMOVE))
{
DispatchMessage(&msgTmp);
}
// Process the command
//
switch (iCmd)
{
case CMIDM_OPEN_CONNECTIONS_FOLDER:
hr = HrOpenConnectionsFolder();
if (S_OK == hr)
{
// The folder should have focus
fSetIconFocus = FALSE;
}
break;
case CMIDM_TRAY_REPAIR:
HrOnCommandFixInternal(
cle.ccfe,
g_hwndTray,
NULL);
break;
case CMIDM_TRAY_WZCDLG_SHOW:
{
PCONFOLDPIDLVEC pcfpVec;
PCONFOLDPIDL pcfp;
hr = cle.ccfe.ConvertToPidl(pcfp);
if (SUCCEEDED(hr))
{
pcfpVec.insert(pcfpVec.begin(), pcfp);
HrOnCommandWZCDlgShow(pcfpVec,
g_hwndTray,
NULL);
}
}
break;
case CMIDM_TRAY_DISCONNECT:
// Ignore the return from this. If it's NULL, we'll just
// pass it in, and it just won't get refreshed properly
//
hr = HrOnCommandDisconnectInternal(
cle.ccfe,
g_hwndTray,
NULL);
// Normalize the return code on success. We don't care
// if the dialog was canceled or not.
//
switch(hr)
{
// If succeeded, mark us as disconnected.
//
case S_OK:
// If we disconnected and the icon went away, then don't
// bother trying to set the focus back
//
fSetIconFocus = FALSE;
break;
// If S_FALSE, we didn't disconnect. Go ahead
// and normalize the return code.
//
case S_FALSE:
hr = S_OK;
break;
}
break;
case CMIDM_TRAY_STATUS:
hr = HrOnCommandStatusInternal(cle.ccfe, FALSE);
break;
case 0:
// Tray menu cancelled without selection
break;
default:
if ((iCmd >= iIdCustomMin) && (iCmd < iIdCustomMax))
{
AssertSz(fBranded, "Hey, what fool added this command?");
hr = HrProcessBrandedTrayMenuCommand(iCmd-iIdCustomMin, cle.pctmd);
}
else
{
AssertSz(FALSE, "Not in custom range, not a known command, what the...?");
}
break;
}
if (fSetIconFocus)
{
// Shift the focus back to the shell
//
SetIconFocus(hwnd, uiIcon);
}
}
}
else
{
// Data returned from the FindByIconId was bogus
//
hr = E_FAIL;
}
}
else // Orphaned item -remove it.
{
NOTIFYICONDATA nid;
ZeroMemory (&nid, sizeof(nid));
nid.cbSize = sizeof(NOTIFYICONDATA);
nid.hWnd = g_hwndTray;
nid.uID = uiIcon;
hr = HrShell_NotifyIcon(NIM_DELETE, &nid);
TraceTag(ttidSystray, "WARNING: Connection not found opening context menu, hr: 0x%08x, uiIcon: %d", hr, uiIcon);
// Removed this assert because we can have a valid state in the connections folder
// where we've updated our cache but the PostMessages to remove the tray icons
// haven't come through yet.
//
}
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: OnTrayWmCreate
//
// Purpose: Tray window message handler for WM_CREATE.
// We will perform the connection enumeration and create the
// appropriate taskbar icons, including the generic connection
// icon if no connections were present.
//
// Arguments:
// hwnd [in] Tray window
//
// Returns:
//
// Author: jeffspr 14 Dec 1997
//
// Notes:
//
LRESULT OnTrayWmCreate(HWND hwnd)
{
TraceFileFunc(ttidSystray);
BOOL fResult = 0;
DWORD dwIconsAdded = 0;
// Do the connections enumeration and add the icons.
// fResult it for debugging only. We'll always do everything here
//
fResult = FInitFoldEnumerator(hwnd, &dwIconsAdded);
g_ccl.EnsureIconsPresent();
ac_Register(hwnd); // homenet auto config service
return 0;
}
//+---------------------------------------------------------------------------
//
// Function: OnTrayWmDestroy
//
// Purpose: Tray window message handler for WM_DESTROY.
// We will perform the connection enumeration and create the
// appropriate taskbar icons, including the generic connection
// icon if no connections were present.
//
// Arguments:
// hwnd [in] Tray window
//
// Returns:
//
// Author: jeffspr 14 Dec 1997
//
// Notes:
//
LRESULT OnTrayWmDestroy(HWND hwnd)
{
TraceFileFunc(ttidSystray);
ac_Unregister(hwnd);
return 0;
}
//+---------------------------------------------------------------------------
//
// Function: CTrayUI_WndProc
//
// Purpose: Window proc for the tray's hidden window
//
// Arguments:
// hwnd [in] See windows documentation
// uiMessage [in] See windows documentation
// wParam [in] See windows documentation
// lParam [in] See windows documentation
//
// Returns: See windows documentation
//
// Author: jeffspr 14 Dec 1997
//
// Notes:
//
LRESULT
CALLBACK
CTrayUI_WndProc (
HWND hwnd, // window handle
UINT uiMessage, // type of message
WPARAM wParam, // additional information
LPARAM lParam) // additional information
{
TraceFileFunc(ttidSystray);
switch (uiMessage)
{
case WM_CREATE:
// Note: Move this to a better place.
g_hwndTray = hwnd;
return OnTrayWmCreate(hwnd);
case WM_DESTROY:
return OnTrayWmDestroy(hwnd);
case MYWM_NOTIFYICON:
return OnMyWMNotifyIcon(hwnd, uiMessage, wParam, lParam);
case MYWM_OPENSTATUS:
return OnMyWMOpenStatus(hwnd, wParam, lParam);
case MYWM_ADDTRAYICON:
return OnMyWMAddTrayIcon(hwnd, wParam, lParam);
case MYWM_REMOVETRAYICON:
return OnMyWMRemoveTrayIcon(hwnd, wParam, lParam);
case MYWM_UPDATETRAYICON:
return OnMyWMUpdateTrayIcon(hwnd, wParam, lParam);
case MYWM_SHOWBALLOON:
return OnMyWMShowTrayIconBalloon(hwnd, wParam, lParam);
case MYWM_FLUSHNOOP:
return OnMyWMFlushNoop(hwnd, wParam, lParam);
case WM_DEVICECHANGE:
return ac_DeviceChange(hwnd, uiMessage, wParam, lParam);
default: // Passes it on if unproccessed
return (DefWindowProc (hwnd, uiMessage, wParam, lParam));
}
return (0);
}
//+---------------------------------------------------------------------------
//
// Function: HrDoMediaDisconnectedIcon
//
// Purpose: Add a Media-disconnected icon to the tray. We're in the
// state where our cable is unplugged on a LAN adapter and
// we want to inform the user of the situation
//
// Arguments:
// pccfe [in] Our connection
// fShowBalloon [in] Show the balloon tip?
//
// Returns:
//
// Author: jeffspr 14 Jul 1999
//
// Notes:
//
HRESULT HrDoMediaDisconnectedIcon(const CONFOLDENTRY& pccfe, BOOL fShowBalloon)
{
TraceFileFunc(ttidSystray);
HRESULT hr = S_OK;
UINT uiIcon = 0;
TraceTag(ttidSystray, "HrDoMediaDisconnectedIcon");
DWORD dwLockingThreadId = 0;
hr = HrGetTrayIconLock(&(pccfe.GetGuidID()), &uiIcon, &dwLockingThreadId);
if (S_OK == hr)
{
if (uiIcon == BOGUS_TRAY_ICON_ID)
{
TraceTag(ttidSystray, "Adding MediaDisconnected icon for: %S", pccfe.GetName());
NETCON_MEDIATYPE ncm = pccfe.GetNetConMediaType();
if (IsMediaLocalType(ncm) || IsMediaSharedAccessHostType(ncm)) // ics beacon will say disconnected if it is in a unknown state.
{
HICON hiconTray = LoadIcon(_Module.GetResourceInstance(),
MAKEINTRESOURCE(IDI_CFT_DISCONNECTED));
if (hiconTray)
{
ConnListEntry cleFind;
g_ccl.AcquireWriteLock();
hr = g_ccl.HrFindConnectionByGuid(&(pccfe.GetGuidID()), cleFind);
if (S_OK == hr)
{
Assert(!cleFind.ccfe.empty());
g_ccl.HrUpdateTrayBalloonInfoByGuid(&(pccfe.GetGuidID()), BALLOON_USE_NCS, NULL, NULL);
g_ccl.ReleaseWriteLock();
NOTIFYICONDATA nid;
ZeroMemory (&nid, sizeof(nid));
nid.cbSize = sizeof(NOTIFYICONDATA);
nid.hWnd = g_hwndTray;
nid.uID = g_pCTrayUI->m_uiNextIconId++;
nid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_STATE;
nid.uCallbackMessage = MYWM_NOTIFYICON;
nid.hIcon = hiconTray;
if (fShowBalloon)
{
nid.uFlags |= NIF_INFO;
nid.dwInfoFlags = NIIF_INFO | NIIF_NOSOUND;
nid.uTimeout = c_dwBalloonTimeoutSeconds * 1000;
if (lstrlenW(pccfe.GetName()) >= celems(nid.szInfoTitle))
{
lstrcpynW(nid.szInfoTitle, pccfe.GetName(),
celems(nid.szInfoTitle) -
celems(c_szDotDotDot) - 1);
lstrcatW(nid.szInfoTitle, c_szDotDotDot);
}
else
{
lstrcpyW(nid.szInfoTitle, pccfe.GetName());
}
if ( (pccfe.GetNetConMediaType() == NCM_LAN) && (pccfe.GetNetConSubMediaType() == NCSM_WIRELESS) )
{
lstrcpyW(nid.szInfo, SzLoadIds(IDS_CONTRAY_WIRELESS_DISCONN_BALLOON));
}
else
{
lstrcpyW(nid.szInfo, SzLoadIds(IDS_CONTRAY_MEDIA_DISCONN_BALLOON));
}
}
hr = HrShell_NotifyIcon(NIM_ADD, &nid);
if (SUCCEEDED(hr))
{
Assert(!pccfe.empty());
// Update the connection list with the new icon identifier
//
hr = g_ccl.HrUpdateTrayIconDataByGuid(
&(pccfe.GetGuidID()),
NULL,
NULL,
NULL,
nid.uID);
if (SUCCEEDED(hr))
{
hr = g_ccl.HrUpdateTrayBalloonInfoByGuid(&(pccfe.GetGuidID()), BALLOON_USE_NCS, NULL, NULL);
}
TraceTag(ttidSystray, "Successfully added mediadisconnected icon for %S, uiIcon: %d",
pccfe.GetName(), nid.uID);
}
}
else
{
g_ccl.ReleaseWriteLock();
}
}
}
else
{
AssertSz(IsMediaLocalType(pccfe.GetNetConMediaType()) || IsMediaSharedAccessHostType(pccfe.GetNetConMediaType()), "I need a dangling cable, not a phone line");
}
}
else
{
TraceTag(ttidSystray, "Preventing the addition of a duplicate media "
"disconnected icon. uiIcon == %d", uiIcon);
}
ReleaseTrayIconLock(&(pccfe.GetGuidID()));
}
else
{
TraceTag(ttidSystray, "Can't get tray icon lock in HrDoMediaDisconnectedIcon for uiIcon: %d as it has been locked by thread %d", uiIcon, dwLockingThreadId);
// Someone else is already mucking with this icon
hr = S_FALSE;
}
TraceHr(ttidSystray, FAL, hr, FALSE, "HrDoMediaDisconnectedIcon");
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: OnMyWMAddTrayIcon
//
// Purpose: Process the status message for the tray window
//
// Arguments:
// hwnd [in]
// wParam [in] Pointer to CCONFOLDENTRY.
// lParam [in] TRUE if we are to briefly show the balloon.
//
// Returns:
//
// Author:
//
// Notes:
//
//
LRESULT OnMyWMAddTrayIcon(HWND hwnd, WPARAM wParam, LPARAM lParam)
{
TraceFileFunc(ttidSystray);
HRESULT hr = S_OK;
HICON hIcon = NULL;
INetStatisticsEngine * pnseStats = NULL;
IConnectionPoint * pcpStat = NULL;
CConnectionTrayStats * pccts = NULL;
CONFOLDENTRY pccfe;
BOOL fStaticIcon = FALSE;
BOOL fBrieflyShowBalloon = (BOOL) lParam;
NOTIFYICONDATA nid;
UINT uiIcon;
Assert(wParam);
pccfe.InitializeFromItemIdList(reinterpret_cast<LPCITEMIDLIST>(wParam));
::SHFree(reinterpret_cast<LPITEMIDLIST>(wParam));
Assert(!pccfe.empty());
Assert(pccfe.FShouldHaveTrayIconDisplayed());
TraceTag(ttidSystray, "In OnMyWMAddTrayIcon message handler");
if (pccfe.GetNetConStatus() == NCS_MEDIA_DISCONNECTED)
{
hr = HrDoMediaDisconnectedIcon(pccfe, fBrieflyShowBalloon);
goto Exit;
}
// Raid #379459: If logged in as non-admin and incoming, don't show systray icon
if (FIsUserAdmin() ||
!(pccfe.GetCharacteristics() & NCCF_INCOMING_ONLY))
{
g_ccl.AcquireWriteLock();
DWORD dwLockingThreadId = 0;
hr = HrGetTrayIconLock(&(pccfe.GetGuidID()), &uiIcon, &dwLockingThreadId);
if (S_OK == hr)
{
ConnListEntry cle;
hr = g_ccl.HrFindConnectionByGuid(&(pccfe.GetGuidID()), cle);
if (S_OK == hr)
{
g_ccl.HrUpdateTrayBalloonInfoByGuid(&(pccfe.GetGuidID()), BALLOON_USE_NCS, NULL, NULL);
g_ccl.ReleaseWriteLock();
if (uiIcon == BOGUS_TRAY_ICON_ID)
{
// Try to load the branded tray icon, if present
//
if (pccfe.GetCharacteristics() & NCCF_BRANDED)
{
if (cle.pcbi && cle.pcbi->szwTrayIconPath)
{
hIcon = (HICON) LoadImage(
NULL,
cle.pcbi->szwTrayIconPath,
IMAGE_ICON,
0, 0,
LR_LOADFROMFILE);
if (hIcon)
{
// When we create the ConTrayStats object, we'll mark it as having
// a static icon so we don't update it on stat changes.
//
fStaticIcon = TRUE;
}
}
}
// If either the branded icon wasn't present or didn't load, or there
// was no branding to begin with, load the standard icon
//
if (!hIcon)
{
INT iConnIcon = IGetCurrentConnectionTrayIconId(pccfe.GetNetConMediaType(), pccfe.GetNetConStatus(), SMDCF_NULL);
hIcon = g_pCTrayUI->GetCachedHIcon(iConnIcon);
}
}
ZeroMemory (&nid, sizeof(nid));
nid.cbSize = sizeof(NOTIFYICONDATA);
nid.hWnd = g_hwndTray;
nid.uID = g_pCTrayUI->m_uiNextIconId++;
nid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_STATE;
nid.uCallbackMessage = MYWM_NOTIFYICON;
nid.hIcon = hIcon;
// If we're not static, then tell the tray that we're using the cached icons
//
if (!fStaticIcon)
{
nid.dwState = NIS_SHAREDICON;
if (IsMediaRASType(pccfe.GetNetConMediaType()) ||
(NCM_SHAREDACCESSHOST_RAS == pccfe.GetNetConMediaType()) ||
( (NCM_LAN == pccfe.GetNetConSubMediaType()) &&
(NCSM_WIRELESS == pccfe.GetNetConSubMediaType()) )
)
{
nid.dwState |= NIS_SHOWALWAYS;
}
nid.dwStateMask = nid.dwState;
}
// Create the statistics objects
//
hr = HrGetStatisticsEngineForEntry(pccfe, &pnseStats, TRUE);
if (SUCCEEDED(hr))
{
// Advise the interface
hr = HrGetPcpFromPnse(pnseStats, &pcpStat);
if (SUCCEEDED(hr))
{
INetConnectionStatisticsNotifySink * pncsThis;
hr = CConnectionTrayStats::CreateInstance (
pccfe,
nid.uID,
fStaticIcon,
IID_INetConnectionStatisticsNotifySink,
reinterpret_cast<VOID**>(&pncsThis));
if (SUCCEEDED(hr))
{
pccts = reinterpret_cast<CConnectionTrayStats*>(pncsThis);
hr = pnseStats->StartStatistics();
if (SUCCEEDED(hr))
{
// Don't release this. We need to store it with
// the entry so we can UnAdvise later
//
hr = pcpStat->Advise(pncsThis, pccts->GetConPointCookie());
}
}
if (fBrieflyShowBalloon)
{
if ( (NCS_CONNECTED == pccfe.GetNetConStatus()) ||
(NCS_AUTHENTICATION_SUCCEEDED == pccfe.GetNetConStatus()) ||
(NCS_INVALID_ADDRESS == pccfe.GetNetConStatus()) )
{
nid.uFlags |= NIF_INFO;
nid.dwInfoFlags = NIIF_INFO | NIIF_NOSOUND;
nid.uTimeout = c_dwBalloonTimeoutSeconds * 1000;
WCHAR szBalloonStr[MAX_PATH];
UINT idTitle = (NCS_INVALID_ADDRESS == pccfe.GetNetConStatus()) ?
IDS_BALLOON_UNAVAILABLE : IDS_BALLOON_CONNECTED;
int dwMaxNameLen = celems(nid.szInfoTitle) - celems(c_szDotDotDot) - lstrlenW(SzLoadIds(IDS_BALLOON_CONNECTED)) - 1;
DwFormatString(SzLoadIds(idTitle), szBalloonStr, MAX_PATH, pccfe.GetName());
if (lstrlenW(szBalloonStr) >= dwMaxNameLen)
{
lstrcpyW(szBalloonStr, pccfe.GetName()); // Only use the Connection Name
if (lstrlenW(nid.szInfoTitle) >= dwMaxNameLen) // Still bigger?
{
lstrcpynW(nid.szInfoTitle, szBalloonStr, dwMaxNameLen);
lstrcatW(nid.szInfoTitle, c_szDotDotDot);
}
else
{
lstrcpyW(nid.szInfoTitle, szBalloonStr);
}
}
else
{
lstrcpyW(nid.szInfoTitle, szBalloonStr);
}
AssertSz(lstrlenW(nid.szInfoTitle) < celems(nid.szInfoTitle),
"Balloon tooltip text is too long!");
if (pccfe.GetNetConStatus() == NCS_INVALID_ADDRESS)
{
STATMON_ENGINEDATA* pData = NULL;
UINT idString = IDS_CONTRAY_ADDRESS_INVALID_BALLOON;
if (S_OK == pnseStats->GetStatistics(&pData) && pData)
{
if (STATIC_ADDR == pData->SMED_DHCP_ADDRESS_TYPE)
{
idString = IDS_CONTRAY_STATIC_ADDR_INVALID_BALLON;
}
CoTaskMemFree(pData);
}
lstrcpynW(nid.szInfo, SzLoadIds(idString), celems(nid.szInfo));
}
else
{
GetInitialBalloonText(
pnseStats,
nid.szInfo,
celems(nid.szInfo));
}
}
}
// Add the icon itself.
//
TraceTag(ttidSystray, "Adding shared shell icon: uID=%u, hIcon=0x%x",
nid.uID,
nid.hIcon);
hr = HrShell_NotifyIcon(NIM_ADD, &nid);
if (SUCCEEDED(hr) && pccts)
{
Assert(!pccfe.empty());
// Update the connection list with the new icon identifier
//
hr = g_ccl.HrUpdateTrayIconDataByGuid(
&(pccfe.GetGuidID()),
pccts,
pcpStat,
pnseStats,
nid.uID);
if (SUCCEEDED(hr))
{
hr = g_ccl.HrUpdateTrayBalloonInfoByGuid(&(pccfe.GetGuidID()), BALLOON_USE_NCS, NULL, NULL);
}
}
::ReleaseObj(pccts);
::ReleaseObj(pcpStat);
}
::ReleaseObj(pnseStats);
}
}
else
{
g_ccl.ReleaseWriteLock();
}
// Release the lock on the tray icon
//
ReleaseTrayIconLock(&(pccfe.GetGuidID()));
}
else
{
g_ccl.ReleaseWriteLock();
// Could not obtain an icon lock
//
#ifdef DBG
if (S_FALSE == hr)
{
Assert(dwLockingThreadId);
TraceTag(ttidSystray, "Tray icon locked by thread id %d", dwLockingThreadId);
}
else
{
TraceTag(ttidError, "Could not obtain tray icon data for connection %S", pccfe.GetName());
}
#endif
hr = S_FALSE;
}
}
else
{
// Non-admin, or incoming connection
//
hr = S_FALSE;
}
Exit:
TraceHr(ttidSystray, FAL, hr, SUCCEEDED(hr), "OnMyWMAddTrayIcon");
return 0;
}
//+---------------------------------------------------------------------------
//
// Function: OnMyRemoveTrayIcon
//
// Purpose: Process the status message for the tray window
//
// Arguments:
// hwnd []
// wParam []
// lParam []
//
// Returns:
//
// Author:
//
// Notes:
//
//
LRESULT OnMyWMRemoveTrayIcon(HWND hwnd, WPARAM wParam, LPARAM lParam)
{
TraceFileFunc(ttidSystray);
HRESULT hr = E_FAIL;
GUID * pGuid = reinterpret_cast<GUID *>(lParam);
NOTIFYICONDATA nid;
// This is returned from a cle, hence should be locked
#ifdef VERYSTRICTCOMPILE
const CTrayIconData * pTrayIconData = reinterpret_cast<const CTrayIconData *>(wParam);
#else
CTrayIconData * pTrayIconData = reinterpret_cast<CTrayIconData *>(wParam);
#endif
TraceTag(ttidSystray, "In OnMyWMRemoveTrayIcon message handler");
ZeroMemory (&nid, sizeof(nid));
nid.cbSize = sizeof(NOTIFYICONDATA);
nid.hWnd = g_hwndTray;
// We'll make do if this wasn't passed in. What that means is that we may
// have a timing window where we are adding and removing these icons
// at such a rate that the add has occurred before the connection has
// had a chance to add the previous icon (so we don't know to remove it).
//
if (!pTrayIconData)
{
TraceTag(ttidSystray, "No tray icon data found, loading from cache");
Assert(pGuid);
g_ccl.AcquireWriteLock();
ConnListEntry cle;
hr = g_ccl.HrFindConnectionByGuid(pGuid, cle);
if (S_OK == hr)
{
TraceTag(ttidSystray, "Tray icon data found in cache");
Assert(!cle.empty())
if (cle.HasTrayIconData())
{
TraceTag(ttidSystray, "pTrayIconData was valid");
pTrayIconData = new CTrayIconData(*cle.GetTrayIconData());
if (!pTrayIconData)
{
g_ccl.ReleaseWriteLock();
return E_OUTOFMEMORY;
}
cle.DeleteTrayIconData();
g_ccl.HrUpdateConnectionByGuid(pGuid, cle);
}
}
g_ccl.ReleaseWriteLock();
// This is a copy that we should delete
//
delete pGuid;
pGuid = NULL;
}
if (pTrayIconData)
{
nid.uID = pTrayIconData->GetTrayIconId();
TraceTag(ttidSystray, "Removing tray icon with id=%u",
pTrayIconData->GetTrayIconId() );
int nCount = 5;
hr = E_FAIL; // Make sure we get at least one attempt in
while ((nCount--) && (S_OK != hr))
{
hr = HrShell_NotifyIcon(NIM_DELETE, &nid);
if (E_FAIL == hr)
{
TraceTag(ttidSystray, "Tray icon: %d failed in delete via "
"HrShell_NotifyIcon. Will retry shortly", pTrayIconData->GetTrayIconId() );
// Raid #370358
Sleep(500);
}
else
{
TraceTag(ttidSystray, "Tray icon: %d removed succesfully", pTrayIconData->GetTrayIconId());
}
}
// Unadvise the statistics interface
//
if (pTrayIconData->GetConnectionPoint() && pTrayIconData->GetConnectionTrayStats() )
{
pTrayIconData->GetConnectionPoint()->Unadvise(*pTrayIconData->GetConnectionTrayStats()->GetConPointCookie());
}
// Stop the statistics
//
if (pTrayIconData->GetNetStatisticsEngine() )
{
pTrayIconData->GetNetStatisticsEngine()->StopStatistics();
}
// Delete the structure
//
delete pTrayIconData;
}
TraceHr(ttidSystray, FAL, hr, FALSE, "OnMyWMRemoveTrayIcon");
return 0;
}
//+---------------------------------------------------------------------------
//
// Function: OnMyUpdateTrayIcon
//
// Purpose: Process the status message for the tray window
//
// Arguments:
// hwnd []
// wParam []
// lParam []
//
// Returns:
//
// Author:
//
// Notes:
//
//
LRESULT OnMyWMUpdateTrayIcon(HWND hwnd, WPARAM wParam, LPARAM lParam)
{
TraceFileFunc(ttidSystray);
if (g_pCTrayUI)
{
g_pCTrayUI->UpdateTrayIcon((UINT)wParam, (int)lParam);
}
return 0;
}
//+---------------------------------------------------------------------------
//
// Function: OnMyWMShowTrayIconBalloon
//
// Purpose: Puts balloon text on the icon for the tray window
// Change the state of the connection
//
// Arguments:
// hwnd [in]
// wParam [in]
// lParam [in] Point to CTrayBalloon structure
//
// Returns:
//
// Author:
//
// Notes:
//
//
LRESULT OnMyWMShowTrayIconBalloon(HWND hwnd, WPARAM wParam, LPARAM lParam)
{
TraceFileFunc(ttidSystray);
HRESULT hr = S_OK;
HRESULT hrFind = S_OK;
BOOL fLockHeld = FALSE;
Assert(lParam);
if (!lParam)
{
return FALSE;
}
CTrayBalloon * pTrayBalloon = reinterpret_cast<CTrayBalloon *>(lParam);
UINT uiIcon;
DWORD dwLockingThreadId = 0;
hr = HrGetTrayIconLock(&(pTrayBalloon->m_gdGuid), &uiIcon, &dwLockingThreadId);
if (S_OK == hr)
{
if (uiIcon != BOGUS_TRAY_ICON_ID)
{
ConnListEntry cleFind;
g_ccl.AcquireWriteLock();
hrFind = g_ccl.HrFindConnectionByGuid(&(pTrayBalloon->m_gdGuid), cleFind);
if (S_OK == hrFind)
{
Assert(!cleFind.ccfe.empty());
Assert(pTrayBalloon->m_pfnFuncCallback);
g_ccl.HrUpdateTrayBalloonInfoByGuid(&(pTrayBalloon->m_gdGuid), BALLOON_CALLBACK, pTrayBalloon->m_szCookie, pTrayBalloon->m_pfnFuncCallback);
g_ccl.ReleaseWriteLock();
NOTIFYICONDATA nid;
INT iIconResourceId;
iIconResourceId = IGetCurrentConnectionTrayIconId(
cleFind.ccfe.GetNetConMediaType(),
cleFind.ccfe.GetNetConStatus(),
0);
ZeroMemory (&nid, sizeof(nid));
nid.cbSize = sizeof(NOTIFYICONDATA);
nid.hWnd = g_hwndTray;
nid.uID = uiIcon;
nid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_STATE;
nid.hIcon = g_pCTrayUI->GetCachedHIcon(iIconResourceId);
nid.dwState = NIS_SHAREDICON;
nid.dwStateMask = nid.dwState;
nid.uCallbackMessage = MYWM_NOTIFYICON;
// Prepare the balloon data
nid.uFlags |= NIF_INFO;
nid.dwInfoFlags = NIIF_INFO | NIIF_NOSOUND;
nid.uTimeout = pTrayBalloon->m_dwTimeOut;
if (lstrlenW(cleFind.ccfe.GetName()) >= celems(nid.szInfoTitle))
{
lstrcpynW(nid.szInfoTitle, cleFind.ccfe.GetName(),
celems(nid.szInfoTitle) -
celems(c_szDotDotDot) - 1);
lstrcatW(nid.szInfoTitle, c_szDotDotDot);
}
else
{
lstrcpyW(nid.szInfoTitle, cleFind.ccfe.GetName());
}
lstrcpynW(nid.szInfo, pTrayBalloon->m_szMessage, celems(nid.szInfo));
// Display the balloon
HrShell_NotifyIcon(NIM_MODIFY, &nid);
}
else
{
g_ccl.ReleaseWriteLock();
}
}
else
{
TraceTag(ttidSystray, "No existing icon data!!!");
}
// Release the lock on the tray icon
//
ReleaseTrayIconLock(&(pTrayBalloon->m_gdGuid));
}
else
{
TraceTag(ttidSystray, "Can't get tray icon lock in OnMyWMShowTrayIconBalloon for uiIcon: %d as it has been locked by thread %d", uiIcon, dwLockingThreadId);
}
delete pTrayBalloon;
TraceHr(ttidSystray, FAL, hr, SUCCEEDED(hr), "OnMyWMShowTrayIconBalloon");
return 0;
}
//+---------------------------------------------------------------------------
//
// Function: OnMyWMOpenStatus
//
// Purpose: Process the status message for the tray window
//
// Arguments:
// hwnd []
// wParam []
// lParam []
//
// Returns:
//
// Author: jeffspr 15 Dec 1997
//
// Notes:
//
//
LRESULT OnMyWMOpenStatus(HWND hwnd, WPARAM wParam, LPARAM lParam)
{
TraceFileFunc(ttidSystray);
Assert(wParam);
CONFOLDENTRY pccfe;
pccfe.InitializeFromItemIdList(reinterpret_cast<LPCITEMIDLIST>(wParam));
BOOL fCreateEngine = (BOOL)lParam;
Assert(!pccfe.empty());
HRESULT hr;
ConnListEntry cle;
hr = g_ccl.HrFindConnectionByGuid(&(pccfe.GetGuidID()), cle);
if (S_OK == hr)
{
if (FHasPermission(NCPERM_Statistics))
{
if (!pccfe.empty())
{
INetStatisticsEngine* pnseNew;
hr = HrGetStatisticsEngineForEntry(pccfe, &pnseNew, fCreateEngine);
if (SUCCEEDED(hr))
{
hr = pnseNew->ShowStatusMonitor();
ReleaseObj(pnseNew);
}
else
{
TraceTag(ttidSystray,
"OnMyWMOpenStatus: Statistics Engine for connection %S has been removed."
"It's ok if this connection is being disconnected", pccfe.GetName());
}
}
}
}
return 0;
}
LRESULT OnMyWMNotifyIcon(HWND hwnd, UINT uiMessage, WPARAM wParam, LPARAM lParam)
{
TraceFileFunc(ttidSystray);
UINT uiIcon;
UINT uiMouseMsg;
uiIcon = (UINT) wParam;
uiMouseMsg = (UINT) lParam;
switch (uiMouseMsg)
{
case WM_MOUSEMOVE:
FormatToolTip(hwnd, uiIcon);
break;
case WM_RBUTTONUP:
OnTaskBarIconRButtonUp(hwnd, uiIcon);
break;
case NIN_BALLOONUSERCLICK:
OnTaskBarIconBalloonClick(hwnd, uiIcon);
break;
case WM_LBUTTONUP:
OnTaskBarIconLButtonDblClk(hwnd, uiIcon);
break;
}
return 0;
}
VOID OnTaskBarIconRButtonUp(HWND hwnd, UINT uiIcon)
{
TraceFileFunc(ttidSystray);
POINT pt;
GetCursorPos(&pt);
(VOID) HrOpenContextMenu(hwnd, &pt, uiIcon);
}
//+---------------------------------------------------------------------------
//
// Function: OnTaskBarIconLButtonDblClk
//
// Purpose: Message handler for the Left-button double click from
// a tray icon
//
// Arguments:
// hwnd [] Our window handle
// uiIcon [] Our Icon ID.
//
// Returns:
//
// Author: jeffspr 12 Jan 1998
//
// Notes:
//
VOID OnTaskBarIconLButtonDblClk(HWND hwnd, UINT uiIcon)
{
TraceFileFunc(ttidSystray);
HRESULT hr = S_OK;
if (GetKeyState(VK_SHIFT))
{
// Uh, nothing special to do here yet, but just in case...
}
// Perform the default context menu action
// Find the connection info based on the tray icon id.
//
ConnListEntry cle;
hr = g_ccl.HrFindConnectionByTrayIconId(uiIcon, cle);
if (hr == S_OK)
{
Assert(!cle.ccfe.empty());
if (!cle.ccfe.empty())
{
if (cle.ccfe.GetNetConStatus() == NCS_MEDIA_DISCONNECTED)
{
if (IsMediaLocalType(cle.ccfe.GetNetConMediaType()) &&
(NCSM_WIRELESS == cle.ccfe.GetNetConSubMediaType()) )
{
PCONFOLDPIDLVEC pcfpVec;
PCONFOLDPIDL pcfp;
hr = cle.ccfe.ConvertToPidl(pcfp);
if (SUCCEEDED(hr))
{
pcfpVec.insert(pcfpVec.begin(), pcfp);
HrOnCommandWZCDlgShow(pcfpVec,
g_hwndTray,
NULL);
}
}
else
{
hr = HrOpenConnectionsFolder();
}
}
else
{
switch(c_idDefaultCMCommand)
{
case CMIDM_TRAY_STATUS:
hr = HrOnCommandStatusInternal(cle.ccfe, FALSE);
break;
default:
AssertSz(FALSE, "Default tray context menu item unhandled");
break;
}
}
}
}
return;
}
DWORD WINAPI OnTaskBarIconBalloonClickThread(LPVOID lpParam)
{
HRESULT hr = E_FAIL;
CTrayBalloon *pTrayBalloon = reinterpret_cast<CTrayBalloon *>(lpParam);
Assert(pTrayBalloon);
FNBALLOONCLICK *pFNBalloonClick;
pFNBalloonClick = pTrayBalloon->m_pfnFuncCallback;
if (pFNBalloonClick)
{
hr = (pFNBalloonClick)(&(pTrayBalloon->m_gdGuid), pTrayBalloon->m_szAdapterName, pTrayBalloon->m_szCookie);
}
if (E_PENDING == hr)
{
MSG msg;
while (GetMessage (&msg, 0, 0, 0))
{
DispatchMessage (&msg);
}
hr = S_OK;
}
delete pTrayBalloon;
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: OnTaskBarIconBalloonClick
//
// Purpose: Message handler for the balloon click from
// a tray icon
//
// Arguments:
// hwnd [] Our window handle
// uiIcon [] Our Icon ID.
//
// Returns:
//
// Author: deon 20 Mar 2001
//
// Notes:
//
VOID OnTaskBarIconBalloonClick(HWND hwnd, UINT uiIcon)
{
TraceFileFunc(ttidSystray);
HRESULT hr = S_OK;
if (GetKeyState(VK_SHIFT))
{
// Uh, nothing special to do here yet, but just in case...
}
// Perform the default context menu action
// Find the connection info based on the tray icon id.
//
ConnListEntry cle;
hr = g_ccl.HrFindConnectionByTrayIconId(uiIcon, cle);
if (hr == S_OK)
{
Assert(!cle.ccfe.empty());
if (!cle.ccfe.empty())
{
if (!cle.GetTrayIconData())
{
ASSERT (0);
}
switch ((cle.GetTrayIconData())->GetLastBalloonMessage())
{
case BALLOON_NOTHING:
AssertSz(NULL, "You didn't set the balloon");
break;
case BALLOON_CALLBACK:
{
CTrayBalloon *pTrayBalloon = new CTrayBalloon();
if (pTrayBalloon)
{
pTrayBalloon->m_gdGuid = cle.ccfe.GetGuidID();
pTrayBalloon->m_szCookie = SysAllocStringByteLen(reinterpret_cast<LPCSTR>(cle.GetTrayIconData()->GetLastBalloonCookie()), SysStringByteLen(cle.GetTrayIconData()->GetLastBalloonCookie()));
pTrayBalloon->m_pfnFuncCallback = cle.GetTrayIconData()->GetLastBalloonFunction();
pTrayBalloon->m_dwTimeOut= 0;
pTrayBalloon->m_szAdapterName = cle.ccfe.GetName();
CreateThread(NULL, STACK_SIZE_SMALL, OnTaskBarIconBalloonClickThread, pTrayBalloon, 0, NULL);
}
}
break;
case BALLOON_USE_NCS:
if (cle.ccfe.GetNetConStatus() == NCS_MEDIA_DISCONNECTED)
{
Assert(c_idDefaultDisconCMCommand == CMIDM_OPEN_CONNECTIONS_FOLDER);
hr = HrOpenConnectionsFolder();
}
else
{
switch(c_idDefaultCMCommand)
{
case CMIDM_TRAY_STATUS:
hr = HrOnCommandStatusInternal(cle.ccfe, FALSE);
break;
default:
AssertSz(FALSE, "Default tray context menu item unhandled");
break;
}
}
break;
default:
ASSERT (0);
break;
}
}
}
return;
}
//+---------------------------------------------------------------------------
//
// Function: OnMyWMFlushNoop
//
// Purpose: Process the MYWM_FLUSHNOOP message for the tray. This is used
// via SendMessage to clear the tray message queue.
//
// Arguments:
// hwnd [in] Our hwnd
// wParam [in] Unused
// lParam [in] Unused
//
// Returns:
//
// Author: jeffspr 30 Aug 1999
//
// Notes:
//
LRESULT OnMyWMFlushNoop(HWND hwnd, WPARAM wParam, LPARAM lParam)
{
TraceFileFunc(ttidSystray);
TraceTag(ttidSystray,
"Tray received tray FLUSH Noop. This should clear the tray message loop");
return 0;
}
//+---------------------------------------------------------------------------
//
// Function: FlushTrayPosts
//
// Purpose: Flush the tray message queue by doing a SendMessage of a NOOP
//
// Arguments:
// hwnd [in] Where to post to.
//
// Returns:
//
// Author: jeffspr 8 Sep 1999
//
// Notes:
//
VOID FlushTrayPosts(HWND hwnd)
{
TraceFileFunc(ttidSystray);
// Flush the tray posts
//
SendMessage(hwnd, MYWM_FLUSHNOOP, (WPARAM) 0, (LPARAM) 0);
}
BOOL FInitFoldEnumerator(HWND hwnd, DWORD * pdwIconsAdded)
{
TraceFileFunc(ttidSystray);
BOOL fReturn = FALSE;
HRESULT hr = S_OK;
CConnectionFolderEnum * pCFEnum = NULL;
PCONFOLDPIDL pidlCon;
DWORD dwFetched = 0;
DWORD dwIconsAdded = 0;
PCONFOLDPIDLFOLDER pidlFolder;
NETCFG_TRY
// Create the IEnumIDList object (CConnectionFolderEnum)
//
hr = CConnectionFolderEnum::CreateInstance (
IID_IEnumIDList,
(VOID **)&pCFEnum);
if (SUCCEEDED(hr))
{
Assert(pCFEnum);
// Call the PidlInitialize function to allow the enumeration
// object to copy the list.
//
PCONFOLDPIDLFOLDER pcfpEmpty;
pCFEnum->PidlInitialize(TRUE, pcfpEmpty, CFCOPT_ENUMALL);
}
if (SUCCEEDED(hr))
{
while (SUCCEEDED(hr) && (S_FALSE != hr))
{
// Clear out the previous results, if any.
//
pidlCon.Clear();
dwFetched = 0;
// Get the next connection
//
LPITEMIDLIST pTempItemIdList;
hr = pCFEnum->Next(1, &pTempItemIdList, &dwFetched);
if (S_OK == hr)
{
pidlCon.InitializeFromItemIDList(pTempItemIdList);
const PCONFOLDPIDL& pcfp = pidlCon;
// If it's not a wizard pidl, then update the
// icon data.
//
if (WIZARD_NOT_WIZARD == pcfp->wizWizard)
{
// If the folder pidl hasn't already been loaded
// then get it
//
if (pidlFolder.empty())
{
hr = HrGetConnectionsFolderPidl(pidlFolder);
}
// Assuming that succeeded (or hr will be S_OK if
// the HrGet... wasn't called)
//
if (SUCCEEDED(hr))
{
// Refresh this item -- this will make the desktop shortcuts
// update to the correct state.
//
RefreshFolderItem(pidlFolder, pidlCon, pidlCon, TRUE);
}
}
}
}
}
if (SUCCEEDED(hr))
{
// Normalize the return code.
//
hr = S_OK;
fReturn = TRUE;
// If the caller wants the fetched count
//
if (pdwIconsAdded)
{
*pdwIconsAdded = dwIconsAdded;
}
}
ReleaseObj(pCFEnum);
NETCFG_CATCH(hr)
TraceHr(ttidError, FAL, hr, FALSE, "FInitFoldEnumerator");
return fReturn;
}
HICON CTrayUI::GetCachedHIcon(INT iIconResourceId)
{
TraceFileFunc(ttidSystray);
CExceptionSafeLock esLock(&m_csLock);
HICON hIcon = m_mapIdToHicon [iIconResourceId];
if (!hIcon)
{
TraceTag(ttidSystray, "Loading HICON for resource id %u and adding it to our map.",
iIconResourceId);
hIcon = LoadIcon(_Module.GetResourceInstance(),
MAKEINTRESOURCE(iIconResourceId));
//AssertSz (hIcon, "Couldn't load a tray icon. You may ignore this "
// "assert and a default icon will be used.");
if (!hIcon)
{
hIcon = LoadIcon(_Module.GetResourceInstance(),
MAKEINTRESOURCE(IDI_LB_GEN_S_16));
AssertSz (hIcon, "Okay, now you're hosed. Couldn't load the "
"default icon either. email jeffspr.");
}
m_mapIdToHicon [iIconResourceId] = hIcon;
// Add a hidden icon to the tray so that the shell will cache it.
//
NOTIFYICONDATA nid;
ZeroMemory (&nid, sizeof(nid));
nid.cbSize = sizeof(NOTIFYICONDATA);
nid.hWnd = g_hwndTray;
nid.uID = m_uiNextHiddenIconId--;
nid.uFlags = NIF_ICON | NIF_STATE;
nid.hIcon = hIcon;
nid.dwState = NIS_HIDDEN;
nid.dwStateMask = nid.dwState;
/*
nid.uFlags |= NIF_TIP;
wsprintfW(nid.szTip, L"hidden: uID=%u, hIcon=0x%x", nid.uID, nid.hIcon);
*/
TraceTag(ttidSystray, "Adding hidden shell icon: uID=%u, hIcon=0x%x",
nid.uID,
nid.hIcon);
HRESULT hr = HrShell_NotifyIcon(NIM_ADD, &nid);
if (SUCCEEDED(hr))
{
// We can now destroy the icon. This looks weird, but we're only
// going to use the hIcon for passing to Shell_NotifyIcon again
// when we add the shared icon with a different uID.
//
DestroyIcon(hIcon);
}
}
Assert (hIcon);
return hIcon;
}
VOID CTrayUI::UpdateTrayIcon(UINT uiTrayIconId,
INT iIconResourceId)
{
TraceFileFunc(ttidSystray);
NOTIFYICONDATA nid;
ZeroMemory (&nid, sizeof(nid));
nid.cbSize = sizeof(NOTIFYICONDATA);
nid.hWnd = g_hwndTray;
nid.uID = uiTrayIconId;
nid.uFlags = NIF_ICON | NIF_STATE;
nid.hIcon = GetCachedHIcon(iIconResourceId);
nid.dwState = NIS_SHAREDICON;
nid.dwStateMask = nid.dwState;
Shell_NotifyIcon(NIM_MODIFY, &nid);
}
VOID GetInitialBalloonText(INetStatisticsEngine* pnse,
PWSTR pszBuf,
DWORD dwSize)
{
TraceFileFunc(ttidSystray);
*pszBuf = 0;
if (pnse)
{
STATMON_ENGINEDATA* pData = NULL;
HRESULT hr = pnse->GetStatistics(&pData);
if (SUCCEEDED(hr) && pData)
{
if (pData->SMED_802_11_SSID && pData->SMED_802_11_SIGNAL_STRENGTH)
{
DwFormatString(SzLoadIds(IDS_TOOLTIP_WIRELESS_CONNECTED), pszBuf, dwSize,
pData->SMED_802_11_SSID, PszGetRSSIString(pData->SMED_802_11_SIGNAL_STRENGTH));
}
else
if ((pData->SMED_SPEEDTRANSMITTING>0) || (pData->SMED_SPEEDRECEIVING>0))
{
WCHAR pszValue [64];
// Format the transmitting (and possibly the receiving) speed
// into the buffer.
//
FormatTransmittingReceivingSpeed (
pData->SMED_SPEEDTRANSMITTING,
pData->SMED_SPEEDRECEIVING,
pszValue);
DwFormatString(SzLoadIds(IDS_TOOLTIP_LINE_SPEED), pszBuf, dwSize, pszValue);
}
CoTaskMemFree(pData);
}
}
// Provide a default.
//
if (!*pszBuf)
{
lstrcpyW(pszBuf, SzLoadIds(IDS_CONTRAY_INITIAL_BALLOON));
}
}
//+---------------------------------------------------------------------------
//
// Function: HrAddTrayExtension
//
// Purpose: Add the tray extension to the Shell's delay load key
//
// Arguments:
// (none)
//
// Returns:
//
// Author: jeffspr 28 Jul 1998
//
// Notes:
//
HRESULT HrAddTrayExtension()
{
TraceFileFunc(ttidSystray);
HRESULT hr = S_OK;
HKEY hkeyDelayLoad = NULL;
hr = HrRegCreateKeyEx(HKEY_LOCAL_MACHINE, c_szDelayLoadKey,
REG_OPTION_NON_VOLATILE, KEY_WRITE,
NULL, &hkeyDelayLoad, NULL);
if (SUCCEEDED(hr))
{
hr = HrRegSetString(hkeyDelayLoad, c_szDelayLoadName, c_szDelayLoadClassID);
RegCloseKey(hkeyDelayLoad);
}
TraceHr(ttidSystray, FAL, hr, FALSE, "HrAddTrayExtension");
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: HrRemoveTrayExtension
//
// Purpose: Remove the tray extension from the shell's delay load key
//
// Arguments:
// (none)
//
// Returns:
//
// Author: jeffspr 28 Jul 1998
//
// Notes:
//
HRESULT HrRemoveTrayExtension()
{
TraceFileFunc(ttidSystray);
HRESULT hr = S_OK;
HKEY hkeyDelayLoad = NULL;
hr = HrRegCreateKeyEx(HKEY_LOCAL_MACHINE, c_szDelayLoadKey,
REG_OPTION_NON_VOLATILE, KEY_WRITE,
NULL, &hkeyDelayLoad, NULL);
if (SUCCEEDED(hr))
{
(void) HrRegDeleteValue(hkeyDelayLoad, c_szDelayLoadName);
RegCloseKey(hkeyDelayLoad);
}
TraceHr(ttidSystray, FAL, hr, FALSE, "HrRemoveTrayExtension");
return hr;
}