ReactOS  0.4.14-dev-583-g2a1ba2c
hotplug.cpp
Go to the documentation of this file.
1 /*
2  * PROJECT: ReactOS system libraries
3  * LICENSE: GPL - See COPYING in the top level directory
4  * FILE: dll/shellext/stobject/hotplug.cpp
5  * PURPOSE: Removable devices notification icon handler
6  * PROGRAMMERS: Shriraj Sawant a.k.a SR13 <sr.official@hotmail.com>
7  */
8 
9 #include "precomp.h"
10 
11 #include <atlsimpcoll.h>
12 #include <dbt.h>
13 #include <cfgmgr32.h>
14 #include <shlwapi.h>
15 
16 #define DISPLAY_NAME_LEN 40
17 
18 //BOOL WINAPI UnregisterDeviceNotification(HDEVNOTIFY Handle);
19 
21 /*static HDEVNOTIFY g_hDevNotify = NULL;*/
23 static LPCWSTR g_strTooltip = L"Safely Remove Hardware and Eject Media";
26 
27 /*++
28 * @name EnumHotpluggedDevices
29 *
30 * Enumerates the connected safely removable devices.
31 *
32 * @param devList
33 * List of device instances, representing the currently attached devices.
34 *
35 * @return The error code.
36 *
37 *--*/
39 {
40  devList.RemoveAll(); // Clear current devList
43  return E_HANDLE;
44  SP_DEVINFO_DATA did = { 0 };
45  did.cbSize = sizeof(did);
46 
47  // Enumerate all the attached devices.
48  for (int idev = 0; SetupDiEnumDeviceInfo(hdev, idev, &did); idev++)
49  {
50  DWORD dwCapabilities = 0, dwSize = sizeof(dwCapabilities);
51  WCHAR dispName[DISPLAY_NAME_LEN];
52  ULONG ulStatus = 0, ulPnum = 0, ulLength = DISPLAY_NAME_LEN * sizeof(WCHAR);
53  CONFIGRET cr = CM_Get_DevNode_Status(&ulStatus, &ulPnum, did.DevInst, 0);
54  if (cr != CR_SUCCESS)
55  continue;
56  cr = CM_Get_DevNode_Registry_Property(did.DevInst, CM_DRP_DEVICEDESC, NULL, dispName, &ulLength, 0);
57  if (cr != CR_SUCCESS)
58  continue;
60  if (cr != CR_SUCCESS)
61  continue;
62 
63  // Filter and make list of only the appropriate safely removable devices.
64  if ( (dwCapabilities & CM_DEVCAP_REMOVABLE) &&
65  !(dwCapabilities & CM_DEVCAP_DOCKDEVICE) &&
66  !(dwCapabilities & CM_DEVCAP_SURPRISEREMOVALOK) &&
67  ((dwCapabilities & CM_DEVCAP_EJECTSUPPORTED) || (ulStatus & DN_DISABLEABLE)) &&
68  !ulPnum)
69  {
70  devList.Add(did.DevInst);
71  }
72  }
74 
76  {
77  return E_UNEXPECTED;
78  }
79 
80  return S_OK;
81 }
82 
83 /*++
84 * @name NotifyBalloon
85 *
86 * Pops the balloon notification of the given notification icon.
87 *
88 * @param pSysTray
89 * Provides interface for acquiring CSysTray information as required.
90 * @param szTitle
91 * Title for the balloon notification.
92 * @param szInfo
93 * Main content for the balloon notification.
94 * @param uId
95 * Represents the particular notification icon.
96 *
97 * @return The error code.
98 *
99 *--*/
101 {
102  NOTIFYICONDATA nim = { 0 };
103 
104  nim.cbSize = sizeof(nim);
105  nim.uID = uId;
106  nim.hWnd = pSysTray->GetHWnd();
107 
108  nim.uFlags = NIF_INFO;
109  nim.uTimeout = 10;
110  nim.dwInfoFlags = NIIF_INFO;
111 
113  StringCchCopy(nim.szInfo, _countof(nim.szInfo), szInfo);
115 
116  Sleep(10000); /* As per windows, the balloon notification remains visible for atleast 10 sec.
117  This timer maintains the same condition.
118  Also it is required so that the icon doesn't hide instantly after last device is removed,
119  as that will prevent popping of notification.
120  */
122  StringCchCopy(nim.szInfo, _countof(nim.szInfo), L"");
124  g_IsRemoving = FALSE; /* This flag is used to prevent instant icon hiding after last device is removed.
125  The above timer maintains the required state for the same.
126  */
127  return ret ? S_OK : E_FAIL;
128 }
129 
131 {
132  TRACE("Hotplug_Init\n");
135 
136  return pSysTray->NotifyIcon(NIM_ADD, ID_ICON_HOTPLUG, g_hIconHotplug, g_strTooltip, NIS_HIDDEN);
137 }
138 
140 {
141  TRACE("Hotplug_Update\n");
142 
143  if(g_devList.GetSize() || g_IsRemoving)
144  return pSysTray->NotifyIcon(NIM_MODIFY, ID_ICON_HOTPLUG, g_hIconHotplug, g_strTooltip);
145  else
146  return pSysTray->NotifyIcon(NIM_MODIFY, ID_ICON_HOTPLUG, g_hIconHotplug, g_strTooltip, NIS_HIDDEN);
147 }
148 
150 {
151  TRACE("Hotplug_Shutdown\n");
152 
153  return pSysTray->NotifyIcon(NIM_DELETE, ID_ICON_HOTPLUG, NULL, NULL);
154 }
155 
156 static void _RunHotplug(CSysTray * pSysTray)
157 {
158  ShellExecuteW(pSysTray->GetHWnd(),
159  L"open",
160  L"rundll32.exe",
161  L"shell32.dll,Control_RunDLL hotplug.dll",
162  NULL,
163  SW_SHOWNORMAL);
164 }
165 
166 static void _ShowContextMenu(CSysTray * pSysTray)
167 {
168  HMENU hPopup = CreatePopupMenu();
169  ULONG ulLength = DISPLAY_NAME_LEN * sizeof(WCHAR);
170 
171  for (INT index = 0; index < g_devList.GetSize(); index++)
172  {
173  WCHAR dispName[DISPLAY_NAME_LEN], menuName[DISPLAY_NAME_LEN + 10];
175  if (cr != CR_SUCCESS)
176  StrCpyW(dispName, L"Unknown Device");
177 
178  swprintf(menuName, L"Eject %wS", dispName);
179  AppendMenuW(hPopup, MF_STRING, index+1, menuName);
180  }
181 
182  SetForegroundWindow(pSysTray->GetHWnd());
184  POINT pt;
185  GetCursorPos(&pt);
186 
187  DWORD id = TrackPopupMenuEx(hPopup, flags,
188  pt.x, pt.y,
189  pSysTray->GetHWnd(), NULL);
190 
191  if (id > 0)
192  {
193  id--; // since array indices starts from zero.
195  if (cr != CR_SUCCESS)
196  StrCpyW(g_strMenuSel, L"Unknown Device");
197 
198  cr = CM_Request_Device_Eject_Ex(g_devList[id], 0, 0, 0, 0, 0);
199  if (cr != CR_SUCCESS)
200  {
201  WCHAR strInfo[128];
202  swprintf(strInfo, L"Problem Ejecting %wS", g_strMenuSel);
203  MessageBox(0, L"The device cannot be stopped right now! Try stopping it again later!", strInfo, MB_OKCANCEL | MB_ICONEXCLAMATION);
204  }
205  else
206  {
207  //MessageBox(0, L"Device ejected successfully!! You can safely remove the device now!", L"Safely Remove Hardware", MB_OKCANCEL | MB_ICONINFORMATION);
208  g_IsRemoving = TRUE;
209  g_devList.RemoveAt(id); /* thing is.. even after removing id at this point, the devnode_change occurs after some seconds of sucessful removal
210  and since pendrive is still plugged in it gets enumerated, if problem number is not filtered.
211  */
212  }
213  }
214 
215  DestroyMenu(hPopup);
216 }
217 
218 static void _ShowContextMenuR(CSysTray * pSysTray)
219 {
221  HMENU hPopup = CreatePopupMenu();
222  AppendMenuW(hPopup, MF_STRING, IDS_HOTPLUG_REMOVE_2, strMenu);
224 
225  SetForegroundWindow(pSysTray->GetHWnd());
227  POINT pt;
228  GetCursorPos(&pt);
229 
230  DWORD id = TrackPopupMenuEx(hPopup, flags,
231  pt.x, pt.y,
232  pSysTray->GetHWnd(), NULL);
233 
234  if (id == IDS_HOTPLUG_REMOVE_2)
235  {
236  _RunHotplug(pSysTray);
237  }
238 
239  DestroyMenu(hPopup);
240 }
241 
243 {
244  HRESULT hr = E_FAIL;
245  TRACE("Hotplug_Message uMsg=%d, wParam=%x, lParam=%x\n", uMsg, wParam, lParam);
246 
247  switch (uMsg)
248  {
249  /*case WM_CREATE:
250  TRACE("Hotplug_Message: WM_CREATE\n");
251  DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;
252 
253  ZeroMemory(&NotificationFilter, sizeof(NotificationFilter));
254  NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
255  NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
256 
257  g_hDevNotify = RegisterDeviceNotification(pSysTray->GetHWnd(), &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE | DEVICE_NOTIFY_ALL_INTERFACE_CLASSES);
258  if (g_hDevNotify != NULL)
259  {
260  lResult = true;
261  return S_OK;
262  }
263  return S_FALSE;*/
264 
265  case WM_USER + 220:
266  TRACE("Hotplug_Message: WM_USER+220\n");
268  {
269  if (lParam)
270  {
271  pSysTray->EnableService(HOTPLUG_SERVICE_FLAG, TRUE);
272  return Hotplug_Init(pSysTray);
273  }
274  else
275  {
276  pSysTray->EnableService(HOTPLUG_SERVICE_FLAG, FALSE);
277  return Hotplug_Shutdown(pSysTray);
278  }
279  }
280  return S_FALSE;
281 
282  case WM_USER + 221:
283  TRACE("Hotplug_Message: WM_USER+221\n");
285  {
286  lResult = (LRESULT)pSysTray->IsServiceEnabled(HOTPLUG_SERVICE_FLAG);
287  return S_OK;
288  }
289  return S_FALSE;
290 
291  case WM_TIMER:
292  if (wParam == HOTPLUG_TIMER_ID)
293  {
294  KillTimer(pSysTray->GetHWnd(), HOTPLUG_TIMER_ID);
295  _ShowContextMenu(pSysTray);
296  }
297  break;
298 
299  case ID_ICON_HOTPLUG:
300  Hotplug_Update(pSysTray);
301 
302  switch (lParam)
303  {
304  case WM_LBUTTONDOWN:
305  SetTimer(pSysTray->GetHWnd(), HOTPLUG_TIMER_ID, GetDoubleClickTime(), NULL);
306  break;
307 
308  case WM_LBUTTONUP:
309  break;
310 
311  case WM_LBUTTONDBLCLK:
312  KillTimer(pSysTray->GetHWnd(), HOTPLUG_TIMER_ID);
313  _RunHotplug(pSysTray);
314  break;
315 
316  case WM_RBUTTONDOWN:
317  break;
318 
319  case WM_RBUTTONUP:
320  _ShowContextMenuR(pSysTray);
321  break;
322 
323  case WM_RBUTTONDBLCLK:
324  break;
325 
326  case WM_MOUSEMOVE:
327  break;
328  }
329  return S_OK;
330 
331  case WM_DEVICECHANGE:
332  switch (wParam)
333  {
336  if (FAILED(hr))
337  return hr;
338 
339  lResult = true;
340  break;
341  case DBT_DEVICEARRIVAL:
342  break;
344  break;
346  break;
348  WCHAR strInfo[128];
349  swprintf(strInfo, L"The %wS can now be safely removed from the system.", g_strMenuSel);
350  NotifyBalloon(pSysTray, L"Safe to Remove Hardware", strInfo);
351 
352  lResult = true;
353  break;
355  break;
356  }
357  return S_OK;
358 
359  /*case WM_CLOSE:
360  if (!UnregisterDeviceNotification(hDeviceNotify))
361  {
362  return S_FALSE;
363  }
364  return S_OK;*/
365 
366  default:
367  TRACE("Hotplug_Message received for unknown ID %d, ignoring.\n");
368  return S_FALSE;
369  }
370 
371  return S_FALSE;
372 }
BOOL WINAPI SetupDiEnumDeviceInfo(HDEVINFO devinfo, DWORD index, PSP_DEVINFO_DATA info)
Definition: devinst.c:1792
#define HOTPLUG_SERVICE_FLAG
Definition: precomp.h:38
static void _RunHotplug(CSysTray *pSysTray)
Definition: hotplug.cpp:156
static void _ShowContextMenu(CSysTray *pSysTray)
Definition: hotplug.cpp:166
#define MAKEINTRESOURCE
Definition: winuser.h:591
static HICON
Definition: imagelist.c:84
#define TRUE
Definition: types.h:120
#define CM_Get_DevNode_Registry_Property
Definition: cfgmgr32.h:1733
VOID WINAPI DECLSPEC_HOTPATCH Sleep(IN DWORD dwMilliseconds)
Definition: synch.c:790
#define CM_DEVCAP_REMOVABLE
Definition: cfgmgr32.h:737
#define E_HANDLE
Definition: winerror.h:2850
BOOL Add(const T &t)
Definition: atlsimpcoll.h:58
#define WM_LBUTTONDOWN
Definition: winuser.h:1758
#define DBT_DEVNODES_CHANGED
Definition: dbt.h:28
HRESULT hr
Definition: shlfolder.c:183
const WCHAR * LPCWSTR
Definition: xmlstorage.h:185
#define CM_DEVCAP_EJECTSUPPORTED
Definition: cfgmgr32.h:736
#define ERROR_NO_MORE_ITEMS
Definition: compat.h:95
#define pt(x, y)
Definition: drawing.c:79
#define IDS_HOTPLUG_REMOVE_2
Definition: resource.h:19
#define TPM_RETURNCMD
Definition: winuser.h:2362
#define _countof(array)
Definition: fontsub.cpp:30
static HICON g_hIconHotplug
Definition: hotplug.cpp:22
#define ID_ICON_HOTPLUG
Definition: precomp.h:34
BOOL WINAPI TrackPopupMenuEx(_In_ HMENU, _In_ UINT, _In_ int, _In_ int, _In_ HWND, _In_opt_ LPTPMPARAMS)
#define MF_STRING
Definition: winuser.h:138
#define DN_DISABLEABLE
Definition: cfg.h:131
DWORD dwInfoFlags
Definition: shellapi.h:244
#define INVALID_HANDLE_VALUE
Definition: compat.h:399
DWORD WINAPI GetLastError(VOID)
Definition: except.c:1059
UINT_PTR WPARAM
Definition: windef.h:207
BOOL WINAPI GetCursorPos(_Out_ LPPOINT)
Definition: cursoricon.c:2635
#define NO_ERROR
Definition: dderror.h:5
#define E_FAIL
Definition: ddrawi.h:102
static LPCWSTR g_strTooltip
Definition: hotplug.cpp:23
int32_t INT
Definition: typedefs.h:56
WPARAM wParam
Definition: combotst.c:138
BOOL WINAPI AppendMenuW(_In_ HMENU, _In_ UINT, _In_ UINT_PTR, _In_opt_ LPCWSTR)
#define MB_ICONEXCLAMATION
Definition: winuser.h:779
unsigned int BOOL
Definition: ntddk_ex.h:94
#define DBT_DEVICEREMOVECOMPLETE
Definition: dbt.h:16
HRESULT STDMETHODCALLTYPE Hotplug_Init(_In_ CSysTray *pSysTray)
Definition: hotplug.cpp:130
#define CR_SUCCESS
Definition: cfgmgr32.h:842
#define S_FALSE
Definition: winerror.h:2357
#define TPM_BOTTOMALIGN
Definition: winuser.h:2360
smooth NULL
Definition: ftsmooth.c:416
LONG_PTR LPARAM
Definition: windef.h:208
#define MB_OKCANCEL
Definition: winuser.h:798
GLuint index
Definition: glext.h:6031
UINT_PTR WINAPI SetTimer(_In_opt_ HWND, _In_ UINT_PTR, _In_ UINT, _In_opt_ TIMERPROC)
#define WM_RBUTTONDOWN
Definition: winuser.h:1761
HRESULT STDMETHODCALLTYPE Hotplug_Message(_In_ CSysTray *pSysTray, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT &lResult)
Definition: hotplug.cpp:242
#define TRACE(s)
Definition: solgame.cpp:4
HRESULT STDMETHODCALLTYPE Hotplug_Shutdown(_In_ CSysTray *pSysTray)
Definition: hotplug.cpp:149
__wchar_t WCHAR
Definition: xmlstorage.h:180
LONG HRESULT
Definition: typedefs.h:77
#define WM_TIMER
Definition: winuser.h:1724
#define DBT_DEVICEQUERYREMOVE
Definition: dbt.h:13
BOOL WINAPI SetupDiDestroyDeviceInfoList(HDEVINFO devinfo)
Definition: devinst.c:2898
HRESULT EnumHotpluggedDevices(CSimpleArray< DEVINST > &devList)
Definition: hotplug.cpp:38
#define swprintf(buf, format,...)
Definition: sprintf.c:56
#define STDMETHODCALLTYPE
Definition: bdasup.h:9
unsigned long DWORD
Definition: ntddk_ex.h:95
#define NIM_DELETE
Definition: shellapi.h:93
#define WM_RBUTTONUP
Definition: winuser.h:1762
#define TPM_NONOTIFY
Definition: winuser.h:2361
TCHAR szTitle[MAX_LOADSTRING]
Definition: magnifier.c:35
GLbitfield flags
Definition: glext.h:7161
#define DIGCF_ALLCLASSES
Definition: setupapi.h:172
BOOL WINAPI KillTimer(_In_opt_ HWND, _In_ UINT_PTR)
int ret
#define DBT_DEVICEQUERYREMOVEFAILED
Definition: dbt.h:14
static const WCHAR L[]
Definition: oid.c:1250
#define CM_DRP_DEVICEDESC
Definition: cfgmgr32.h:676
#define StringCchCopy
Definition: strsafe.h:139
#define WM_LBUTTONDBLCLK
Definition: winuser.h:1760
#define LRESULT
Definition: ole.h:14
#define SetupDiGetClassDevs
Definition: setupapi.h:2588
#define MessageBox
Definition: winuser.h:5797
LPWSTR WINAPI StrCpyW(LPWSTR lpszStr, LPCWSTR lpszSrc)
Definition: string.c:514
#define WM_USER
Definition: winuser.h:1877
BOOL WINAPI DestroyMenu(_In_ HMENU)
#define CM_Request_Device_Eject_Ex
Definition: cfgmgr32.h:2878
#define _In_
Definition: no_sal2.h:204
#define S_OK
Definition: intsafe.h:59
#define SW_SHOWNORMAL
Definition: winuser.h:764
static BOOL g_IsRemoving
Definition: hotplug.cpp:25
CSimpleArray< DEVINST > g_devList
Definition: hotplug.cpp:20
static WCHAR g_strMenuSel[DISPLAY_NAME_LEN]
Definition: hotplug.cpp:24
HRESULT STDMETHODCALLTYPE Hotplug_Update(_In_ CSysTray *pSysTray)
Definition: hotplug.cpp:139
#define WM_LBUTTONUP
Definition: winuser.h:1759
#define DBT_DEVICEARRIVAL
Definition: dbt.h:12
HWND GetHWnd()
Definition: csystray.h:53
unsigned int UINT
Definition: ndis.h:50
#define WM_MOUSEMOVE
Definition: winuser.h:1757
RETURN_TYPE CONFIGRET
Definition: cfgmgr32.h:74
BOOL WINAPI SetMenuDefaultItem(_In_ HMENU, _In_ UINT, _In_ UINT)
CHAR szInfo[256]
Definition: shellapi.h:238
#define WM_DEVICECHANGE
Definition: winuser.h:1793
#define E_UNEXPECTED
Definition: winerror.h:2456
#define NIM_ADD
Definition: shellapi.h:91
CONFIGRET WINAPI CM_Get_DevNode_Status(_Out_ PULONG pulStatus, _Out_ PULONG pulProblemNumber, _In_ DEVINST dnDevInst, _In_ ULONG ulFlags)
Definition: cfgmgr.c:3561
HMENU WINAPI CreatePopupMenu(void)
Definition: menu.c:846
UINT WINAPI GetDoubleClickTime(void)
Definition: ntwrapper.h:314
#define DBT_DEVICEREMOVEPENDING
Definition: dbt.h:15
#define Shell_NotifyIcon
Definition: shellapi.h:687
#define WM_RBUTTONDBLCLK
Definition: winuser.h:1763
HINSTANCE WINAPI ShellExecuteW(HWND hwnd, LPCWSTR lpVerb, LPCWSTR lpFile, LPCWSTR lpParameters, LPCWSTR lpDirectory, INT nShowCmd)
Definition: shlexec.cpp:2246
#define HOTPLUG_TIMER_ID
Definition: precomp.h:79
unsigned int ULONG
Definition: retypes.h:1
BOOL WINAPI SetForegroundWindow(_In_ HWND)
HRESULT NotifyBalloon(CSysTray *pSysTray, LPCWSTR szTitle=NULL, LPCWSTR szInfo=NULL, UINT uId=ID_ICON_HOTPLUG)
Definition: hotplug.cpp:100
#define CM_DEVCAP_SURPRISEREMOVALOK
Definition: cfgmgr32.h:742
#define LoadIcon
Definition: winuser.h:5788
HINSTANCE g_hInstance
Definition: MainWindow.cpp:18
#define CM_DEVCAP_DOCKDEVICE
Definition: cfgmgr32.h:738
WCHAR * LPWSTR
Definition: xmlstorage.h:184
LONG_PTR LRESULT
Definition: windef.h:209
static const CLSID *static CLSID *static const GUID VARIANT VARIANT *static IServiceProvider DWORD *static HMENU
Definition: ordinal.c:60
CHAR szInfoTitle[64]
Definition: shellapi.h:243
LPARAM lParam
Definition: combotst.c:139
_In_ HDEV hdev
Definition: winddi.h:3449
static void _ShowContextMenuR(CSysTray *pSysTray)
Definition: hotplug.cpp:218
#define CM_DRP_CAPABILITIES
Definition: cfgmgr32.h:691
PSDBQUERYRESULT_VISTA PVOID DWORD * dwSize
Definition: env.c:54
#define TPM_RIGHTALIGN
Definition: winuser.h:2353
#define NIM_MODIFY
Definition: shellapi.h:92
#define IDI_HOTPLUG_OK
Definition: resource.h:57
#define DIGCF_PRESENT
Definition: setupapi.h:171
#define DISPLAY_NAME_LEN
Definition: hotplug.cpp:16