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