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