1 /* 2 * PROJECT: ReactOS system libraries 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: dll/shellext/stobject/csystray.cpp 5 * PURPOSE: Systray shell service object implementation 6 * PROGRAMMERS: David Quintana <gigaherz@gmail.com> 7 * Shriraj Sawant a.k.a SR13 <sr.official@hotmail.com> 8 */ 9 10 #include "precomp.h" 11 12 #include <regstr.h> 13 #include <undocshell.h> 14 #include <shellutils.h> 15 16 SysTrayIconHandlers_t g_IconHandlers [] = { 17 { VOLUME_SERVICE_FLAG, Volume_Init, Volume_Shutdown, Volume_Update, Volume_Message }, 18 { HOTPLUG_SERVICE_FLAG, Hotplug_Init, Hotplug_Shutdown, Hotplug_Update, Hotplug_Message }, 19 { POWER_SERVICE_FLAG, Power_Init, Power_Shutdown, Power_Update, Power_Message } 20 }; 21 const int g_NumIcons = _countof(g_IconHandlers); 22 23 CSysTray::CSysTray() : dwServicesEnabled(0) 24 { 25 wm_SHELLHOOK = RegisterWindowMessageW(L"SHELLHOOK"); 26 wm_DESTROYWINDOW = RegisterWindowMessageW(L"CSysTray_DESTROY"); 27 } 28 29 CSysTray::~CSysTray() 30 { 31 } 32 33 VOID CSysTray::GetServicesEnabled() 34 { 35 HKEY hKey; 36 DWORD dwSize; 37 38 /* Enable power, volume and hotplug by default */ 39 this->dwServicesEnabled = POWER_SERVICE_FLAG | VOLUME_SERVICE_FLAG | HOTPLUG_SERVICE_FLAG; 40 41 if (RegCreateKeyExW(HKEY_CURRENT_USER, REGSTR_PATH_SYSTRAY, 42 0, 43 NULL, 44 REG_OPTION_NON_VOLATILE, 45 KEY_READ, 46 NULL, 47 &hKey, 48 NULL) == ERROR_SUCCESS) 49 { 50 dwSize = sizeof(DWORD); 51 RegQueryValueExW(hKey, 52 L"Services", 53 NULL, 54 NULL, 55 (LPBYTE)&this->dwServicesEnabled, 56 &dwSize); 57 58 RegCloseKey(hKey); 59 } 60 } 61 62 VOID CSysTray::EnableService(DWORD dwServiceFlag, BOOL bEnable) 63 { 64 HKEY hKey; 65 66 if (bEnable) 67 this->dwServicesEnabled |= dwServiceFlag; 68 else 69 this->dwServicesEnabled &= ~dwServiceFlag; 70 71 if (RegCreateKeyExW(HKEY_CURRENT_USER, 72 L"Software\\Microsoft\\Windows\\CurrentVersion\\Applets\\SysTray", 73 0, 74 NULL, 75 REG_OPTION_NON_VOLATILE, 76 KEY_WRITE, 77 NULL, 78 &hKey, 79 NULL) == ERROR_SUCCESS) 80 { 81 RegSetValueExW(hKey, 82 L"Services", 83 0, 84 REG_DWORD, 85 (LPBYTE)&this->dwServicesEnabled, 86 sizeof(DWORD)); 87 88 RegCloseKey(hKey); 89 } 90 } 91 92 BOOL CSysTray::IsServiceEnabled(DWORD dwServiceFlag) 93 { 94 return (this->dwServicesEnabled & dwServiceFlag); 95 } 96 97 HRESULT CSysTray::InitNetShell() 98 { 99 HRESULT hr = CoCreateInstance(CLSID_ConnectionTray, 0, 1u, IID_PPV_ARG(IOleCommandTarget, &pctNetShell)); 100 if (FAILED(hr)) 101 return hr; 102 103 return pctNetShell->Exec(&CGID_ShellServiceObject, 104 OLECMDID_NEW, 105 OLECMDEXECOPT_DODEFAULT, NULL, NULL); 106 } 107 108 HRESULT CSysTray::ShutdownNetShell() 109 { 110 if (!pctNetShell) 111 return S_FALSE; 112 HRESULT hr = pctNetShell->Exec(&CGID_ShellServiceObject, 113 OLECMDID_SAVE, 114 OLECMDEXECOPT_DODEFAULT, NULL, NULL); 115 pctNetShell.Release(); 116 return hr; 117 } 118 119 HRESULT CSysTray::InitIcons() 120 { 121 TRACE("Initializing Notification icons...\n"); 122 for (int i = 0; i < g_NumIcons; i++) 123 { 124 if (this->dwServicesEnabled & g_IconHandlers[i].dwServiceFlag) 125 { 126 HRESULT hr = g_IconHandlers[i].pfnInit(this); 127 if (FAILED(hr)) 128 return hr; 129 } 130 } 131 132 MouseKeys_Init(this); 133 134 return InitNetShell(); 135 } 136 137 HRESULT CSysTray::ShutdownIcons() 138 { 139 TRACE("Shutting down Notification icons...\n"); 140 for (int i = 0; i < g_NumIcons; i++) 141 { 142 if (this->dwServicesEnabled & g_IconHandlers[i].dwServiceFlag) 143 { 144 this->dwServicesEnabled &= ~g_IconHandlers[i].dwServiceFlag; 145 HRESULT hr = g_IconHandlers[i].pfnShutdown(this); 146 FAILED_UNEXPECTEDLY(hr); 147 } 148 } 149 150 MouseKeys_Shutdown(this); 151 152 return ShutdownNetShell(); 153 } 154 155 HRESULT CSysTray::UpdateIcons() 156 { 157 TRACE("Updating Notification icons...\n"); 158 for (int i = 0; i < g_NumIcons; i++) 159 { 160 if (this->dwServicesEnabled & g_IconHandlers[i].dwServiceFlag) 161 { 162 HRESULT hr = g_IconHandlers[i].pfnUpdate(this); 163 if (FAILED(hr)) 164 return hr; 165 } 166 } 167 168 return S_OK; 169 } 170 171 HRESULT CSysTray::ProcessIconMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT &lResult) 172 { 173 for (int i = 0; i < g_NumIcons; i++) 174 { 175 HRESULT hr = g_IconHandlers[i].pfnMessage(this, uMsg, wParam, lParam, lResult); 176 if (FAILED(hr)) 177 return hr; 178 179 if (hr == S_OK) 180 return hr; 181 } 182 183 // Not handled by anyone, so return accordingly. 184 return S_FALSE; 185 } 186 187 /*++ 188 * @name NotifyIcon 189 * 190 * Basically a Shell_NotifyIcon wrapper. 191 * Based on the parameters provided, it changes the current state of the notification icon. 192 * 193 * @param code 194 * Determines whether to add, delete or modify the notification icon (represented by uId). 195 * @param uId 196 * Represents the particular notification icon. 197 * @param hIcon 198 * A handle to an icon for the notification object. 199 * @param szTip 200 * A string for the tooltip of the notification. 201 * @param dwstate 202 * Determines whether to show or hide the notification icon. 203 * 204 * @return The error code. 205 * 206 *--*/ 207 HRESULT CSysTray::NotifyIcon(INT code, UINT uId, HICON hIcon, LPCWSTR szTip, DWORD dwstate) 208 { 209 NOTIFYICONDATA nim = { 0 }; 210 211 TRACE("NotifyIcon code=%d, uId=%d, hIcon=%p, szTip=%S\n", code, uId, hIcon, szTip); 212 213 nim.cbSize = sizeof(nim); 214 nim.uFlags = NIF_MESSAGE | NIF_ICON | NIF_STATE | NIF_TIP; 215 nim.hIcon = hIcon; 216 nim.uID = uId; 217 nim.uCallbackMessage = uId; 218 nim.dwState = dwstate; 219 nim.dwStateMask = NIS_HIDDEN; 220 nim.hWnd = m_hWnd; 221 nim.uVersion = NOTIFYICON_VERSION; 222 if (szTip) 223 StringCchCopy(nim.szTip, _countof(nim.szTip), szTip); 224 else 225 nim.szTip[0] = 0; 226 BOOL ret = Shell_NotifyIcon(code, &nim); 227 return ret ? S_OK : E_FAIL; 228 } 229 230 DWORD WINAPI CSysTray::s_SysTrayThreadProc(PVOID param) 231 { 232 CSysTray * st = (CSysTray*) param; 233 return st->SysTrayThreadProc(); 234 } 235 236 HRESULT CSysTray::SysTrayMessageLoop() 237 { 238 BOOL ret; 239 MSG msg; 240 241 while ((ret = GetMessage(&msg, NULL, 0, 0)) != 0) 242 { 243 if (ret < 0) 244 break; 245 246 TranslateMessage(&msg); 247 DispatchMessage(&msg); 248 } 249 250 return S_OK; 251 } 252 253 HRESULT CSysTray::SysTrayThreadProc() 254 { 255 WCHAR strFileName[MAX_PATH]; 256 GetModuleFileNameW(g_hInstance, strFileName, MAX_PATH); 257 HMODULE hLib = LoadLibraryW(strFileName); 258 259 CoInitializeEx(NULL, COINIT_DISABLE_OLE1DDE | COINIT_APARTMENTTHREADED); 260 261 Create(NULL); 262 263 HRESULT ret = SysTrayMessageLoop(); 264 265 CoUninitialize(); 266 267 Release(); 268 FreeLibraryAndExitThread(hLib, ret); 269 } 270 271 HRESULT CSysTray::CreateSysTrayThread() 272 { 273 TRACE("CSysTray Init TODO: Initialize tray icon handlers.\n"); 274 AddRef(); 275 276 HANDLE hThread = CreateThread(NULL, 0, s_SysTrayThreadProc, this, 0, NULL); 277 278 CloseHandle(hThread); 279 280 return S_OK; 281 } 282 283 HRESULT CSysTray::DestroySysTrayWindow() 284 { 285 if (!DestroyWindow()) 286 { 287 // Window is from another thread, ask it politely to destroy itself: 288 SendMessage(wm_DESTROYWINDOW); 289 } 290 return S_OK; 291 } 292 293 // *** IOleCommandTarget methods *** 294 HRESULT STDMETHODCALLTYPE CSysTray::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds [], OLECMDTEXT *pCmdText) 295 { 296 UNIMPLEMENTED; 297 return S_OK; 298 } 299 300 HRESULT STDMETHODCALLTYPE CSysTray::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut) 301 { 302 if (!IsEqualGUID(*pguidCmdGroup, CGID_ShellServiceObject)) 303 return E_FAIL; 304 305 switch (nCmdID) 306 { 307 case OLECMDID_NEW: // init 308 return CreateSysTrayThread(); 309 case OLECMDID_SAVE: // shutdown 310 return DestroySysTrayWindow(); 311 } 312 return S_OK; 313 } 314 315 BOOL CSysTray::ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT &lResult, DWORD dwMsgMapID) 316 { 317 HRESULT hr; 318 319 if (hWnd != m_hWnd) 320 return FALSE; 321 322 if (wm_DESTROYWINDOW && uMsg == wm_DESTROYWINDOW) 323 { 324 return DestroyWindow(); 325 } 326 327 if (wm_SHELLHOOK && uMsg == wm_SHELLHOOK) 328 { 329 if (wParam == HSHELL_ACCESSIBILITYSTATE && lParam == ACCESS_MOUSEKEYS) 330 { 331 MouseKeys_Update(this); 332 } 333 lResult = 0L; 334 return TRUE; 335 } 336 337 switch (uMsg) 338 { 339 case WM_NCCREATE: 340 case WM_NCDESTROY: 341 return FALSE; 342 343 case WM_CREATE: 344 GetServicesEnabled(); 345 InitIcons(); 346 SetTimer(1, 2000, NULL); 347 RegisterShellHookWindow(hWnd); 348 return TRUE; 349 350 case WM_TIMER: 351 if (wParam == 1) 352 UpdateIcons(); 353 else 354 ProcessIconMessage(uMsg, wParam, lParam, lResult); 355 return TRUE; 356 357 case WM_SETTINGCHANGE: 358 if (wParam == SPI_SETMOUSEKEYS) 359 { 360 MouseKeys_Update(this); 361 } 362 break; 363 364 case WM_DESTROY: 365 KillTimer(1); 366 DeregisterShellHookWindow(hWnd); 367 ShutdownIcons(); 368 PostQuitMessage(0); 369 return TRUE; 370 } 371 372 TRACE("SysTray message received %u (%08p %08p)\n", uMsg, wParam, lParam); 373 374 hr = ProcessIconMessage(uMsg, wParam, lParam, lResult); 375 if (FAILED(hr)) 376 return FALSE; 377 378 return (hr == S_OK); 379 } 380