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