Reactos
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}