//+--------------------------------------------------------------------------- // // 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 #include #include #include #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(wParam)); ::SHFree(reinterpret_cast(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(&pncsThis)); if (SUCCEEDED(hr)) { pccts = reinterpret_cast(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(lParam); NOTIFYICONDATA nid; // This is returned from a cle, hence should be locked #ifdef VERYSTRICTCOMPILE const CTrayIconData * pTrayIconData = reinterpret_cast(wParam); #else CTrayIconData * pTrayIconData = reinterpret_cast(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(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(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(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(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; }