[UMPNPMGR][USETUP] Use PlugPlayControlStartDevice in usetup and umpnpmgr
[reactos.git] / base / services / umpnpmgr / umpnpmgr.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 2005 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19 /*
20 * COPYRIGHT: See COPYING in the top level directory
21 * PROJECT: ReactOS kernel
22 * FILE: base/services/umpnpmgr/umpnpmgr.c
23 * PURPOSE: User-mode Plug and Play manager
24 * PROGRAMMER: Eric Kohl (eric.kohl@reactos.org)
25 * Hervé Poussineau (hpoussin@reactos.org)
26 * Colin Finck (colin@reactos.org)
27 */
28
29 /* INCLUDES *****************************************************************/
30
31 #include "precomp.h"
32
33 #define NDEBUG
34 #include <debug.h>
35
36
37 /* GLOBALS ******************************************************************/
38
39 static WCHAR ServiceName[] = L"PlugPlay";
40
41 static SERVICE_STATUS_HANDLE ServiceStatusHandle;
42 static SERVICE_STATUS ServiceStatus;
43
44 HKEY hEnumKey = NULL;
45 HKEY hClassKey = NULL;
46 BOOL g_IsUISuppressed = FALSE;
47
48 /* FUNCTIONS *****************************************************************/
49
50 static DWORD WINAPI
51 PnpEventThread(LPVOID lpParameter)
52 {
53 PLUGPLAY_CONTROL_USER_RESPONSE_DATA ResponseData = {0, 0, 0, 0};
54 DWORD dwRet = ERROR_SUCCESS;
55 NTSTATUS Status;
56 RPC_STATUS RpcStatus;
57 PPLUGPLAY_EVENT_BLOCK PnpEvent, NewPnpEvent;
58 ULONG PnpEventSize;
59
60 UNREFERENCED_PARAMETER(lpParameter);
61
62 PnpEventSize = 0x1000;
63 PnpEvent = HeapAlloc(GetProcessHeap(), 0, PnpEventSize);
64 if (PnpEvent == NULL)
65 return ERROR_OUTOFMEMORY;
66
67 for (;;)
68 {
69 DPRINT("Calling NtGetPlugPlayEvent()\n");
70
71 /* Wait for the next PnP event */
72 Status = NtGetPlugPlayEvent(0, 0, PnpEvent, PnpEventSize);
73
74 /* Resize the buffer for the PnP event if it's too small */
75 if (Status == STATUS_BUFFER_TOO_SMALL)
76 {
77 PnpEventSize += 0x400;
78 NewPnpEvent = HeapReAlloc(GetProcessHeap(), 0, PnpEvent, PnpEventSize);
79 if (NewPnpEvent == NULL)
80 {
81 dwRet = ERROR_OUTOFMEMORY;
82 break;
83 }
84 PnpEvent = NewPnpEvent;
85 continue;
86 }
87
88 if (!NT_SUCCESS(Status))
89 {
90 DPRINT1("NtGetPlugPlayEvent() failed (Status 0x%08lx)\n", Status);
91 break;
92 }
93
94 /* Process the PnP event */
95 DPRINT("Received PnP Event\n");
96 if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_ENUMERATED, &RpcStatus))
97 {
98 DeviceInstallParams* Params;
99 DWORD len;
100 DWORD DeviceIdLength;
101
102 DPRINT("Device enumerated: %S\n", PnpEvent->TargetDevice.DeviceIds);
103
104 DeviceIdLength = lstrlenW(PnpEvent->TargetDevice.DeviceIds);
105 if (DeviceIdLength)
106 {
107 /* Allocate a new device-install event */
108 len = FIELD_OFFSET(DeviceInstallParams, DeviceIds) + (DeviceIdLength + 1) * sizeof(WCHAR);
109 Params = HeapAlloc(GetProcessHeap(), 0, len);
110 if (Params)
111 {
112 wcscpy(Params->DeviceIds, PnpEvent->TargetDevice.DeviceIds);
113
114 /* Queue the event (will be dequeued by DeviceInstallThread) */
115 WaitForSingleObject(hDeviceInstallListMutex, INFINITE);
116 InsertTailList(&DeviceInstallListHead, &Params->ListEntry);
117 ReleaseMutex(hDeviceInstallListMutex);
118
119 SetEvent(hDeviceInstallListNotEmpty);
120 }
121 }
122 }
123 else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_ARRIVAL, &RpcStatus))
124 {
125 // DWORD dwRecipient;
126
127 DPRINT("Device arrival: %S\n", PnpEvent->TargetDevice.DeviceIds);
128
129 // dwRecipient = BSM_ALLDESKTOPS | BSM_APPLICATIONS;
130 // BroadcastSystemMessageW(BSF_POSTMESSAGE,
131 // &dwRecipient,
132 // WM_DEVICECHANGE,
133 // DBT_DEVNODES_CHANGED,
134 // 0);
135 SendMessageW(HWND_BROADCAST, WM_DEVICECHANGE, DBT_DEVNODES_CHANGED, 0);
136 }
137 else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_EJECT_VETOED, &RpcStatus))
138 {
139 DPRINT1("Eject vetoed: %S\n", PnpEvent->TargetDevice.DeviceIds);
140 }
141 else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_KERNEL_INITIATED_EJECT, &RpcStatus))
142 {
143 DPRINT1("Kernel initiated eject: %S\n", PnpEvent->TargetDevice.DeviceIds);
144 }
145 else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_SAFE_REMOVAL, &RpcStatus))
146 {
147 // DWORD dwRecipient;
148
149 DPRINT1("Safe removal: %S\n", PnpEvent->TargetDevice.DeviceIds);
150
151 // dwRecipient = BSM_ALLDESKTOPS | BSM_APPLICATIONS;
152 // BroadcastSystemMessageW(BSF_POSTMESSAGE,
153 // &dwRecipient,
154 // WM_DEVICECHANGE,
155 // DBT_DEVNODES_CHANGED,
156 // 0);
157 SendMessageW(HWND_BROADCAST, WM_DEVICECHANGE, DBT_DEVNODES_CHANGED, 0);
158 }
159 else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_SURPRISE_REMOVAL, &RpcStatus))
160 {
161 // DWORD dwRecipient;
162
163 DPRINT1("Surprise removal: %S\n", PnpEvent->TargetDevice.DeviceIds);
164
165 // dwRecipient = BSM_ALLDESKTOPS | BSM_APPLICATIONS;
166 // BroadcastSystemMessageW(BSF_POSTMESSAGE,
167 // &dwRecipient,
168 // WM_DEVICECHANGE,
169 // DBT_DEVNODES_CHANGED,
170 // 0);
171 SendMessageW(HWND_BROADCAST, WM_DEVICECHANGE, DBT_DEVNODES_CHANGED, 0);
172 }
173 else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_REMOVAL_VETOED, &RpcStatus))
174 {
175 DPRINT1("Removal vetoed: %S\n", PnpEvent->TargetDevice.DeviceIds);
176 }
177 else if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_REMOVE_PENDING, &RpcStatus))
178 {
179 DPRINT1("Removal pending: %S\n", PnpEvent->TargetDevice.DeviceIds);
180 }
181 else
182 {
183 DPRINT1("Unknown event, GUID {%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}\n",
184 PnpEvent->EventGuid.Data1, PnpEvent->EventGuid.Data2, PnpEvent->EventGuid.Data3,
185 PnpEvent->EventGuid.Data4[0], PnpEvent->EventGuid.Data4[1], PnpEvent->EventGuid.Data4[2],
186 PnpEvent->EventGuid.Data4[3], PnpEvent->EventGuid.Data4[4], PnpEvent->EventGuid.Data4[5],
187 PnpEvent->EventGuid.Data4[6], PnpEvent->EventGuid.Data4[7]);
188 }
189
190 /* Dequeue the current PnP event and signal the next one */
191 Status = NtPlugPlayControl(PlugPlayControlUserResponse,
192 &ResponseData,
193 sizeof(ResponseData));
194 if (!NT_SUCCESS(Status))
195 {
196 DPRINT1("NtPlugPlayControl(PlugPlayControlUserResponse) failed (Status 0x%08lx)\n", Status);
197 break;
198 }
199 }
200
201 HeapFree(GetProcessHeap(), 0, PnpEvent);
202
203 return dwRet;
204 }
205
206
207 static VOID
208 UpdateServiceStatus(DWORD dwState)
209 {
210 ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
211 ServiceStatus.dwCurrentState = dwState;
212 ServiceStatus.dwControlsAccepted = 0;
213 ServiceStatus.dwWin32ExitCode = 0;
214 ServiceStatus.dwServiceSpecificExitCode = 0;
215 ServiceStatus.dwCheckPoint = 0;
216
217 if (dwState == SERVICE_START_PENDING ||
218 dwState == SERVICE_STOP_PENDING ||
219 dwState == SERVICE_PAUSE_PENDING ||
220 dwState == SERVICE_CONTINUE_PENDING)
221 ServiceStatus.dwWaitHint = 10000;
222 else
223 ServiceStatus.dwWaitHint = 0;
224
225 SetServiceStatus(ServiceStatusHandle,
226 &ServiceStatus);
227 }
228
229
230 static DWORD WINAPI
231 ServiceControlHandler(DWORD dwControl,
232 DWORD dwEventType,
233 LPVOID lpEventData,
234 LPVOID lpContext)
235 {
236 DPRINT1("ServiceControlHandler() called\n");
237
238 switch (dwControl)
239 {
240 case SERVICE_CONTROL_STOP:
241 DPRINT1(" SERVICE_CONTROL_STOP received\n");
242 /* Stop listening to RPC Messages */
243 RpcMgmtStopServerListening(NULL);
244 UpdateServiceStatus(SERVICE_STOPPED);
245 return ERROR_SUCCESS;
246
247 case SERVICE_CONTROL_PAUSE:
248 DPRINT1(" SERVICE_CONTROL_PAUSE received\n");
249 UpdateServiceStatus(SERVICE_PAUSED);
250 return ERROR_SUCCESS;
251
252 case SERVICE_CONTROL_CONTINUE:
253 DPRINT1(" SERVICE_CONTROL_CONTINUE received\n");
254 UpdateServiceStatus(SERVICE_RUNNING);
255 return ERROR_SUCCESS;
256
257 case SERVICE_CONTROL_INTERROGATE:
258 DPRINT1(" SERVICE_CONTROL_INTERROGATE received\n");
259 SetServiceStatus(ServiceStatusHandle,
260 &ServiceStatus);
261 return ERROR_SUCCESS;
262
263 case SERVICE_CONTROL_SHUTDOWN:
264 DPRINT1(" SERVICE_CONTROL_SHUTDOWN received\n");
265 /* Stop listening to RPC Messages */
266 RpcMgmtStopServerListening(NULL);
267 UpdateServiceStatus(SERVICE_STOPPED);
268 return ERROR_SUCCESS;
269
270 default :
271 DPRINT1(" Control %lu received\n", dwControl);
272 return ERROR_CALL_NOT_IMPLEMENTED;
273 }
274 }
275
276 static DWORD
277 GetBooleanRegValue(
278 IN HKEY hKey,
279 IN PCWSTR lpSubKey,
280 IN PCWSTR lpValue,
281 OUT PBOOL pValue)
282 {
283 DWORD dwError, dwType, dwData;
284 DWORD cbData = sizeof(dwData);
285 HKEY hSubKey = NULL;
286
287 /* Default value */
288 *pValue = FALSE;
289
290 dwError = RegOpenKeyExW(hKey,
291 lpSubKey,
292 0,
293 KEY_READ,
294 &hSubKey);
295 if (dwError != ERROR_SUCCESS)
296 {
297 DPRINT("GetBooleanRegValue(): RegOpenKeyExW() has failed to open '%S' key! (Error: %lu)\n",
298 lpSubKey, dwError);
299 return dwError;
300 }
301
302 dwError = RegQueryValueExW(hSubKey,
303 lpValue,
304 0,
305 &dwType,
306 (PBYTE)&dwData,
307 &cbData);
308 if (dwError != ERROR_SUCCESS)
309 {
310 DPRINT("GetBooleanRegValue(): RegQueryValueExW() has failed to query '%S' value! (Error: %lu)\n",
311 lpValue, dwError);
312 goto Cleanup;
313 }
314 if (dwType != REG_DWORD)
315 {
316 DPRINT("GetBooleanRegValue(): The value is not of REG_DWORD type!\n");
317 goto Cleanup;
318 }
319
320 /* Return the value */
321 *pValue = (dwData == 1);
322
323 Cleanup:
324 RegCloseKey(hSubKey);
325
326 return dwError;
327 }
328
329 BOOL
330 GetSuppressNewUIValue(VOID)
331 {
332 BOOL bSuppressNewHWUI = FALSE;
333
334 /*
335 * Query the SuppressNewHWUI policy registry value. Don't cache it
336 * as we want to update our behaviour in consequence.
337 */
338 GetBooleanRegValue(HKEY_LOCAL_MACHINE,
339 L"Software\\Policies\\Microsoft\\Windows\\DeviceInstall\\Settings",
340 L"SuppressNewHWUI",
341 &bSuppressNewHWUI);
342 if (bSuppressNewHWUI)
343 DPRINT("GetSuppressNewUIValue(): newdev.dll's wizard UI won't be shown!\n");
344
345 return bSuppressNewHWUI;
346 }
347
348 VOID WINAPI
349 ServiceMain(DWORD argc, LPTSTR *argv)
350 {
351 HANDLE hThread;
352 DWORD dwThreadId;
353
354 UNREFERENCED_PARAMETER(argc);
355 UNREFERENCED_PARAMETER(argv);
356
357 DPRINT("ServiceMain() called\n");
358
359 ServiceStatusHandle = RegisterServiceCtrlHandlerExW(ServiceName,
360 ServiceControlHandler,
361 NULL);
362 if (!ServiceStatusHandle)
363 {
364 DPRINT1("RegisterServiceCtrlHandlerExW() failed! (Error %lu)\n", GetLastError());
365 return;
366 }
367
368 UpdateServiceStatus(SERVICE_START_PENDING);
369
370 hThread = CreateThread(NULL,
371 0,
372 PnpEventThread,
373 NULL,
374 0,
375 &dwThreadId);
376 if (hThread != NULL)
377 CloseHandle(hThread);
378
379 hThread = CreateThread(NULL,
380 0,
381 RpcServerThread,
382 NULL,
383 0,
384 &dwThreadId);
385 if (hThread != NULL)
386 CloseHandle(hThread);
387
388 hThread = CreateThread(NULL,
389 0,
390 DeviceInstallThread,
391 NULL,
392 0,
393 &dwThreadId);
394 if (hThread != NULL)
395 CloseHandle(hThread);
396
397 UpdateServiceStatus(SERVICE_RUNNING);
398
399 DPRINT("ServiceMain() done\n");
400 }
401
402 static DWORD
403 InitializePnPManager(VOID)
404 {
405 BOOLEAN OldValue;
406 DWORD dwError;
407
408 DPRINT("UMPNPMGR: InitializePnPManager() started\n");
409
410 /* We need this privilege for using CreateProcessAsUserW */
411 RtlAdjustPrivilege(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE, TRUE, FALSE, &OldValue);
412
413 hInstallEvent = CreateEventW(NULL, TRUE, SetupIsActive()/*FALSE*/, NULL);
414 if (hInstallEvent == NULL)
415 {
416 dwError = GetLastError();
417 DPRINT1("Could not create the Install Event! (Error %lu)\n", dwError);
418 return dwError;
419 }
420
421 hNoPendingInstalls = CreateEventW(NULL,
422 TRUE,
423 FALSE,
424 L"Global\\PnP_No_Pending_Install_Events");
425 if (hNoPendingInstalls == NULL)
426 {
427 dwError = GetLastError();
428 DPRINT1("Could not create the Pending-Install Event! (Error %lu)\n", dwError);
429 return dwError;
430 }
431
432 /*
433 * Initialize the device-install event list
434 */
435
436 hDeviceInstallListNotEmpty = CreateEventW(NULL, FALSE, FALSE, NULL);
437 if (hDeviceInstallListNotEmpty == NULL)
438 {
439 dwError = GetLastError();
440 DPRINT1("Could not create the List Event! (Error %lu)\n", dwError);
441 return dwError;
442 }
443
444 hDeviceInstallListMutex = CreateMutexW(NULL, FALSE, NULL);
445 if (hDeviceInstallListMutex == NULL)
446 {
447 dwError = GetLastError();
448 DPRINT1("Could not create the List Mutex! (Error %lu)\n", dwError);
449 return dwError;
450 }
451 InitializeListHead(&DeviceInstallListHead);
452
453 /* Query the SuppressUI registry value and cache it for our whole lifetime */
454 GetBooleanRegValue(HKEY_LOCAL_MACHINE,
455 L"System\\CurrentControlSet\\Services\\PlugPlay\\Parameters",
456 L"SuppressUI",
457 &g_IsUISuppressed);
458 if (g_IsUISuppressed)
459 DPRINT("UMPNPMGR: newdev.dll's wizard UI won't be shown!\n");
460
461 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
462 L"System\\CurrentControlSet\\Enum",
463 0,
464 KEY_ALL_ACCESS,
465 &hEnumKey);
466 if (dwError != ERROR_SUCCESS)
467 {
468 DPRINT1("Could not open the Enum Key! (Error %lu)\n", dwError);
469 return dwError;
470 }
471
472 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
473 L"System\\CurrentControlSet\\Control\\Class",
474 0,
475 KEY_ALL_ACCESS,
476 &hClassKey);
477 if (dwError != ERROR_SUCCESS)
478 {
479 DPRINT1("Could not open the Class Key! (Error %lu)\n", dwError);
480 return dwError;
481 }
482
483 DPRINT("UMPNPMGR: InitializePnPManager() done\n");
484
485 return 0;
486 }
487
488 BOOL WINAPI
489 DllMain(HINSTANCE hinstDLL,
490 DWORD fdwReason,
491 LPVOID lpvReserved)
492 {
493 switch (fdwReason)
494 {
495 case DLL_PROCESS_ATTACH:
496 DisableThreadLibraryCalls(hinstDLL);
497 InitializePnPManager();
498 break;
499
500 case DLL_PROCESS_DETACH:
501 break;
502 }
503
504 return TRUE;
505 }
506
507 /* EOF */