3 * Copyright (C) 2005 ReactOS Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 * COPYRIGHT: See COPYING in the top level directory
21 * PROJECT: ReactOS kernel
22 * FILE: base/services/umpnpmgr/umpnpmgr.c
23 * PURPOSE: User-mode Plug and Play manager
24 * PROGRAMMER: Eric Kohl (eric.kohl@reactos.org)
25 * Hervé Poussineau (hpoussin@reactos.org)
26 * Colin Finck (colin@reactos.org)
29 /* INCLUDES *****************************************************************/
37 /* GLOBALS ******************************************************************/
39 static WCHAR ServiceName
[] = L
"PlugPlay";
41 static SERVICE_STATUS_HANDLE ServiceStatusHandle
;
42 static SERVICE_STATUS ServiceStatus
;
45 HKEY hClassKey
= NULL
;
46 BOOL g_IsUISuppressed
= FALSE
;
48 /* FUNCTIONS *****************************************************************/
51 PnpEventThread(LPVOID lpParameter
)
53 PLUGPLAY_CONTROL_USER_RESPONSE_DATA ResponseData
= {0, 0, 0, 0};
54 DWORD dwRet
= ERROR_SUCCESS
;
57 PPLUGPLAY_EVENT_BLOCK PnpEvent
, NewPnpEvent
;
60 UNREFERENCED_PARAMETER(lpParameter
);
62 PnpEventSize
= 0x1000;
63 PnpEvent
= HeapAlloc(GetProcessHeap(), 0, PnpEventSize
);
65 return ERROR_OUTOFMEMORY
;
69 DPRINT("Calling NtGetPlugPlayEvent()\n");
71 /* Wait for the next PnP event */
72 Status
= NtGetPlugPlayEvent(0, 0, PnpEvent
, PnpEventSize
);
74 /* Resize the buffer for the PnP event if it's too small */
75 if (Status
== STATUS_BUFFER_TOO_SMALL
)
77 PnpEventSize
+= 0x400;
78 NewPnpEvent
= HeapReAlloc(GetProcessHeap(), 0, PnpEvent
, PnpEventSize
);
79 if (NewPnpEvent
== NULL
)
81 dwRet
= ERROR_OUTOFMEMORY
;
84 PnpEvent
= NewPnpEvent
;
88 if (!NT_SUCCESS(Status
))
90 DPRINT1("NtGetPlugPlayEvent() failed (Status 0x%08lx)\n", Status
);
94 /* Process the PnP event */
95 DPRINT("Received PnP Event\n");
96 if (UuidEqual(&PnpEvent
->EventGuid
, (UUID
*)&GUID_DEVICE_ENUMERATED
, &RpcStatus
))
98 DeviceInstallParams
* Params
;
100 DWORD DeviceIdLength
;
102 DPRINT("Device enumerated: %S\n", PnpEvent
->TargetDevice
.DeviceIds
);
104 DeviceIdLength
= lstrlenW(PnpEvent
->TargetDevice
.DeviceIds
);
107 /* Allocate a new device-install event */
108 len
= FIELD_OFFSET(DeviceInstallParams
, DeviceIds
) + (DeviceIdLength
+ 1) * sizeof(WCHAR
);
109 Params
= HeapAlloc(GetProcessHeap(), 0, len
);
112 wcscpy(Params
->DeviceIds
, PnpEvent
->TargetDevice
.DeviceIds
);
114 /* Queue the event (will be dequeued by DeviceInstallThread) */
115 WaitForSingleObject(hDeviceInstallListMutex
, INFINITE
);
116 InsertTailList(&DeviceInstallListHead
, &Params
->ListEntry
);
117 ReleaseMutex(hDeviceInstallListMutex
);
119 SetEvent(hDeviceInstallListNotEmpty
);
123 else if (UuidEqual(&PnpEvent
->EventGuid
, (UUID
*)&GUID_DEVICE_ARRIVAL
, &RpcStatus
))
125 // DWORD dwRecipient;
127 DPRINT("Device arrival: %S\n", PnpEvent
->TargetDevice
.DeviceIds
);
129 // dwRecipient = BSM_ALLDESKTOPS | BSM_APPLICATIONS;
130 // BroadcastSystemMessageW(BSF_POSTMESSAGE,
133 // DBT_DEVNODES_CHANGED,
135 SendMessageW(HWND_BROADCAST
, WM_DEVICECHANGE
, DBT_DEVNODES_CHANGED
, 0);
137 else if (UuidEqual(&PnpEvent
->EventGuid
, (UUID
*)&GUID_DEVICE_EJECT_VETOED
, &RpcStatus
))
139 DPRINT1("Eject vetoed: %S\n", PnpEvent
->TargetDevice
.DeviceIds
);
141 else if (UuidEqual(&PnpEvent
->EventGuid
, (UUID
*)&GUID_DEVICE_KERNEL_INITIATED_EJECT
, &RpcStatus
))
143 DPRINT1("Kernel initiated eject: %S\n", PnpEvent
->TargetDevice
.DeviceIds
);
145 else if (UuidEqual(&PnpEvent
->EventGuid
, (UUID
*)&GUID_DEVICE_SAFE_REMOVAL
, &RpcStatus
))
147 // DWORD dwRecipient;
149 DPRINT1("Safe removal: %S\n", PnpEvent
->TargetDevice
.DeviceIds
);
151 // dwRecipient = BSM_ALLDESKTOPS | BSM_APPLICATIONS;
152 // BroadcastSystemMessageW(BSF_POSTMESSAGE,
155 // DBT_DEVNODES_CHANGED,
157 SendMessageW(HWND_BROADCAST
, WM_DEVICECHANGE
, DBT_DEVNODES_CHANGED
, 0);
159 else if (UuidEqual(&PnpEvent
->EventGuid
, (UUID
*)&GUID_DEVICE_SURPRISE_REMOVAL
, &RpcStatus
))
161 // DWORD dwRecipient;
163 DPRINT1("Surprise removal: %S\n", PnpEvent
->TargetDevice
.DeviceIds
);
165 // dwRecipient = BSM_ALLDESKTOPS | BSM_APPLICATIONS;
166 // BroadcastSystemMessageW(BSF_POSTMESSAGE,
169 // DBT_DEVNODES_CHANGED,
171 SendMessageW(HWND_BROADCAST
, WM_DEVICECHANGE
, DBT_DEVNODES_CHANGED
, 0);
173 else if (UuidEqual(&PnpEvent
->EventGuid
, (UUID
*)&GUID_DEVICE_REMOVAL_VETOED
, &RpcStatus
))
175 DPRINT1("Removal vetoed: %S\n", PnpEvent
->TargetDevice
.DeviceIds
);
177 else if (UuidEqual(&PnpEvent
->EventGuid
, (UUID
*)&GUID_DEVICE_REMOVE_PENDING
, &RpcStatus
))
179 DPRINT1("Removal pending: %S\n", PnpEvent
->TargetDevice
.DeviceIds
);
183 DPRINT1("Unknown event, GUID {%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}\n",
184 PnpEvent
->EventGuid
.Data1
, PnpEvent
->EventGuid
.Data2
, PnpEvent
->EventGuid
.Data3
,
185 PnpEvent
->EventGuid
.Data4
[0], PnpEvent
->EventGuid
.Data4
[1], PnpEvent
->EventGuid
.Data4
[2],
186 PnpEvent
->EventGuid
.Data4
[3], PnpEvent
->EventGuid
.Data4
[4], PnpEvent
->EventGuid
.Data4
[5],
187 PnpEvent
->EventGuid
.Data4
[6], PnpEvent
->EventGuid
.Data4
[7]);
190 /* Dequeue the current PnP event and signal the next one */
191 Status
= NtPlugPlayControl(PlugPlayControlUserResponse
,
193 sizeof(ResponseData
));
194 if (!NT_SUCCESS(Status
))
196 DPRINT1("NtPlugPlayControl(PlugPlayControlUserResponse) failed (Status 0x%08lx)\n", Status
);
201 HeapFree(GetProcessHeap(), 0, PnpEvent
);
208 UpdateServiceStatus(DWORD dwState
)
210 ServiceStatus
.dwServiceType
= SERVICE_WIN32_OWN_PROCESS
;
211 ServiceStatus
.dwCurrentState
= dwState
;
212 ServiceStatus
.dwControlsAccepted
= 0;
213 ServiceStatus
.dwWin32ExitCode
= 0;
214 ServiceStatus
.dwServiceSpecificExitCode
= 0;
215 ServiceStatus
.dwCheckPoint
= 0;
217 if (dwState
== SERVICE_START_PENDING
||
218 dwState
== SERVICE_STOP_PENDING
||
219 dwState
== SERVICE_PAUSE_PENDING
||
220 dwState
== SERVICE_CONTINUE_PENDING
)
221 ServiceStatus
.dwWaitHint
= 10000;
223 ServiceStatus
.dwWaitHint
= 0;
225 SetServiceStatus(ServiceStatusHandle
,
231 ServiceControlHandler(DWORD dwControl
,
236 DPRINT1("ServiceControlHandler() called\n");
240 case SERVICE_CONTROL_STOP
:
241 DPRINT1(" SERVICE_CONTROL_STOP received\n");
242 /* Stop listening to RPC Messages */
243 RpcMgmtStopServerListening(NULL
);
244 UpdateServiceStatus(SERVICE_STOPPED
);
245 return ERROR_SUCCESS
;
247 case SERVICE_CONTROL_PAUSE
:
248 DPRINT1(" SERVICE_CONTROL_PAUSE received\n");
249 UpdateServiceStatus(SERVICE_PAUSED
);
250 return ERROR_SUCCESS
;
252 case SERVICE_CONTROL_CONTINUE
:
253 DPRINT1(" SERVICE_CONTROL_CONTINUE received\n");
254 UpdateServiceStatus(SERVICE_RUNNING
);
255 return ERROR_SUCCESS
;
257 case SERVICE_CONTROL_INTERROGATE
:
258 DPRINT1(" SERVICE_CONTROL_INTERROGATE received\n");
259 SetServiceStatus(ServiceStatusHandle
,
261 return ERROR_SUCCESS
;
263 case SERVICE_CONTROL_SHUTDOWN
:
264 DPRINT1(" SERVICE_CONTROL_SHUTDOWN received\n");
265 /* Stop listening to RPC Messages */
266 RpcMgmtStopServerListening(NULL
);
267 UpdateServiceStatus(SERVICE_STOPPED
);
268 return ERROR_SUCCESS
;
271 DPRINT1(" Control %lu received\n", dwControl
);
272 return ERROR_CALL_NOT_IMPLEMENTED
;
283 DWORD dwError
, dwType
, dwData
;
284 DWORD cbData
= sizeof(dwData
);
290 dwError
= RegOpenKeyExW(hKey
,
295 if (dwError
!= ERROR_SUCCESS
)
297 DPRINT("GetBooleanRegValue(): RegOpenKeyExW() has failed to open '%S' key! (Error: %lu)\n",
302 dwError
= RegQueryValueExW(hSubKey
,
308 if (dwError
!= ERROR_SUCCESS
)
310 DPRINT("GetBooleanRegValue(): RegQueryValueExW() has failed to query '%S' value! (Error: %lu)\n",
314 if (dwType
!= REG_DWORD
)
316 DPRINT("GetBooleanRegValue(): The value is not of REG_DWORD type!\n");
320 /* Return the value */
321 *pValue
= (dwData
== 1);
324 RegCloseKey(hSubKey
);
330 GetSuppressNewUIValue(VOID
)
332 BOOL bSuppressNewHWUI
= FALSE
;
335 * Query the SuppressNewHWUI policy registry value. Don't cache it
336 * as we want to update our behaviour in consequence.
338 GetBooleanRegValue(HKEY_LOCAL_MACHINE
,
339 L
"Software\\Policies\\Microsoft\\Windows\\DeviceInstall\\Settings",
342 if (bSuppressNewHWUI
)
343 DPRINT("GetSuppressNewUIValue(): newdev.dll's wizard UI won't be shown!\n");
345 return bSuppressNewHWUI
;
349 ServiceMain(DWORD argc
, LPTSTR
*argv
)
354 UNREFERENCED_PARAMETER(argc
);
355 UNREFERENCED_PARAMETER(argv
);
357 DPRINT("ServiceMain() called\n");
359 ServiceStatusHandle
= RegisterServiceCtrlHandlerExW(ServiceName
,
360 ServiceControlHandler
,
362 if (!ServiceStatusHandle
)
364 DPRINT1("RegisterServiceCtrlHandlerExW() failed! (Error %lu)\n", GetLastError());
368 UpdateServiceStatus(SERVICE_START_PENDING
);
370 hThread
= CreateThread(NULL
,
377 CloseHandle(hThread
);
379 hThread
= CreateThread(NULL
,
386 CloseHandle(hThread
);
388 hThread
= CreateThread(NULL
,
395 CloseHandle(hThread
);
397 UpdateServiceStatus(SERVICE_RUNNING
);
399 DPRINT("ServiceMain() done\n");
403 InitializePnPManager(VOID
)
408 DPRINT("UMPNPMGR: InitializePnPManager() started\n");
410 /* We need this privilege for using CreateProcessAsUserW */
411 RtlAdjustPrivilege(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE
, TRUE
, FALSE
, &OldValue
);
413 hInstallEvent
= CreateEventW(NULL
, TRUE
, SetupIsActive()/*FALSE*/, NULL
);
414 if (hInstallEvent
== NULL
)
416 dwError
= GetLastError();
417 DPRINT1("Could not create the Install Event! (Error %lu)\n", dwError
);
421 hNoPendingInstalls
= CreateEventW(NULL
,
424 L
"Global\\PnP_No_Pending_Install_Events");
425 if (hNoPendingInstalls
== NULL
)
427 dwError
= GetLastError();
428 DPRINT1("Could not create the Pending-Install Event! (Error %lu)\n", dwError
);
433 * Initialize the device-install event list
436 hDeviceInstallListNotEmpty
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
437 if (hDeviceInstallListNotEmpty
== NULL
)
439 dwError
= GetLastError();
440 DPRINT1("Could not create the List Event! (Error %lu)\n", dwError
);
444 hDeviceInstallListMutex
= CreateMutexW(NULL
, FALSE
, NULL
);
445 if (hDeviceInstallListMutex
== NULL
)
447 dwError
= GetLastError();
448 DPRINT1("Could not create the List Mutex! (Error %lu)\n", dwError
);
451 InitializeListHead(&DeviceInstallListHead
);
453 /* Query the SuppressUI registry value and cache it for our whole lifetime */
454 GetBooleanRegValue(HKEY_LOCAL_MACHINE
,
455 L
"System\\CurrentControlSet\\Services\\PlugPlay\\Parameters",
458 if (g_IsUISuppressed
)
459 DPRINT("UMPNPMGR: newdev.dll's wizard UI won't be shown!\n");
461 dwError
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
,
462 L
"System\\CurrentControlSet\\Enum",
466 if (dwError
!= ERROR_SUCCESS
)
468 DPRINT1("Could not open the Enum Key! (Error %lu)\n", dwError
);
472 dwError
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
,
473 L
"System\\CurrentControlSet\\Control\\Class",
477 if (dwError
!= ERROR_SUCCESS
)
479 DPRINT1("Could not open the Class Key! (Error %lu)\n", dwError
);
483 DPRINT("UMPNPMGR: InitializePnPManager() done\n");
489 DllMain(HINSTANCE hinstDLL
,
495 case DLL_PROCESS_ATTACH
:
496 DisableThreadLibraryCalls(hinstDLL
);
497 InitializePnPManager();
500 case DLL_PROCESS_DETACH
: