xref: /reactos/base/setup/usetup/devinst.c (revision 84344399)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS text-mode setup
4  * PURPOSE:         Device installation
5  * PROGRAMMER:      Herv� Poussineau (hpoussin@reactos.org)
6  *                  Hermes Belusca-Maito
7  */
8 
9 #include <usetup.h>
10 
11 #define NDEBUG
12 #include <debug.h>
13 
14 #define INITGUID
15 #include <guiddef.h>
16 #include <libs/umpnpmgr/sysguid.h>
17 
18 /* LOCALS *******************************************************************/
19 
20 static HANDLE hEnumKey = NULL;
21 static HANDLE hServicesKey = NULL;
22 
23 static HANDLE hNoPendingInstalls = NULL;
24 
25 static HANDLE hPnpThread = NULL;
26 static HANDLE hDeviceInstallThread = NULL;
27 
28 /* Device-install event list */
29 static HANDLE hDeviceInstallListMutex = NULL;
30 static LIST_ENTRY DeviceInstallListHead;
31 static HANDLE hDeviceInstallListNotEmpty = NULL;
32 
33 typedef struct
34 {
35     LIST_ENTRY ListEntry;
36     WCHAR DeviceIds[ANYSIZE_ARRAY];
37 } DeviceInstallParams;
38 
39 /* FUNCTIONS ****************************************************************/
40 
41 static BOOLEAN
42 AreDriversLoaded(
43     IN PCWSTR DeviceId)
44 {
45     PLUGPLAY_CONTROL_STATUS_DATA PlugPlayData;
46     NTSTATUS Status;
47 
48     RtlInitUnicodeString(&PlugPlayData.DeviceInstance, DeviceId);
49     PlugPlayData.Operation = PNP_GET_DEVICE_STATUS;
50 
51     Status = NtPlugPlayControl(PlugPlayControlDeviceStatus, &PlugPlayData, sizeof(PlugPlayData));
52     if (NT_SUCCESS(Status))
53     {
54         return (_Bool)((PlugPlayData.DeviceStatus & DN_DRIVER_LOADED) &&
55                        !(PlugPlayData.DeviceStatus & DN_HAS_PROBLEM));
56     }
57     else
58     {
59         return FALSE;
60     }
61 }
62 
63 static BOOLEAN
64 InstallDriver(
65     IN HINF hInf,
66     IN HANDLE hServices,
67     IN HANDLE hDeviceKey,
68     IN LPCWSTR DeviceId,
69     IN LPCWSTR HardwareId)
70 {
71     UNICODE_STRING ServiceU = RTL_CONSTANT_STRING(L"Service");
72     UNICODE_STRING ErrorControlU = RTL_CONSTANT_STRING(L"ErrorControl");
73     UNICODE_STRING StartU = RTL_CONSTANT_STRING(L"Start");
74     UNICODE_STRING TypeU = RTL_CONSTANT_STRING(L"Type");
75     UNICODE_STRING UpperFiltersU = RTL_CONSTANT_STRING(L"UpperFilters");
76     PWSTR keyboardClass = L"kbdclass\0";
77     PWSTR partMgr = L"partmgr\0";
78 
79     UNICODE_STRING StringU;
80     OBJECT_ATTRIBUTES ObjectAttributes;
81     HANDLE hService;
82     INFCONTEXT Context;
83     PCWSTR Driver, ClassGuid, ImagePath;
84     ULONG dwValue;
85     ULONG Disposition;
86     NTSTATUS Status;
87     BOOLEAN deviceInstalled = FALSE;
88 
89     /* First check if the driver needs any action at all */
90     if (AreDriversLoaded(DeviceId))
91         return TRUE;
92 
93     /* Check if we know the hardware */
94     if (!SpInfFindFirstLine(hInf, L"HardwareIdsDatabase", HardwareId, &Context))
95         return FALSE;
96     if (!INF_GetDataField(&Context, 1, &Driver))
97         return FALSE;
98 
99     /* Get associated class GUID (if any) */
100     if (!INF_GetDataField(&Context, 2, &ClassGuid))
101         ClassGuid = NULL;
102 
103     /* Find associated driver name */
104     /* FIXME: check in other sections too! */
105     if (!SpInfFindFirstLine(hInf, L"BootBusExtenders.Load", Driver, &Context)
106      && !SpInfFindFirstLine(hInf, L"BusExtenders.Load", Driver, &Context)
107      && !SpInfFindFirstLine(hInf, L"SCSI.Load", Driver, &Context)
108      && !SpInfFindFirstLine(hInf, L"InputDevicesSupport.Load", Driver, &Context)
109      && !SpInfFindFirstLine(hInf, L"Keyboard.Load", Driver, &Context))
110     {
111         INF_FreeData(ClassGuid);
112         INF_FreeData(Driver);
113         return FALSE;
114     }
115 
116     if (!INF_GetDataField(&Context, 1, &ImagePath))
117     {
118         INF_FreeData(ClassGuid);
119         INF_FreeData(Driver);
120         return FALSE;
121     }
122 
123     DPRINT1("Using driver '%S' for device '%S'\n", ImagePath, DeviceId);
124 
125     /* Create service key */
126     RtlInitUnicodeString(&StringU, Driver);
127     InitializeObjectAttributes(&ObjectAttributes, &StringU, OBJ_CASE_INSENSITIVE, hServices, NULL);
128     Status = NtCreateKey(&hService, KEY_SET_VALUE, &ObjectAttributes, 0, NULL, REG_OPTION_NON_VOLATILE, &Disposition);
129     if (!NT_SUCCESS(Status))
130     {
131         DPRINT1("NtCreateKey('%wZ') failed with status 0x%08x\n", &StringU, Status);
132         INF_FreeData(ImagePath);
133         INF_FreeData(ClassGuid);
134         INF_FreeData(Driver);
135         return FALSE;
136     }
137 
138     /* Fill service key */
139     if (Disposition == REG_CREATED_NEW_KEY)
140     {
141         dwValue = 0;
142         NtSetValueKey(hService,
143                       &ErrorControlU,
144                       0,
145                       REG_DWORD,
146                       &dwValue,
147                       sizeof(dwValue));
148 
149         dwValue = 0;
150         NtSetValueKey(hService,
151                       &StartU,
152                       0,
153                       REG_DWORD,
154                       &dwValue,
155                       sizeof(dwValue));
156 
157         dwValue = SERVICE_KERNEL_DRIVER;
158         NtSetValueKey(hService,
159                       &TypeU,
160                       0,
161                       REG_DWORD,
162                       &dwValue,
163                       sizeof(dwValue));
164     }
165 
166     INF_FreeData(ImagePath);
167     NtClose(hService);
168 
169     /* Add kbdclass and partmgr upper filters */
170     if (ClassGuid &&_wcsicmp(ClassGuid, L"{4D36E96B-E325-11CE-BFC1-08002BE10318}") == 0)
171     {
172         DPRINT1("Installing keyboard class driver for '%S'\n", DeviceId);
173         NtSetValueKey(hDeviceKey,
174                       &UpperFiltersU,
175                       0,
176                       REG_MULTI_SZ,
177                       keyboardClass,
178                       (wcslen(keyboardClass) + 2) * sizeof(WCHAR));
179     }
180     else if (ClassGuid && _wcsicmp(ClassGuid, L"{4D36E967-E325-11CE-BFC1-08002BE10318}") == 0)
181     {
182         DPRINT1("Installing partition manager driver for '%S'\n", DeviceId);
183         NtSetValueKey(hDeviceKey,
184                       &UpperFiltersU,
185                       0,
186                       REG_MULTI_SZ,
187                       partMgr,
188                       (wcslen(partMgr) + 2) * sizeof(WCHAR));
189     }
190 
191     INF_FreeData(ClassGuid);
192 
193     /* Associate device with the service we just filled */
194     Status = NtSetValueKey(hDeviceKey,
195                            &ServiceU,
196                            0,
197                            REG_SZ,
198                            (PVOID)Driver,
199                            (wcslen(Driver) + 1) * sizeof(WCHAR));
200     if (NT_SUCCESS(Status))
201     {
202         /* We've registered the driver, time to start a device */
203         PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA ControlData;
204         RtlInitUnicodeString(&ControlData.DeviceInstance, DeviceId);
205 
206         Status = NtPlugPlayControl(PlugPlayControlStartDevice, &ControlData, sizeof(ControlData));
207         if (!NT_SUCCESS(Status))
208         {
209             DPRINT1("NtPlugPlayControl() failed with status 0x%08x\n", Status);
210         }
211 
212         deviceInstalled = NT_SUCCESS(Status);
213     }
214 
215     INF_FreeData(Driver);
216 
217     return deviceInstalled;
218 }
219 
220 static VOID
221 InstallDevice(
222     IN HINF hInf,
223     IN HANDLE hEnum,
224     IN HANDLE hServices,
225     IN LPCWSTR DeviceId)
226 {
227     UNICODE_STRING HardwareIDU = RTL_CONSTANT_STRING(L"HardwareID");
228     UNICODE_STRING CompatibleIDsU = RTL_CONSTANT_STRING(L"CompatibleIDs");
229 
230     UNICODE_STRING DeviceIdU;
231     OBJECT_ATTRIBUTES ObjectAttributes;
232     LPCWSTR HardwareID;
233     PKEY_VALUE_PARTIAL_INFORMATION pPartialInformation = NULL;
234     HANDLE hDeviceKey;
235     ULONG ulRequired;
236     BOOLEAN bDriverInstalled = FALSE;
237     NTSTATUS Status;
238 
239     RtlInitUnicodeString(&DeviceIdU, DeviceId);
240     InitializeObjectAttributes(&ObjectAttributes, &DeviceIdU, OBJ_CASE_INSENSITIVE, hEnum, NULL);
241     Status = NtOpenKey(&hDeviceKey, KEY_QUERY_VALUE | KEY_SET_VALUE, &ObjectAttributes);
242     if (!NT_SUCCESS(Status))
243     {
244         DPRINT("Unable to open subkey '%S'\n", DeviceId);
245         return;
246     }
247 
248     Status = NtQueryValueKey(
249         hDeviceKey,
250         &HardwareIDU,
251         KeyValuePartialInformation,
252         NULL,
253         0,
254         &ulRequired);
255     if (Status == STATUS_BUFFER_TOO_SMALL)
256     {
257         pPartialInformation = (PKEY_VALUE_PARTIAL_INFORMATION)RtlAllocateHeap(ProcessHeap, 0, ulRequired);
258         if (!pPartialInformation)
259         {
260             DPRINT1("RtlAllocateHeap() failed\n");
261             NtClose(hDeviceKey);
262             return;
263         }
264         Status = NtQueryValueKey(
265             hDeviceKey,
266             &HardwareIDU,
267             KeyValuePartialInformation,
268             pPartialInformation,
269             ulRequired,
270             &ulRequired);
271     }
272     if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
273     {
274         /* Nothing to do */
275     }
276     else if (!NT_SUCCESS(Status))
277     {
278         DPRINT1("NtQueryValueKey() failed with status 0x%08x\n", Status);
279         if (pPartialInformation)
280             RtlFreeHeap(ProcessHeap, 0, pPartialInformation);
281         NtClose(hDeviceKey);
282         return;
283     }
284     else if (pPartialInformation)
285     {
286         for (HardwareID = (LPCWSTR)pPartialInformation->Data;
287              (PUCHAR)HardwareID < pPartialInformation->Data + pPartialInformation->DataLength
288                  && *HardwareID
289                  && !bDriverInstalled;
290             HardwareID += wcslen(HardwareID) + 1)
291         {
292             bDriverInstalled = InstallDriver(hInf, hServices,hDeviceKey, DeviceId, HardwareID);
293         }
294     }
295 
296     if (!bDriverInstalled)
297     {
298         if (pPartialInformation)
299         {
300             RtlFreeHeap(ProcessHeap, 0, pPartialInformation);
301             pPartialInformation = NULL;
302         }
303         Status = NtQueryValueKey(
304             hDeviceKey,
305             &CompatibleIDsU,
306             KeyValuePartialInformation,
307             NULL,
308             0,
309             &ulRequired);
310         if (Status == STATUS_BUFFER_TOO_SMALL)
311         {
312             pPartialInformation = (PKEY_VALUE_PARTIAL_INFORMATION)RtlAllocateHeap(ProcessHeap, 0, ulRequired);
313             if (!pPartialInformation)
314             {
315                 DPRINT("RtlAllocateHeap() failed\n");
316                 NtClose(hDeviceKey);
317                 return;
318             }
319             Status = NtQueryValueKey(
320                 hDeviceKey,
321                 &CompatibleIDsU,
322                 KeyValuePartialInformation,
323                 pPartialInformation,
324                 ulRequired,
325                 &ulRequired);
326         }
327         if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
328         {
329             /* Nothing to do */
330         }
331         else if (!NT_SUCCESS(Status))
332         {
333             if (pPartialInformation)
334                 RtlFreeHeap(ProcessHeap, 0, pPartialInformation);
335             NtClose(hDeviceKey);
336             DPRINT1("NtQueryValueKey() failed with status 0x%08x\n", Status);
337             return;
338         }
339         else if (pPartialInformation)
340         {
341             for (HardwareID = (LPCWSTR)pPartialInformation->Data;
342                  (PUCHAR)HardwareID < pPartialInformation->Data + pPartialInformation->DataLength
343                      && *HardwareID
344                      && !bDriverInstalled;
345                 HardwareID += wcslen(HardwareID) + 1)
346             {
347                 bDriverInstalled = InstallDriver(hInf, hServices,hDeviceKey, DeviceId, HardwareID);
348             }
349         }
350     }
351     if (!bDriverInstalled)
352         DPRINT("No driver available for %S\n", DeviceId);
353 
354     RtlFreeHeap(ProcessHeap, 0, pPartialInformation);
355     NtClose(hDeviceKey);
356 }
357 
358 /* Loop to install all queued devices installations */
359 static ULONG NTAPI
360 DeviceInstallThread(IN PVOID Parameter)
361 {
362     HINF hSetupInf = *(HINF*)Parameter;
363     PLIST_ENTRY ListEntry;
364     DeviceInstallParams* Params;
365     LARGE_INTEGER Timeout;
366 
367     for (;;)
368     {
369         /* Dequeue the next oldest device-install event */
370         NtWaitForSingleObject(hDeviceInstallListMutex, FALSE, NULL);
371         ListEntry = (IsListEmpty(&DeviceInstallListHead)
372                         ? NULL : RemoveHeadList(&DeviceInstallListHead));
373         NtReleaseMutant(hDeviceInstallListMutex, NULL);
374 
375         if (ListEntry == NULL)
376         {
377             /*
378              * The list is now empty, but there may be a new enumerated device
379              * that is going to be added to the list soon. In order to avoid
380              * setting the hNoPendingInstalls event to release it soon after,
381              * we wait for maximum 1 second for no PnP enumeration event being
382              * received before declaring that no pending installations are
383              * taking place and setting the corresponding event.
384              */
385             Timeout.QuadPart = -10000000LL; /* Wait for 1 second */
386             if (NtWaitForSingleObject(hDeviceInstallListNotEmpty, FALSE, &Timeout) == STATUS_TIMEOUT)
387             {
388                 /* We timed out: set the event and do the actual wait */
389                 NtSetEvent(hNoPendingInstalls, NULL);
390                 NtWaitForSingleObject(hDeviceInstallListNotEmpty, FALSE, NULL);
391             }
392         }
393         else
394         {
395             NtResetEvent(hNoPendingInstalls, NULL);
396             Params = CONTAINING_RECORD(ListEntry, DeviceInstallParams, ListEntry);
397             InstallDevice(hSetupInf, hEnumKey, hServicesKey, Params->DeviceIds);
398             RtlFreeHeap(ProcessHeap, 0, Params);
399         }
400     }
401 
402     return 0;
403 }
404 
405 static ULONG NTAPI
406 PnpEventThread(IN PVOID Parameter)
407 {
408     NTSTATUS Status;
409     PLUGPLAY_CONTROL_USER_RESPONSE_DATA ResponseData = {0, 0, 0, 0};
410     PPLUGPLAY_EVENT_BLOCK PnpEvent, NewPnpEvent;
411     ULONG PnpEventSize;
412 
413     UNREFERENCED_PARAMETER(Parameter);
414 
415     PnpEventSize = 0x1000;
416     PnpEvent = RtlAllocateHeap(ProcessHeap, 0, PnpEventSize);
417     if (PnpEvent == NULL)
418     {
419         Status = STATUS_NO_MEMORY;
420         goto Quit;
421     }
422 
423     for (;;)
424     {
425         DPRINT("Calling NtGetPlugPlayEvent()\n");
426 
427         /* Wait for the next PnP event */
428         Status = NtGetPlugPlayEvent(0, 0, PnpEvent, PnpEventSize);
429 
430         /* Resize the buffer for the PnP event if it's too small */
431         if (Status == STATUS_BUFFER_TOO_SMALL)
432         {
433             PnpEventSize += 0x400;
434             NewPnpEvent = RtlReAllocateHeap(ProcessHeap, 0, PnpEvent, PnpEventSize);
435             if (NewPnpEvent == NULL)
436             {
437                 Status = STATUS_NO_MEMORY;
438                 goto Quit;
439             }
440             PnpEvent = NewPnpEvent;
441             continue;
442         }
443 
444         if (!NT_SUCCESS(Status))
445         {
446             DPRINT1("NtGetPlugPlayEvent() failed (Status 0x%08lx)\n", Status);
447             goto Quit;
448         }
449 
450         /* Process the PnP event */
451         DPRINT("Received PnP Event\n");
452         if (IsEqualGUID(&PnpEvent->EventGuid, &GUID_DEVICE_ENUMERATED))
453         {
454             DeviceInstallParams* Params;
455             ULONG len;
456             ULONG DeviceIdLength;
457 
458             DPRINT("Device enumerated: %S\n", PnpEvent->TargetDevice.DeviceIds);
459 
460             DeviceIdLength = wcslen(PnpEvent->TargetDevice.DeviceIds);
461             if (DeviceIdLength)
462             {
463                 /* Allocate a new device-install event */
464                 len = FIELD_OFFSET(DeviceInstallParams, DeviceIds) + (DeviceIdLength + 1) * sizeof(WCHAR);
465                 Params = RtlAllocateHeap(ProcessHeap, 0, len);
466                 if (Params)
467                 {
468                     wcscpy(Params->DeviceIds, PnpEvent->TargetDevice.DeviceIds);
469 
470                     /* Queue the event (will be dequeued by DeviceInstallThread) */
471                     NtWaitForSingleObject(hDeviceInstallListMutex, FALSE, NULL);
472                     InsertTailList(&DeviceInstallListHead, &Params->ListEntry);
473                     NtReleaseMutant(hDeviceInstallListMutex, NULL);
474 
475                     NtSetEvent(hDeviceInstallListNotEmpty, NULL);
476                 }
477                 else
478                 {
479                     DPRINT1("Not enough memory (size %lu)\n", len);
480                 }
481             }
482         }
483         else
484         {
485             DPRINT("Unknown event, GUID {%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}\n",
486                 PnpEvent->EventGuid.Data1, PnpEvent->EventGuid.Data2, PnpEvent->EventGuid.Data3,
487                 PnpEvent->EventGuid.Data4[0], PnpEvent->EventGuid.Data4[1], PnpEvent->EventGuid.Data4[2],
488                 PnpEvent->EventGuid.Data4[3], PnpEvent->EventGuid.Data4[4], PnpEvent->EventGuid.Data4[5],
489                 PnpEvent->EventGuid.Data4[6], PnpEvent->EventGuid.Data4[7]);
490         }
491 
492         /* Dequeue the current PnP event and signal the next one */
493         Status = NtPlugPlayControl(PlugPlayControlUserResponse,
494                                    &ResponseData,
495                                    sizeof(ResponseData));
496         if (!NT_SUCCESS(Status))
497         {
498             DPRINT1("NtPlugPlayControl(PlugPlayControlUserResponse) failed (Status 0x%08lx)\n", Status);
499             goto Quit;
500         }
501     }
502 
503     Status = STATUS_SUCCESS;
504 
505 Quit:
506     if (PnpEvent)
507         RtlFreeHeap(ProcessHeap, 0, PnpEvent);
508 
509     NtTerminateThread(NtCurrentThread(), Status);
510     return Status;
511 }
512 
513 NTSTATUS
514 WaitNoPendingInstallEvents(
515     IN PLARGE_INTEGER Timeout OPTIONAL)
516 {
517     return NtWaitForSingleObject(hNoPendingInstalls, FALSE, Timeout);
518 }
519 
520 BOOLEAN
521 EnableUserModePnpManager(VOID)
522 {
523     LARGE_INTEGER Timeout;
524 
525     /* Start the PnP thread */
526     if (hPnpThread != NULL)
527         NtResumeThread(hPnpThread, NULL);
528 
529     /*
530      * Wait a little bit so that we get a chance to have some events being
531      * queued by the time the device-installation thread becomes resumed.
532      */
533     Timeout.QuadPart = -10000000LL; /* Wait for 1 second */
534     NtWaitForSingleObject(hDeviceInstallListNotEmpty, FALSE, &Timeout);
535 
536     /* Start the device installation thread */
537     if (hDeviceInstallThread != NULL)
538         NtResumeThread(hDeviceInstallThread, NULL);
539 
540     return TRUE;
541 }
542 
543 BOOLEAN
544 DisableUserModePnpManager(VOID)
545 {
546     /* Wait until all pending installations are done, then freeze the threads */
547     if (WaitNoPendingInstallEvents(NULL) != STATUS_WAIT_0)
548         DPRINT1("WaitNoPendingInstallEvents() failed to wait!\n");
549 
550     // TODO: use signalling events
551 
552     NtSuspendThread(hPnpThread, NULL);
553     NtSuspendThread(hDeviceInstallThread, NULL);
554 
555     return TRUE;
556 }
557 
558 NTSTATUS
559 InitializeUserModePnpManager(
560     IN HINF* phSetupInf)
561 {
562     NTSTATUS Status;
563     OBJECT_ATTRIBUTES ObjectAttributes;
564 
565     UNICODE_STRING EnumU = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Enum");
566     UNICODE_STRING ServicesU = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Services");
567 
568     Status = NtCreateEvent(&hNoPendingInstalls,
569                            EVENT_ALL_ACCESS,
570                            NULL,
571                            NotificationEvent,
572                            FALSE);
573     if (!NT_SUCCESS(Status))
574     {
575         DPRINT1("Could not create the Pending-Install Event! (Status 0x%08lx)\n", Status);
576         goto Failure;
577     }
578 
579     /*
580      * Initialize the device-install event list
581      */
582 
583     Status = NtCreateEvent(&hDeviceInstallListNotEmpty,
584                            EVENT_ALL_ACCESS,
585                            NULL,
586                            SynchronizationEvent,
587                            FALSE);
588     if (!NT_SUCCESS(Status))
589     {
590         DPRINT1("Could not create the List Event! (Status 0x%08lx)\n", Status);
591         goto Failure;
592     }
593 
594     Status = NtCreateMutant(&hDeviceInstallListMutex,
595                             MUTANT_ALL_ACCESS,
596                             NULL, FALSE);
597     if (!NT_SUCCESS(Status))
598     {
599         DPRINT1("Could not create the List Mutex! (Status 0x%08lx)\n", Status);
600         goto Failure;
601     }
602     InitializeListHead(&DeviceInstallListHead);
603 
604     InitializeObjectAttributes(&ObjectAttributes, &EnumU, OBJ_CASE_INSENSITIVE, NULL, NULL);
605     Status = NtOpenKey(&hEnumKey, KEY_QUERY_VALUE, &ObjectAttributes);
606     if (!NT_SUCCESS(Status))
607     {
608         DPRINT1("NtOpenKey('%wZ') failed (Status 0x%08lx)\n", &EnumU, Status);
609         goto Failure;
610     }
611 
612     InitializeObjectAttributes(&ObjectAttributes, &ServicesU, OBJ_CASE_INSENSITIVE, NULL, NULL);
613     Status = NtCreateKey(&hServicesKey, KEY_ALL_ACCESS, &ObjectAttributes, 0, NULL, REG_OPTION_NON_VOLATILE, NULL);
614     if (!NT_SUCCESS(Status))
615     {
616         DPRINT1("NtCreateKey('%wZ') failed (Status 0x%08lx)\n", &ServicesU, Status);
617         goto Failure;
618     }
619 
620     /* Create the PnP event thread in suspended state */
621     Status = RtlCreateUserThread(NtCurrentProcess(),
622                                  NULL,
623                                  TRUE,
624                                  0,
625                                  0,
626                                  0,
627                                  PnpEventThread,
628                                  NULL,
629                                  &hPnpThread,
630                                  NULL);
631     if (!NT_SUCCESS(Status))
632     {
633         DPRINT1("Failed to create the PnP event thread (Status 0x%08lx)\n", Status);
634         hPnpThread = NULL;
635         goto Failure;
636     }
637 
638     /* Create the device installation thread in suspended state */
639     Status = RtlCreateUserThread(NtCurrentProcess(),
640                                  NULL,
641                                  TRUE,
642                                  0,
643                                  0,
644                                  0,
645                                  DeviceInstallThread,
646                                  phSetupInf,
647                                  &hDeviceInstallThread,
648                                  NULL);
649     if (!NT_SUCCESS(Status))
650     {
651         DPRINT1("Failed to create the device installation thread (Status 0x%08lx)\n", Status);
652         hDeviceInstallThread = NULL;
653         goto Failure;
654     }
655 
656     return STATUS_SUCCESS;
657 
658 Failure:
659     if (hPnpThread)
660     {
661         NtTerminateThread(hPnpThread, STATUS_SUCCESS);
662         NtClose(hPnpThread);
663     }
664     hPnpThread = NULL;
665 
666     if (hServicesKey)
667         NtClose(hServicesKey);
668     hServicesKey = NULL;
669 
670     if (hEnumKey)
671         NtClose(hEnumKey);
672     hEnumKey = NULL;
673 
674     if (hDeviceInstallListMutex)
675         NtClose(hDeviceInstallListMutex);
676     hDeviceInstallListMutex = NULL;
677 
678     if (hDeviceInstallListNotEmpty)
679         NtClose(hDeviceInstallListNotEmpty);
680     hDeviceInstallListNotEmpty = NULL;
681 
682     if (hNoPendingInstalls)
683         NtClose(hNoPendingInstalls);
684     hNoPendingInstalls = NULL;
685 
686     return Status;
687 }
688 
689 VOID
690 TerminateUserModePnpManager(VOID)
691 {
692     DisableUserModePnpManager();
693 
694     // TODO: use signalling events
695 
696     /* Kill the PnP thread as it blocks inside the NtGetPlugPlayEvent() call */
697     if (hPnpThread)
698     {
699         NtTerminateThread(hPnpThread, STATUS_SUCCESS);
700         NtClose(hPnpThread);
701     }
702     hPnpThread = NULL;
703 
704     /* Kill the device installation thread */
705     if (hDeviceInstallThread)
706     {
707         NtTerminateThread(hDeviceInstallThread, STATUS_SUCCESS);
708         NtClose(hDeviceInstallThread);
709     }
710     hDeviceInstallThread = NULL;
711 
712     /* Close the opened handles */
713 
714     if (hServicesKey)
715         NtClose(hServicesKey);
716     hServicesKey = NULL;
717 
718     if (hEnumKey)
719         NtClose(hEnumKey);
720     hEnumKey = NULL;
721 
722     if (hNoPendingInstalls)
723         NtClose(hNoPendingInstalls);
724     hNoPendingInstalls = NULL;
725 
726     if (hDeviceInstallListNotEmpty)
727         NtClose(hDeviceInstallListNotEmpty);
728     hDeviceInstallListNotEmpty = NULL;
729 }
730