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