xref: /reactos/base/setup/usetup/devinst.c (revision 7eead935)
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     PPLUGPLAY_EVENT_BLOCK PnpEvent, NewPnpEvent;
409     ULONG PnpEventSize;
410 
411     UNREFERENCED_PARAMETER(Parameter);
412 
413     PnpEventSize = 0x1000;
414     PnpEvent = RtlAllocateHeap(ProcessHeap, 0, PnpEventSize);
415     if (PnpEvent == NULL)
416     {
417         Status = STATUS_NO_MEMORY;
418         goto Quit;
419     }
420 
421     for (;;)
422     {
423         DPRINT("Calling NtGetPlugPlayEvent()\n");
424 
425         /* Wait for the next PnP event */
426         Status = NtGetPlugPlayEvent(0, 0, PnpEvent, PnpEventSize);
427 
428         /* Resize the buffer for the PnP event if it's too small */
429         if (Status == STATUS_BUFFER_TOO_SMALL)
430         {
431             PnpEventSize += 0x400;
432             NewPnpEvent = RtlReAllocateHeap(ProcessHeap, 0, PnpEvent, PnpEventSize);
433             if (NewPnpEvent == NULL)
434             {
435                 Status = STATUS_NO_MEMORY;
436                 goto Quit;
437             }
438             PnpEvent = NewPnpEvent;
439             continue;
440         }
441 
442         if (!NT_SUCCESS(Status))
443         {
444             DPRINT1("NtGetPlugPlayEvent() failed (Status 0x%08lx)\n", Status);
445             goto Quit;
446         }
447 
448         /* Process the PnP event */
449         DPRINT("Received PnP Event\n");
450         if (IsEqualGUID(&PnpEvent->EventGuid, &GUID_DEVICE_ENUMERATED))
451         {
452             DeviceInstallParams* Params;
453             ULONG len;
454             ULONG DeviceIdLength;
455 
456             DPRINT("Device enumerated event: %S\n", PnpEvent->TargetDevice.DeviceIds);
457 
458             DeviceIdLength = wcslen(PnpEvent->TargetDevice.DeviceIds);
459             if (DeviceIdLength)
460             {
461                 /* Queue device install (will be dequeued by DeviceInstallThread) */
462                 len = FIELD_OFFSET(DeviceInstallParams, DeviceIds) + (DeviceIdLength + 1) * sizeof(WCHAR);
463                 Params = RtlAllocateHeap(ProcessHeap, 0, len);
464                 if (Params)
465                 {
466                     wcscpy(Params->DeviceIds, PnpEvent->TargetDevice.DeviceIds);
467                     RtlInterlockedPushEntrySList(&DeviceInstallListHead, &Params->ListEntry);
468                     NtSetEvent(hDeviceInstallListNotEmpty, NULL);
469                 }
470                 else
471                 {
472                     DPRINT1("Not enough memory (size %lu)\n", len);
473                 }
474             }
475         }
476         else
477         {
478             DPRINT("Unknown event, GUID {%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}\n",
479                 PnpEvent->EventGuid.Data1, PnpEvent->EventGuid.Data2, PnpEvent->EventGuid.Data3,
480                 PnpEvent->EventGuid.Data4[0], PnpEvent->EventGuid.Data4[1], PnpEvent->EventGuid.Data4[2],
481                 PnpEvent->EventGuid.Data4[3], PnpEvent->EventGuid.Data4[4], PnpEvent->EventGuid.Data4[5],
482                 PnpEvent->EventGuid.Data4[6], PnpEvent->EventGuid.Data4[7]);
483         }
484 
485         /* Dequeue the current PnP event and signal the next one */
486         NtPlugPlayControl(PlugPlayControlUserResponse, NULL, 0);
487     }
488 
489     Status = STATUS_SUCCESS;
490 
491 Quit:
492     if (PnpEvent)
493         RtlFreeHeap(ProcessHeap, 0, PnpEvent);
494 
495     NtTerminateThread(NtCurrentThread(), Status);
496     return Status;
497 }
498 
499 NTSTATUS
500 WaitNoPendingInstallEvents(
501     IN PLARGE_INTEGER Timeout OPTIONAL)
502 {
503     return NtWaitForSingleObject(hNoPendingInstalls, FALSE, Timeout);
504 }
505 
506 BOOLEAN
507 EnableUserModePnpManager(VOID)
508 {
509     LARGE_INTEGER Timeout;
510 
511     /* Start the PnP thread */
512     if (hPnpThread != NULL)
513         NtResumeThread(hPnpThread, NULL);
514 
515     /*
516      * Wait a little bit so that we get a chance to have some events being
517      * queued by the time the device-installation thread becomes resumed.
518      */
519     Timeout.QuadPart = -10000000LL; /* Wait for 1 second */
520     NtWaitForSingleObject(hDeviceInstallListNotEmpty, FALSE, &Timeout);
521 
522     /* Start the device installation thread */
523     if (hDeviceInstallThread != NULL)
524         NtResumeThread(hDeviceInstallThread, NULL);
525 
526     return TRUE;
527 }
528 
529 BOOLEAN
530 DisableUserModePnpManager(VOID)
531 {
532     /* Wait until all pending installations are done, then freeze the threads */
533     if (WaitNoPendingInstallEvents(NULL) != STATUS_WAIT_0)
534         DPRINT1("WaitNoPendingInstallEvents() failed to wait!\n");
535 
536     // TODO: use signalling events
537 
538     NtSuspendThread(hPnpThread, NULL);
539     NtSuspendThread(hDeviceInstallThread, NULL);
540 
541     return TRUE;
542 }
543 
544 NTSTATUS
545 InitializeUserModePnpManager(
546     IN HINF* phSetupInf)
547 {
548     NTSTATUS Status;
549     OBJECT_ATTRIBUTES ObjectAttributes;
550 
551     UNICODE_STRING EnumU = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Enum");
552     UNICODE_STRING ServicesU = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Services");
553 
554     Status = NtCreateEvent(&hDeviceInstallListNotEmpty,
555                            EVENT_ALL_ACCESS,
556                            NULL,
557                            SynchronizationEvent,
558                            FALSE);
559     if (!NT_SUCCESS(Status))
560     {
561         DPRINT1("Could not create the event! (Status 0x%08lx)\n", Status);
562         goto Failure;
563     }
564 
565     Status = NtCreateEvent(&hNoPendingInstalls,
566                            EVENT_ALL_ACCESS,
567                            NULL,
568                            NotificationEvent,
569                            FALSE);
570     if (!NT_SUCCESS(Status))
571     {
572         DPRINT1("Could not create the event! (Status 0x%08lx)\n", Status);
573         goto Failure;
574     }
575 
576     RtlInitializeSListHead(&DeviceInstallListHead);
577 
578     InitializeObjectAttributes(&ObjectAttributes, &EnumU, OBJ_CASE_INSENSITIVE, NULL, NULL);
579     Status = NtOpenKey(&hEnumKey, KEY_QUERY_VALUE, &ObjectAttributes);
580     if (!NT_SUCCESS(Status))
581     {
582         DPRINT1("NtOpenKey('%wZ') failed (Status 0x%08lx)\n", &EnumU, Status);
583         goto Failure;
584     }
585 
586     InitializeObjectAttributes(&ObjectAttributes, &ServicesU, OBJ_CASE_INSENSITIVE, NULL, NULL);
587     Status = NtCreateKey(&hServicesKey, KEY_ALL_ACCESS, &ObjectAttributes, 0, NULL, REG_OPTION_NON_VOLATILE, NULL);
588     if (!NT_SUCCESS(Status))
589     {
590         DPRINT1("NtCreateKey('%wZ') failed (Status 0x%08lx)\n", &ServicesU, Status);
591         goto Failure;
592     }
593 
594     /* Create the PnP event thread in suspended state */
595     Status = RtlCreateUserThread(NtCurrentProcess(),
596                                  NULL,
597                                  TRUE,
598                                  0,
599                                  0,
600                                  0,
601                                  PnpEventThread,
602                                  NULL,
603                                  &hPnpThread,
604                                  NULL);
605     if (!NT_SUCCESS(Status))
606     {
607         DPRINT1("Failed to create the PnP event thread (Status 0x%08lx)\n", Status);
608         hPnpThread = NULL;
609         goto Failure;
610     }
611 
612     /* Create the device installation thread in suspended state */
613     Status = RtlCreateUserThread(NtCurrentProcess(),
614                                  NULL,
615                                  TRUE,
616                                  0,
617                                  0,
618                                  0,
619                                  DeviceInstallThread,
620                                  phSetupInf,
621                                  &hDeviceInstallThread,
622                                  NULL);
623     if (!NT_SUCCESS(Status))
624     {
625         DPRINT1("Failed to create the device installation thread (Status 0x%08lx)\n", Status);
626         hDeviceInstallThread = NULL;
627         goto Failure;
628     }
629 
630     return STATUS_SUCCESS;
631 
632 Failure:
633     if (hPnpThread)
634     {
635         NtTerminateThread(hPnpThread, STATUS_SUCCESS);
636         NtClose(hPnpThread);
637     }
638     hPnpThread = NULL;
639 
640     if (hServicesKey)
641         NtClose(hServicesKey);
642     hServicesKey = NULL;
643 
644     if (hEnumKey)
645         NtClose(hEnumKey);
646     hEnumKey = NULL;
647 
648     if (hNoPendingInstalls)
649         NtClose(hNoPendingInstalls);
650     hNoPendingInstalls = NULL;
651 
652     if (hDeviceInstallListNotEmpty)
653         NtClose(hDeviceInstallListNotEmpty);
654     hDeviceInstallListNotEmpty = NULL;
655 
656     return Status;
657 }
658 
659 VOID
660 TerminateUserModePnpManager(VOID)
661 {
662     DisableUserModePnpManager();
663 
664     // TODO: use signalling events
665 
666     /* Kill the PnP thread as it blocks inside the NtGetPlugPlayEvent() call */
667     if (hPnpThread)
668     {
669         NtTerminateThread(hPnpThread, STATUS_SUCCESS);
670         NtClose(hPnpThread);
671     }
672     hPnpThread = NULL;
673 
674     /* Kill the device installation thread */
675     if (hDeviceInstallThread)
676     {
677         NtTerminateThread(hDeviceInstallThread, STATUS_SUCCESS);
678         NtClose(hDeviceInstallThread);
679     }
680     hDeviceInstallThread = NULL;
681 
682     /* Close the opened handles */
683 
684     if (hServicesKey)
685         NtClose(hServicesKey);
686     hServicesKey = NULL;
687 
688     if (hEnumKey)
689         NtClose(hEnumKey);
690     hEnumKey = NULL;
691 
692     if (hNoPendingInstalls)
693         NtClose(hNoPendingInstalls);
694     hNoPendingInstalls = NULL;
695 
696     if (hDeviceInstallListNotEmpty)
697         NtClose(hDeviceInstallListNotEmpty);
698     hDeviceInstallListNotEmpty = NULL;
699 }
700