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