[STOBJECT] Fully zero-out the full NOTIFYICONDATA structure (it wasn't zeroed out...
[reactos.git] / dll / shellext / stobject / csystray.cpp
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;
125
126 TRACE("NotifyIcon code=%d, uId=%d, hIcon=%p, szTip=%S\n", code, uId, hIcon, szTip);
127
128 ZeroMemory(&nim, sizeof(nim));
129 nim.cbSize = sizeof(nim);
130 nim.uFlags = NIF_MESSAGE | NIF_ICON | NIF_STATE | NIF_TIP;
131 nim.hIcon = hIcon;
132 nim.uID = uId;
133 nim.uCallbackMessage = uId;
134 nim.dwState = dwstate;
135 nim.dwStateMask = NIS_HIDDEN;
136 nim.hWnd = m_hWnd;
137 nim.uVersion = NOTIFYICON_VERSION;
138 if (szTip)
139 StringCchCopy(nim.szTip, _countof(nim.szTip), szTip);
140 else
141 nim.szTip[0] = 0;
142 BOOL ret = Shell_NotifyIcon(code, &nim);
143 return ret ? S_OK : E_FAIL;
144 }
145
146 DWORD WINAPI CSysTray::s_SysTrayThreadProc(PVOID param)
147 {
148 CSysTray * st = (CSysTray*) param;
149 return st->SysTrayThreadProc();
150 }
151
152 HRESULT CSysTray::SysTrayMessageLoop()
153 {
154 BOOL ret;
155 MSG msg;
156
157 while ((ret = GetMessage(&msg, NULL, 0, 0)) != 0)
158 {
159 if (ret < 0)
160 break;
161
162 TranslateMessage(&msg);
163 DispatchMessage(&msg);
164 }
165
166 return S_OK;
167 }
168
169 HRESULT CSysTray::SysTrayThreadProc()
170 {
171 WCHAR strFileName[MAX_PATH];
172 GetModuleFileNameW(g_hInstance, strFileName, MAX_PATH);
173 HMODULE hLib = LoadLibraryW(strFileName);
174
175 CoInitializeEx(NULL, COINIT_DISABLE_OLE1DDE | COINIT_APARTMENTTHREADED);
176
177 Create(NULL);
178
179 HRESULT ret = SysTrayMessageLoop();
180
181 CoUninitialize();
182
183 FreeLibraryAndExitThread(hLib, ret);
184 }
185
186 HRESULT CSysTray::CreateSysTrayThread()
187 {
188 TRACE("CSysTray Init TODO: Initialize tray icon handlers.\n");
189
190 HANDLE hThread = CreateThread(NULL, 0, s_SysTrayThreadProc, this, 0, NULL);
191
192 CloseHandle(hThread);
193
194 return S_OK;
195 }
196
197 HRESULT CSysTray::DestroySysTrayWindow()
198 {
199 DestroyWindow();
200 hwndSysTray = NULL;
201 return S_OK;
202 }
203
204 // *** IOleCommandTarget methods ***
205 HRESULT STDMETHODCALLTYPE CSysTray::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds [], OLECMDTEXT *pCmdText)
206 {
207 UNIMPLEMENTED;
208 return S_OK;
209 }
210
211 HRESULT STDMETHODCALLTYPE CSysTray::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
212 {
213 if (!IsEqualGUID(*pguidCmdGroup, CGID_ShellServiceObject))
214 return E_FAIL;
215
216 switch (nCmdID)
217 {
218 case OLECMDID_NEW: // init
219 return CreateSysTrayThread();
220 case OLECMDID_SAVE: // shutdown
221 return DestroySysTrayWindow();
222 }
223 return S_OK;
224 }
225
226 BOOL CSysTray::ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT &lResult, DWORD dwMsgMapID)
227 {
228 HRESULT hr;
229
230 if (hWnd != m_hWnd)
231 return FALSE;
232
233 switch (uMsg)
234 {
235 case WM_NCCREATE:
236 case WM_NCDESTROY:
237 return FALSE;
238
239 case WM_CREATE:
240 InitIcons();
241 SetTimer(1, 2000, NULL);
242 return TRUE;
243
244 case WM_TIMER:
245 UpdateIcons();
246 return TRUE;
247 case WM_DESTROY:
248 KillTimer(1);
249 ShutdownIcons();
250 return TRUE;
251 }
252
253 TRACE("SysTray message received %u (%08p %08p)\n", uMsg, wParam, lParam);
254
255 hr = ProcessIconMessage(uMsg, wParam, lParam, lResult);
256 if (FAILED(hr))
257 return FALSE;
258
259 return (hr == S_OK);
260 }