xref: /reactos/dll/shellext/stobject/csystray.cpp (revision d5b576b2)
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_Init, Volume_Shutdown, Volume_Update, Volume_Message },
17         { Hotplug_Init, Hotplug_Shutdown, Hotplug_Update, Hotplug_Message },
18         { 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 HRESULT CSysTray::InitNetShell()
26 {
27     HRESULT hr = CoCreateInstance(CLSID_ConnectionTray, 0, 1u, IID_PPV_ARG(IOleCommandTarget, &pctNetShell));
28     if (FAILED(hr))
29         return hr;
30 
31     return pctNetShell->Exec(&CGID_ShellServiceObject,
32                              OLECMDID_NEW,
33                              OLECMDEXECOPT_DODEFAULT, NULL, NULL);
34 }
35 
36 HRESULT CSysTray::ShutdownNetShell()
37 {
38     if (!pctNetShell)
39         return S_FALSE;
40     HRESULT hr = pctNetShell->Exec(&CGID_ShellServiceObject,
41                                    OLECMDID_SAVE,
42                                    OLECMDEXECOPT_DODEFAULT, NULL, NULL);
43     pctNetShell.Release();
44     return hr;
45 }
46 
47 HRESULT CSysTray::InitIcons()
48 {
49     TRACE("Initializing Notification icons...\n");
50     for (int i = 0; i < g_NumIcons; i++)
51     {
52         HRESULT hr = g_IconHandlers[i].pfnInit(this);
53         if (FAILED(hr))
54             return hr;
55     }
56 
57     return InitNetShell();
58 }
59 
60 HRESULT CSysTray::ShutdownIcons()
61 {
62     TRACE("Shutting down Notification icons...\n");
63     for (int i = 0; i < g_NumIcons; i++)
64     {
65         HRESULT hr = g_IconHandlers[i].pfnShutdown(this);
66         if (FAILED(hr))
67             return hr;
68     }
69 
70     return ShutdownNetShell();
71 }
72 
73 HRESULT CSysTray::UpdateIcons()
74 {
75     TRACE("Updating Notification icons...\n");
76     for (int i = 0; i < g_NumIcons; i++)
77     {
78         HRESULT hr = g_IconHandlers[i].pfnUpdate(this);
79         if (FAILED(hr))
80             return hr;
81     }
82 
83     return S_OK;
84 }
85 
86 HRESULT CSysTray::ProcessIconMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT &lResult)
87 {
88     for (int i = 0; i < g_NumIcons; i++)
89     {
90         HRESULT hr = g_IconHandlers[i].pfnMessage(this, uMsg, wParam, lParam, lResult);
91         if (FAILED(hr))
92             return hr;
93 
94         if (hr == S_OK)
95             return hr;
96     }
97 
98     // Not handled by anyone, so return accordingly.
99     return S_FALSE;
100 }
101 
102 /*++
103 * @name NotifyIcon
104 *
105 * Basically a Shell_NotifyIcon wrapper.
106 * Based on the parameters provided, it changes the current state of the notification icon.
107 *
108 * @param code
109 *        Determines whether to add, delete or modify the notification icon (represented by uId).
110 * @param uId
111 *        Represents the particular notification icon.
112 * @param hIcon
113 *        A handle to an icon for the notification object.
114 * @param szTip
115 *        A string for the tooltip of the notification.
116 * @param dwstate
117 *        Determines whether to show or hide the notification icon.
118 *
119 * @return The error code.
120 *
121 *--*/
122 HRESULT CSysTray::NotifyIcon(INT code, UINT uId, HICON hIcon, LPCWSTR szTip, DWORD dwstate)
123 {
124     NOTIFYICONDATA nim = { 0 };
125 
126     TRACE("NotifyIcon code=%d, uId=%d, hIcon=%p, szTip=%S\n", code, uId, hIcon, szTip);
127 
128     nim.cbSize = sizeof(nim);
129     nim.uFlags = NIF_MESSAGE | NIF_ICON | NIF_STATE | NIF_TIP;
130     nim.hIcon = hIcon;
131     nim.uID = uId;
132     nim.uCallbackMessage = uId;
133     nim.dwState = dwstate;
134     nim.dwStateMask = NIS_HIDDEN;
135     nim.hWnd = m_hWnd;
136     nim.uVersion = NOTIFYICON_VERSION;
137     if (szTip)
138         StringCchCopy(nim.szTip, _countof(nim.szTip), szTip);
139     else
140         nim.szTip[0] = 0;
141     BOOL ret = Shell_NotifyIcon(code, &nim);
142     return ret ? S_OK : E_FAIL;
143 }
144 
145 DWORD WINAPI CSysTray::s_SysTrayThreadProc(PVOID param)
146 {
147     CSysTray * st = (CSysTray*) param;
148     return st->SysTrayThreadProc();
149 }
150 
151 HRESULT CSysTray::SysTrayMessageLoop()
152 {
153     BOOL ret;
154     MSG msg;
155 
156     while ((ret = GetMessage(&msg, NULL, 0, 0)) != 0)
157     {
158         if (ret < 0)
159             break;
160 
161         TranslateMessage(&msg);
162         DispatchMessage(&msg);
163     }
164 
165     return S_OK;
166 }
167 
168 HRESULT CSysTray::SysTrayThreadProc()
169 {
170     WCHAR strFileName[MAX_PATH];
171     GetModuleFileNameW(g_hInstance, strFileName, MAX_PATH);
172     HMODULE hLib = LoadLibraryW(strFileName);
173 
174     CoInitializeEx(NULL, COINIT_DISABLE_OLE1DDE | COINIT_APARTMENTTHREADED);
175 
176     Create(NULL);
177 
178     HRESULT ret = SysTrayMessageLoop();
179 
180     CoUninitialize();
181 
182     FreeLibraryAndExitThread(hLib, ret);
183 }
184 
185 HRESULT CSysTray::CreateSysTrayThread()
186 {
187     TRACE("CSysTray Init TODO: Initialize tray icon handlers.\n");
188 
189     HANDLE hThread = CreateThread(NULL, 0, s_SysTrayThreadProc, this, 0, NULL);
190 
191     CloseHandle(hThread);
192 
193     return S_OK;
194 }
195 
196 HRESULT CSysTray::DestroySysTrayWindow()
197 {
198     DestroyWindow();
199     hwndSysTray = NULL;
200     return S_OK;
201 }
202 
203 // *** IOleCommandTarget methods ***
204 HRESULT STDMETHODCALLTYPE CSysTray::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds [], OLECMDTEXT *pCmdText)
205 {
206     UNIMPLEMENTED;
207     return S_OK;
208 }
209 
210 HRESULT STDMETHODCALLTYPE CSysTray::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
211 {
212     if (!IsEqualGUID(*pguidCmdGroup, CGID_ShellServiceObject))
213         return E_FAIL;
214 
215     switch (nCmdID)
216     {
217     case OLECMDID_NEW: // init
218         return CreateSysTrayThread();
219     case OLECMDID_SAVE: // shutdown
220         return DestroySysTrayWindow();
221     }
222     return S_OK;
223 }
224 
225 BOOL CSysTray::ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT &lResult, DWORD dwMsgMapID)
226 {
227     HRESULT hr;
228 
229     if (hWnd != m_hWnd)
230         return FALSE;
231 
232     switch (uMsg)
233     {
234     case WM_NCCREATE:
235     case WM_NCDESTROY:
236         return FALSE;
237 
238     case WM_CREATE:
239         InitIcons();
240         SetTimer(1, 2000, NULL);
241         return TRUE;
242 
243     case WM_TIMER:
244         if (wParam == 1)
245             UpdateIcons();
246         else
247             ProcessIconMessage(uMsg, wParam, lParam, lResult);
248         return TRUE;
249 
250     case WM_DESTROY:
251         KillTimer(1);
252         ShutdownIcons();
253         return TRUE;
254     }
255 
256     TRACE("SysTray message received %u (%08p %08p)\n", uMsg, wParam, lParam);
257 
258     hr = ProcessIconMessage(uMsg, wParam, lParam, lResult);
259     if (FAILED(hr))
260         return FALSE;
261 
262     return (hr == S_OK);
263 }
264