xref: /reactos/dll/shellext/stobject/csystray.cpp (revision 27bc3110)
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