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
;
48 /* FUNCTIONS *****************************************************************/
51 PnpEventThread(LPVOID lpParameter
)
53 DWORD dwRet
= ERROR_SUCCESS
;
56 PPLUGPLAY_EVENT_BLOCK PnpEvent
, NewPnpEvent
;
59 UNREFERENCED_PARAMETER(lpParameter
);
61 PnpEventSize
= 0x1000;
62 PnpEvent
= HeapAlloc(GetProcessHeap(), 0, PnpEventSize
);
64 return ERROR_OUTOFMEMORY
;
68 DPRINT("Calling NtGetPlugPlayEvent()\n");
70 /* Wait for the next PnP event */
71 Status
= NtGetPlugPlayEvent(0, 0, PnpEvent
, PnpEventSize
);
73 /* Resize the buffer for the PnP event if it's too small */
74 if (Status
== STATUS_BUFFER_TOO_SMALL
)
76 PnpEventSize
+= 0x400;
77 NewPnpEvent
= HeapReAlloc(GetProcessHeap(), 0, PnpEvent
, PnpEventSize
);
78 if (NewPnpEvent
== NULL
)
80 dwRet
= ERROR_OUTOFMEMORY
;
83 PnpEvent
= NewPnpEvent
;
87 if (!NT_SUCCESS(Status
))
89 DPRINT1("NtGetPlugPlayEvent() failed (Status 0x%08lx)\n", Status
);
93 /* Process the PnP event */
94 DPRINT("Received PnP Event\n");
95 if (UuidEqual(&PnpEvent
->EventGuid
, (UUID
*)&GUID_DEVICE_ENUMERATED
, &RpcStatus
))
97 DeviceInstallParams
* Params
;
101 DPRINT("Device enumerated: %S\n", PnpEvent
->TargetDevice
.DeviceIds
);
103 DeviceIdLength
= lstrlenW(PnpEvent
->TargetDevice
.DeviceIds
);
106 /* Queue device install (will be dequeued by DeviceInstallThread) */
107 len
= FIELD_OFFSET(DeviceInstallParams
, DeviceIds
) + (DeviceIdLength
+ 1) * sizeof(WCHAR
);
108 Params
= HeapAlloc(GetProcessHeap(), 0, len
);
111 wcscpy(Params
->DeviceIds
, PnpEvent
->TargetDevice
.DeviceIds
);
112 InterlockedPushEntrySList(&DeviceInstallListHead
, &Params
->ListEntry
);
113 SetEvent(hDeviceInstallListNotEmpty
);
117 else if (UuidEqual(&PnpEvent
->EventGuid
, (UUID
*)&GUID_DEVICE_ARRIVAL
, &RpcStatus
))
119 // DWORD dwRecipient;
121 DPRINT("Device arrival: %S\n", PnpEvent
->TargetDevice
.DeviceIds
);
123 // dwRecipient = BSM_ALLDESKTOPS | BSM_APPLICATIONS;
124 // BroadcastSystemMessageW(BSF_POSTMESSAGE,
127 // DBT_DEVNODES_CHANGED,
129 SendMessageW(HWND_BROADCAST
, WM_DEVICECHANGE
, DBT_DEVNODES_CHANGED
, 0);
131 else if (UuidEqual(&PnpEvent
->EventGuid
, (UUID
*)&GUID_DEVICE_EJECT_VETOED
, &RpcStatus
))
133 DPRINT1("Eject vetoed: %S\n", PnpEvent
->TargetDevice
.DeviceIds
);
135 else if (UuidEqual(&PnpEvent
->EventGuid
, (UUID
*)&GUID_DEVICE_KERNEL_INITIATED_EJECT
, &RpcStatus
))
137 DPRINT1("Kernel initiated eject: %S\n", PnpEvent
->TargetDevice
.DeviceIds
);
139 else if (UuidEqual(&PnpEvent
->EventGuid
, (UUID
*)&GUID_DEVICE_SAFE_REMOVAL
, &RpcStatus
))
141 // DWORD dwRecipient;
143 DPRINT1("Safe removal: %S\n", PnpEvent
->TargetDevice
.DeviceIds
);
145 // dwRecipient = BSM_ALLDESKTOPS | BSM_APPLICATIONS;
146 // BroadcastSystemMessageW(BSF_POSTMESSAGE,
149 // DBT_DEVNODES_CHANGED,
151 SendMessageW(HWND_BROADCAST
, WM_DEVICECHANGE
, DBT_DEVNODES_CHANGED
, 0);
153 else if (UuidEqual(&PnpEvent
->EventGuid
, (UUID
*)&GUID_DEVICE_SURPRISE_REMOVAL
, &RpcStatus
))
155 // DWORD dwRecipient;
157 DPRINT1("Surprise removal: %S\n", PnpEvent
->TargetDevice
.DeviceIds
);
159 // dwRecipient = BSM_ALLDESKTOPS | BSM_APPLICATIONS;
160 // BroadcastSystemMessageW(BSF_POSTMESSAGE,
163 // DBT_DEVNODES_CHANGED,
165 SendMessageW(HWND_BROADCAST
, WM_DEVICECHANGE
, DBT_DEVNODES_CHANGED
, 0);
167 else if (UuidEqual(&PnpEvent
->EventGuid
, (UUID
*)&GUID_DEVICE_REMOVAL_VETOED
, &RpcStatus
))
169 DPRINT1("Removal vetoed: %S\n", PnpEvent
->TargetDevice
.DeviceIds
);
171 else if (UuidEqual(&PnpEvent
->EventGuid
, (UUID
*)&GUID_DEVICE_REMOVE_PENDING
, &RpcStatus
))
173 DPRINT1("Removal pending: %S\n", PnpEvent
->TargetDevice
.DeviceIds
);
177 DPRINT1("Unknown event, GUID {%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}\n",
178 PnpEvent
->EventGuid
.Data1
, PnpEvent
->EventGuid
.Data2
, PnpEvent
->EventGuid
.Data3
,
179 PnpEvent
->EventGuid
.Data4
[0], PnpEvent
->EventGuid
.Data4
[1], PnpEvent
->EventGuid
.Data4
[2],
180 PnpEvent
->EventGuid
.Data4
[3], PnpEvent
->EventGuid
.Data4
[4], PnpEvent
->EventGuid
.Data4
[5],
181 PnpEvent
->EventGuid
.Data4
[6], PnpEvent
->EventGuid
.Data4
[7]);
184 /* Dequeue the current PnP event and signal the next one */
185 NtPlugPlayControl(PlugPlayControlUserResponse
, NULL
, 0);
188 HeapFree(GetProcessHeap(), 0, PnpEvent
);
195 UpdateServiceStatus(DWORD dwState
)
197 ServiceStatus
.dwServiceType
= SERVICE_WIN32_OWN_PROCESS
;
198 ServiceStatus
.dwCurrentState
= dwState
;
199 ServiceStatus
.dwControlsAccepted
= 0;
200 ServiceStatus
.dwWin32ExitCode
= 0;
201 ServiceStatus
.dwServiceSpecificExitCode
= 0;
202 ServiceStatus
.dwCheckPoint
= 0;
204 if (dwState
== SERVICE_START_PENDING
||
205 dwState
== SERVICE_STOP_PENDING
||
206 dwState
== SERVICE_PAUSE_PENDING
||
207 dwState
== SERVICE_CONTINUE_PENDING
)
208 ServiceStatus
.dwWaitHint
= 10000;
210 ServiceStatus
.dwWaitHint
= 0;
212 SetServiceStatus(ServiceStatusHandle
,
218 ServiceControlHandler(DWORD dwControl
,
223 DPRINT1("ServiceControlHandler() called\n");
227 case SERVICE_CONTROL_STOP
:
228 DPRINT1(" SERVICE_CONTROL_STOP received\n");
229 /* Stop listening to RPC Messages */
230 RpcMgmtStopServerListening(NULL
);
231 UpdateServiceStatus(SERVICE_STOPPED
);
232 return ERROR_SUCCESS
;
234 case SERVICE_CONTROL_PAUSE
:
235 DPRINT1(" SERVICE_CONTROL_PAUSE received\n");
236 UpdateServiceStatus(SERVICE_PAUSED
);
237 return ERROR_SUCCESS
;
239 case SERVICE_CONTROL_CONTINUE
:
240 DPRINT1(" SERVICE_CONTROL_CONTINUE received\n");
241 UpdateServiceStatus(SERVICE_RUNNING
);
242 return ERROR_SUCCESS
;
244 case SERVICE_CONTROL_INTERROGATE
:
245 DPRINT1(" SERVICE_CONTROL_INTERROGATE received\n");
246 SetServiceStatus(ServiceStatusHandle
,
248 return ERROR_SUCCESS
;
250 case SERVICE_CONTROL_SHUTDOWN
:
251 DPRINT1(" SERVICE_CONTROL_SHUTDOWN received\n");
252 /* Stop listening to RPC Messages */
253 RpcMgmtStopServerListening(NULL
);
254 UpdateServiceStatus(SERVICE_STOPPED
);
255 return ERROR_SUCCESS
;
258 DPRINT1(" Control %lu received\n", dwControl
);
259 return ERROR_CALL_NOT_IMPLEMENTED
;
265 ServiceMain(DWORD argc
, LPTSTR
*argv
)
270 UNREFERENCED_PARAMETER(argc
);
271 UNREFERENCED_PARAMETER(argv
);
273 DPRINT("ServiceMain() called\n");
275 ServiceStatusHandle
= RegisterServiceCtrlHandlerExW(ServiceName
,
276 ServiceControlHandler
,
278 if (!ServiceStatusHandle
)
280 DPRINT1("RegisterServiceCtrlHandlerExW() failed! (Error %lu)\n", GetLastError());
284 UpdateServiceStatus(SERVICE_START_PENDING
);
286 hThread
= CreateThread(NULL
,
293 CloseHandle(hThread
);
295 hThread
= CreateThread(NULL
,
302 CloseHandle(hThread
);
304 hThread
= CreateThread(NULL
,
311 CloseHandle(hThread
);
313 UpdateServiceStatus(SERVICE_RUNNING
);
315 DPRINT("ServiceMain() done\n");
319 InitializePnPManager(VOID
)
324 DPRINT("UMPNPMGR: InitializePnPManager() started\n");
326 /* We need this privilege for using CreateProcessAsUserW */
327 RtlAdjustPrivilege(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE
, TRUE
, FALSE
, &OldValue
);
329 hInstallEvent
= CreateEventW(NULL
, TRUE
, SetupIsActive()/*FALSE*/, NULL
);
330 if (hInstallEvent
== NULL
)
332 dwError
= GetLastError();
333 DPRINT1("Could not create the Install Event! (Error %lu)\n", dwError
);
337 hDeviceInstallListNotEmpty
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
338 if (hDeviceInstallListNotEmpty
== NULL
)
340 dwError
= GetLastError();
341 DPRINT1("Could not create the Event! (Error %lu)\n", dwError
);
345 hNoPendingInstalls
= CreateEventW(NULL
,
348 L
"Global\\PnP_No_Pending_Install_Events");
349 if (hNoPendingInstalls
== NULL
)
351 dwError
= GetLastError();
352 DPRINT1("Could not create the Event! (Error %lu)\n", dwError
);
356 InitializeSListHead(&DeviceInstallListHead
);
358 dwError
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
,
359 L
"System\\CurrentControlSet\\Enum",
363 if (dwError
!= ERROR_SUCCESS
)
365 DPRINT1("Could not open the Enum Key! (Error %lu)\n", dwError
);
369 dwError
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
,
370 L
"System\\CurrentControlSet\\Control\\Class",
374 if (dwError
!= ERROR_SUCCESS
)
376 DPRINT1("Could not open the Class Key! (Error %lu)\n", dwError
);
380 DPRINT("UMPNPMGR: InitializePnPManager() done\n");
386 DllMain(HINSTANCE hinstDLL
,
392 case DLL_PROCESS_ATTACH
:
393 DisableThreadLibraryCalls(hinstDLL
);
394 InitializePnPManager();
397 case DLL_PROCESS_DETACH
: