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