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
CSysTray()23 CSysTray::CSysTray() : dwServicesEnabled(0)
24 {
25 wm_SHELLHOOK = RegisterWindowMessageW(L"SHELLHOOK");
26 wm_DESTROYWINDOW = RegisterWindowMessageW(L"CSysTray_DESTROY");
27 }
28
~CSysTray()29 CSysTray::~CSysTray()
30 {
31 }
32
GetServicesEnabled()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
EnableService(DWORD dwServiceFlag,BOOL bEnable)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
IsServiceEnabled(DWORD dwServiceFlag)92 BOOL CSysTray::IsServiceEnabled(DWORD dwServiceFlag)
93 {
94 return (this->dwServicesEnabled & dwServiceFlag);
95 }
96
InitNetShell()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
ShutdownNetShell()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
InitIcons()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
ShutdownIcons()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
UpdateIcons()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
ProcessIconMessage(UINT uMsg,WPARAM wParam,LPARAM lParam,LRESULT & lResult)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 *--*/
NotifyIcon(INT code,UINT uId,HICON hIcon,LPCWSTR szTip,DWORD dwstate)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
s_SysTrayThreadProc(PVOID param)230 DWORD WINAPI CSysTray::s_SysTrayThreadProc(PVOID param)
231 {
232 CSysTray * st = (CSysTray*) param;
233 return st->SysTrayThreadProc();
234 }
235
SysTrayMessageLoop()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
SysTrayThreadProc()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
CreateSysTrayThread()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
DestroySysTrayWindow()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 ***
QueryStatus(const GUID * pguidCmdGroup,ULONG cCmds,OLECMD prgCmds[],OLECMDTEXT * pCmdText)294 HRESULT STDMETHODCALLTYPE CSysTray::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds [], OLECMDTEXT *pCmdText)
295 {
296 UNIMPLEMENTED;
297 return S_OK;
298 }
299
Exec(const GUID * pguidCmdGroup,DWORD nCmdID,DWORD nCmdexecopt,VARIANT * pvaIn,VARIANT * pvaOut)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
ProcessWindowMessage(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam,LRESULT & lResult,DWORD dwMsgMapID)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