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