Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

16520 lines
555 KiB

  1. /*++
  2. Copyright (c) Microsoft Corporation. All rights reserved.
  3. Module Name:
  4. revent.c
  5. Abstract:
  6. This module contains the server-side event notification and device
  7. installation routines.
  8. Author:
  9. Paula Tomlinson (paulat) 6-28-1995
  10. Environment:
  11. User-mode only.
  12. Revision History:
  13. 28-June-1995 paulat
  14. Creation and initial implementation.
  15. --*/
  16. //
  17. // includes
  18. //
  19. #include "precomp.h"
  20. #pragma hdrstop
  21. #include "umpnpi.h"
  22. #include "umpnpdat.h"
  23. #include "pnpipc.h"
  24. #include "pnpmsg.h"
  25. #include <process.h>
  26. #pragma warning(push)
  27. #pragma warning(disable:4201) // warning C4201: nonstandard extension used : nameless struct/union
  28. #include <setupapi.h>
  29. #pragma warning(pop)
  30. #include <spapip.h>
  31. #include <wtsapi32.h>
  32. #pragma warning(push)
  33. #pragma warning(disable:4201) // warning C4201: nonstandard extension used : nameless struct/union
  34. #pragma warning(disable:4214) // warning C4214: nonstandard extension used : bit field types other than int
  35. #include <winsta.h>
  36. #pragma warning(pop)
  37. #include <userenv.h>
  38. #include <syslib.h>
  39. #include <initguid.h>
  40. #pragma warning(push)
  41. #pragma warning(disable:4201) // warning C4201: nonstandard extension used : nameless struct/union
  42. #include <winioctl.h>
  43. #pragma warning(pop)
  44. #include <ntddpar.h>
  45. #include <pnpmgr.h>
  46. #include <wdmguid.h>
  47. #include <ioevent.h>
  48. #include <devguid.h>
  49. #include <winsvcp.h>
  50. #include <svcsp.h>
  51. //
  52. // Maximum number of times (per pass) we will reenumerate the device tree during
  53. // GUI setup in an attempt to find and install new devices.
  54. //
  55. #define MAX_REENUMERATION_COUNT 128
  56. //
  57. // Define and initialize a global variable, GUID_NULL
  58. // (from coguid.h)
  59. //
  60. DEFINE_GUID(GUID_NULL, 0L, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
  61. //
  62. // Private interface device class that is used to register for all devnode change
  63. // notifications. This is no longer supported, but we want to fail anyone who registers
  64. // this GUID.
  65. //
  66. DEFINE_GUID(GUID_DEVNODE_CHANGE, 0xfa1fb208L, 0xf892, 0x11d0, 0x8a, 0x2e, 0x00, 0x00, 0xf8, 0x75, 0x3f, 0x55);
  67. //
  68. // Private interface device class that is assigned to entries registered for
  69. // device interface change notifications, using the
  70. // DEVICE_NOTIFY_ALL_INTERFACE_CLASSES flag. For internal use only.
  71. //
  72. DEFINE_GUID(GUID_DEVINTERFACE_INCLUDE_ALL_INTERFACE_CLASSES,
  73. 0x2121db68, 0x0993, 0x4a29, 0xb8, 0xe0, 0x1e, 0x51, 0x9c, 0x43, 0x72, 0xe6);
  74. //
  75. // SessionId 0 is the main session, and is always created during system startup
  76. // and remains until system shutdown, whether or not terminal services is
  77. // running. This session always hosts services.exe and all services, so it is
  78. // the only session that our ConsoleCtrlHandler can receive events for.
  79. //
  80. #define MAIN_SESSION ((ULONG) 0)
  81. #define INVALID_SESSION ((ULONG)-1)
  82. //
  83. // The active console session is the session currently connected to the physical
  84. // Console. We store this value in a global variable, whose access is
  85. // controlled by an event. The routine GetActiveConsoleSessionId() is used to
  86. // retrieve the value when it is safe to do so.
  87. //
  88. // Note that SessionId 0 is the initial console session, and that the
  89. // SessionNotificationHandler is responsible for maintaining state.
  90. //
  91. ULONG gActiveConsoleSessionId = MAIN_SESSION; // system always starts with session 0
  92. HANDLE ghActiveConsoleSessionEvent = NULL; // nonsignaled while session change is in progress
  93. //
  94. // We always use DeviceEventWorker and BroadcastSystemMessage to deliver
  95. // notification to windows in SessionId 0. For all other sessions, we use
  96. // WinStationSendWindowMessage and WinStationBroadcastSystemMessage.
  97. // These are the timeout period (in seconds) for messages sent and broadcast to
  98. // sessions other than SessionId 0. These times should be the same as those
  99. // implemented by their SessionId 0 counterparts.
  100. //
  101. #define DEFAULT_SEND_TIME_OUT 30 // same as DeviceEventWorker
  102. #define DEFAULT_BROADCAST_TIME_OUT 5 // same as BroadcastSystemMessage
  103. //
  104. // Notification list structure.
  105. //
  106. typedef struct _PNP_NOTIFY_LIST {
  107. PVOID Next;
  108. PVOID Previous;
  109. LOCKINFO Lock;
  110. } PNP_NOTIFY_LIST, *PPNP_NOTIFY_LIST;
  111. //
  112. // Notification entry structure.
  113. //
  114. typedef struct _PNP_NOTIFY_ENTRY {
  115. PVOID Next;
  116. PVOID Previous;
  117. BOOL Unregistered;
  118. ULONG Signature;
  119. HANDLE Handle;
  120. DWORD Flags;
  121. ULONG SessionId;
  122. ULONG Freed;
  123. ULONG64 ClientCtxPtr;
  124. LPWSTR ClientName;
  125. union {
  126. struct {
  127. GUID ClassGuid;
  128. } Class;
  129. struct {
  130. HANDLE FileHandle;
  131. WCHAR DeviceId[MAX_DEVICE_ID_LEN];
  132. } Target;
  133. struct {
  134. DWORD Reserved;
  135. } Devnode;
  136. struct {
  137. DWORD scmControls;
  138. } Service;
  139. } u;
  140. } PNP_NOTIFY_ENTRY, *PPNP_NOTIFY_ENTRY;
  141. //
  142. // Deferred operation list structure.
  143. //
  144. typedef struct _PNP_DEFERRED_LIST {
  145. PVOID Next;
  146. handle_t hBinding;
  147. PPNP_NOTIFY_ENTRY Entry;
  148. } PNP_DEFERRED_LIST, *PPNP_DEFERRED_LIST;
  149. //
  150. // Signatures describing which notification list an entry currently belongs to.
  151. //
  152. #define CLASS_ENTRY_SIGNATURE (0x07625100)
  153. #define TARGET_ENTRY_SIGNATURE (0x17625100)
  154. #define SERVICE_ENTRY_SIGNATURE (0x37625100)
  155. #define LIST_ENTRY_SIGNATURE_MASK (0xFFFFFF00)
  156. #define LIST_ENTRY_INDEX_MASK (~LIST_ENTRY_SIGNATURE_MASK)
  157. #define MarkEntryWithList(ent,value) { ent->Signature &= LIST_ENTRY_SIGNATURE_MASK;\
  158. ent->Signature |= value; }
  159. //
  160. // Device event notification lists.
  161. //
  162. #define TARGET_HASH_BUCKETS 13
  163. #define CLASS_GUID_HASH_BUCKETS 13
  164. #define SERVICE_NUM_CONTROLS 3
  165. #define HashClassGuid(_Guid) \
  166. ( ( ((PULONG)_Guid)[0] + ((PULONG)_Guid)[1] + ((PULONG)_Guid)[2] \
  167. + ((PULONG)_Guid)[3]) % CLASS_GUID_HASH_BUCKETS)
  168. PNP_NOTIFY_LIST TargetList[TARGET_HASH_BUCKETS];
  169. PNP_NOTIFY_LIST ClassList[CLASS_GUID_HASH_BUCKETS];
  170. PNP_NOTIFY_LIST ServiceList[SERVICE_NUM_CONTROLS];
  171. PPNP_DEFERRED_LIST UnregisterList;
  172. PPNP_DEFERRED_LIST RegisterList;
  173. PPNP_DEFERRED_LIST RundownList;
  174. CRITICAL_SECTION RegistrationCS;
  175. //
  176. // These are indices into the global ServiceList array of lists containing
  177. // services registered for the corresponding service control events.
  178. //
  179. enum cBitIndex {
  180. CINDEX_HWPROFILE = 0,
  181. CINDEX_POWEREVENT = 1
  182. };
  183. //
  184. // These are a bit mask for the above lists.
  185. // (the two enums should match! One is 0,1,2,...n. The other 2^n.)
  186. //
  187. enum cBits {
  188. CBIT_HWPROFILE = 1,
  189. CBIT_POWEREVENT = 2
  190. };
  191. //
  192. // Properties describing how a notification entry was freed.
  193. //
  194. // (the entry has been removed from the notification list)
  195. #define DEFER_NOTIFY_FREE 0x80000000
  196. // (used for debugging only)
  197. #define PNP_UNREG_FREE 0x00000100
  198. #define PNP_UNREG_CLASS 0x00000200
  199. #define PNP_UNREG_TARGET 0x00000400
  200. #define PNP_UNREG_DEFER 0x00000800
  201. #define PNP_UNREG_WIN 0x00001000
  202. #define PNP_UNREG_SERVICE 0x00002000
  203. #define PNP_UNREG_CANCEL 0x00004000
  204. #define PNP_UNREG_RUNDOWN 0x00008000
  205. //
  206. // List of devices to be installed.
  207. //
  208. typedef struct _PNP_INSTALL_LIST {
  209. PVOID Next;
  210. LOCKINFO Lock;
  211. } PNP_INSTALL_LIST, *PPNP_INSTALL_LIST;
  212. //
  213. // Device install list entry structure.
  214. //
  215. typedef struct _PNP_INSTALL_ENTRY {
  216. PVOID Next;
  217. DWORD Flags;
  218. WCHAR szDeviceId[MAX_DEVICE_ID_LEN];
  219. } PNP_INSTALL_ENTRY, *PPNP_INSTALL_ENTRY;
  220. //
  221. // Install event list.
  222. //
  223. PNP_INSTALL_LIST InstallList;
  224. //
  225. // Flags for PNP_INSTALL_ENTRY nodes
  226. //
  227. #define PIE_SERVER_SIDE_INSTALL_ATTEMPTED 0x00000001
  228. #define PIE_DEVICE_INSTALL_REQUIRED_REBOOT 0x00000002
  229. //
  230. // Device install client information list structure.
  231. //
  232. typedef struct _INSTALL_CLIENT_LIST {
  233. PVOID Next;
  234. LOCKINFO Lock;
  235. } INSTALL_CLIENT_LIST, *PINSTALL_CLIENT_LIST;
  236. //
  237. // Device install client information list entry structure.
  238. //
  239. typedef struct _INSTALL_CLIENT_ENTRY {
  240. PVOID Next;
  241. ULONG RefCount;
  242. ULONG ulSessionId;
  243. HANDLE hEvent;
  244. HANDLE hPipe;
  245. HANDLE hProcess;
  246. HANDLE hDisconnectEvent;
  247. ULONG ulInstallFlags;
  248. WCHAR LastDeviceId[MAX_DEVICE_ID_LEN];
  249. } INSTALL_CLIENT_ENTRY, *PINSTALL_CLIENT_ENTRY;
  250. //
  251. // Device install client list.
  252. //
  253. INSTALL_CLIENT_LIST InstallClientList;
  254. //
  255. // Global BOOL that tracks if a server side device install reboot is needed.
  256. //
  257. BOOL gServerSideDeviceInstallRebootNeeded = FALSE;
  258. //
  259. // private prototypes
  260. //
  261. DWORD
  262. ThreadProc_DeviceEvent(
  263. LPDWORD lpParam
  264. );
  265. DWORD
  266. ThreadProc_DeviceInstall(
  267. LPDWORD lpParam
  268. );
  269. DWORD
  270. ThreadProc_GuiSetupDeviceInstall(
  271. LPDWORD lpThreadParam
  272. );
  273. DWORD
  274. ThreadProc_FactoryPreinstallDeviceInstall(
  275. LPDWORD lpThreadParam
  276. );
  277. DWORD
  278. ThreadProc_ReenumerateDeviceTree(
  279. LPVOID lpThreadParam
  280. );
  281. BOOL
  282. InstallDevice(
  283. IN LPWSTR pszDeviceId,
  284. IN OUT PULONG SessionId,
  285. IN ULONG Flags
  286. );
  287. DWORD
  288. InstallDeviceServerSide(
  289. IN LPWSTR pszDeviceId,
  290. IN OUT PBOOL RebootRequired,
  291. IN OUT PBOOL DeviceHasProblem,
  292. IN OUT PULONG SessionId,
  293. IN ULONG Flags
  294. );
  295. BOOL
  296. CreateDeviceInstallClient(
  297. IN ULONG SessionId,
  298. OUT PINSTALL_CLIENT_ENTRY *DeviceInstallClient
  299. );
  300. BOOL
  301. ConnectDeviceInstallClient(
  302. IN ULONG SessionId,
  303. OUT PINSTALL_CLIENT_ENTRY *DeviceInstallClient
  304. );
  305. BOOL
  306. DisconnectDeviceInstallClient(
  307. IN PINSTALL_CLIENT_ENTRY DeviceInstallClient
  308. );
  309. PINSTALL_CLIENT_ENTRY
  310. LocateDeviceInstallClient(
  311. IN ULONG SessionId
  312. );
  313. VOID
  314. ReferenceDeviceInstallClient(
  315. IN PINSTALL_CLIENT_ENTRY DeviceInstallClient
  316. );
  317. VOID
  318. DereferenceDeviceInstallClient(
  319. IN PINSTALL_CLIENT_ENTRY DeviceInstallClient
  320. );
  321. BOOL
  322. DoDeviceInstallClient(
  323. IN LPWSTR DeviceId,
  324. IN PULONG SessionId,
  325. IN ULONG Flags,
  326. OUT PINSTALL_CLIENT_ENTRY *DeviceInstallClient
  327. );
  328. BOOL
  329. InitNotification(
  330. VOID
  331. );
  332. VOID
  333. TermNotification(
  334. VOID
  335. );
  336. ULONG
  337. ProcessDeviceEvent(
  338. IN PPLUGPLAY_EVENT_BLOCK EventBlock,
  339. IN DWORD EventBufferSize,
  340. OUT PPNP_VETO_TYPE VetoType,
  341. OUT LPWSTR VetoName,
  342. IN OUT PULONG VetoNameLength
  343. );
  344. ULONG
  345. NotifyInterfaceClassChange(
  346. IN DWORD ServiceControl,
  347. IN DWORD EventId,
  348. IN DWORD Flags,
  349. IN PDEV_BROADCAST_DEVICEINTERFACE ClassData
  350. );
  351. ULONG
  352. NotifyTargetDeviceChange(
  353. IN DWORD ServiceControl,
  354. IN DWORD EventId,
  355. IN DWORD Flags,
  356. IN PDEV_BROADCAST_HANDLE HandleData,
  357. IN LPWSTR DeviceId,
  358. OUT PPNP_VETO_TYPE VetoType OPTIONAL,
  359. OUT LPWSTR VetoName OPTIONAL,
  360. IN OUT PULONG VetoNameLength OPTIONAL
  361. );
  362. ULONG
  363. NotifyHardwareProfileChange(
  364. IN DWORD ServiceControl,
  365. IN DWORD EventId,
  366. IN DWORD Flags,
  367. OUT PPNP_VETO_TYPE VetoType OPTIONAL,
  368. OUT LPWSTR VetoName OPTIONAL,
  369. IN OUT PULONG VetoNameLength OPTIONAL
  370. );
  371. ULONG
  372. NotifyPower(
  373. IN DWORD ServiceControl,
  374. IN DWORD EventId,
  375. IN DWORD EventData,
  376. IN DWORD Flags,
  377. OUT PPNP_VETO_TYPE VetoType OPTIONAL,
  378. OUT LPWSTR VetoName OPTIONAL,
  379. IN OUT PULONG VetoNameLength OPTIONAL
  380. );
  381. BOOL
  382. SendCancelNotification(
  383. IN PPNP_NOTIFY_ENTRY LastEntry,
  384. IN DWORD ServiceControl,
  385. IN DWORD EventId,
  386. IN ULONG Flags,
  387. IN PDEV_BROADCAST_HDR NotifyData OPTIONAL,
  388. IN LPWSTR DeviceId OPTIONAL
  389. );
  390. VOID
  391. BroadcastCompatibleDeviceMsg(
  392. IN DWORD EventId,
  393. IN PDEV_BROADCAST_DEVICEINTERFACE ClassData,
  394. IN PDWORD CurrentMask
  395. );
  396. VOID
  397. BroadcastVolumeNameChange(
  398. VOID
  399. );
  400. DWORD
  401. GetAllVolumeMountPoints(
  402. VOID
  403. );
  404. BOOL
  405. EventIdFromEventGuid(
  406. IN CONST GUID *EventGuid,
  407. OUT LPDWORD EventId,
  408. OUT LPDWORD Flags,
  409. OUT LPDWORD ServiceControl
  410. );
  411. ULONG
  412. SendHotplugNotification(
  413. IN CONST GUID *EventGuid,
  414. IN PPNP_VETO_TYPE VetoType OPTIONAL,
  415. IN LPWSTR MultiSzList,
  416. IN OUT PULONG SessionId,
  417. IN ULONG Flags
  418. );
  419. VOID
  420. LogErrorEvent(
  421. DWORD dwEventID,
  422. DWORD dwError,
  423. WORD nStrings,
  424. ...
  425. );
  426. VOID
  427. LogWarningEvent(
  428. DWORD dwEventID,
  429. WORD nStrings,
  430. ...
  431. );
  432. BOOL
  433. LockNotifyList(
  434. IN LOCKINFO *Lock
  435. );
  436. VOID
  437. UnlockNotifyList(
  438. IN LOCKINFO *Lock
  439. );
  440. PPNP_NOTIFY_LIST
  441. GetNotifyListForEntry(
  442. IN PPNP_NOTIFY_ENTRY entry
  443. );
  444. BOOL
  445. DeleteNotifyEntry(
  446. IN PPNP_NOTIFY_ENTRY Entry,
  447. IN BOOLEAN RpcNotified
  448. );
  449. VOID
  450. AddNotifyEntry(
  451. IN PPNP_NOTIFY_LIST NotifyList,
  452. IN PPNP_NOTIFY_ENTRY NewEntry
  453. );
  454. ULONG
  455. HashString(
  456. IN LPWSTR String,
  457. IN ULONG Buckets
  458. );
  459. DWORD
  460. MapQueryEventToCancelEvent(
  461. IN DWORD QueryEventId
  462. );
  463. VOID
  464. FixUpDeviceId(
  465. IN OUT LPWSTR DeviceId
  466. );
  467. ULONG
  468. MapSCMControlsToControlBit(
  469. IN ULONG scmControls
  470. );
  471. DWORD
  472. GetFirstPass(
  473. IN BOOL bQuery
  474. );
  475. DWORD
  476. GetNextPass(
  477. IN DWORD curPass,
  478. IN BOOL bQuery
  479. );
  480. BOOL
  481. NotifyEntryThisPass(
  482. IN PPNP_NOTIFY_ENTRY Entry,
  483. IN DWORD Pass
  484. );
  485. DWORD
  486. GetPassFromEntry(
  487. IN PPNP_NOTIFY_ENTRY Entry
  488. );
  489. BOOL
  490. GetClientName(
  491. IN PPNP_NOTIFY_ENTRY entry,
  492. OUT LPWSTR lpszClientName,
  493. IN OUT PULONG pulClientNameLength
  494. );
  495. BOOL
  496. GetWindowsExeFileName(
  497. IN HWND hWnd,
  498. OUT LPWSTR lpszFileName,
  499. IN OUT PULONG pulFileNameLength
  500. );
  501. PPNP_NOTIFY_ENTRY
  502. GetNextNotifyEntry(
  503. IN PPNP_NOTIFY_ENTRY Entry,
  504. IN DWORD Flags
  505. );
  506. PPNP_NOTIFY_ENTRY
  507. GetFirstNotifyEntry(
  508. IN PPNP_NOTIFY_LIST List,
  509. IN DWORD Flags
  510. );
  511. BOOL
  512. InitializeHydraInterface(
  513. VOID
  514. );
  515. DWORD
  516. LoadDeviceInstaller(
  517. VOID
  518. );
  519. VOID
  520. UnloadDeviceInstaller(
  521. VOID
  522. );
  523. BOOL
  524. PromptUser(
  525. IN OUT PULONG SessionId,
  526. IN ULONG Flags
  527. );
  528. VOID
  529. DoRunOnce(
  530. VOID
  531. );
  532. BOOL
  533. GetSessionUserToken(
  534. IN ULONG ulSessionId,
  535. OUT LPHANDLE lphUserToken
  536. );
  537. BOOL
  538. IsUserLoggedOnSession(
  539. IN ULONG ulSessionId
  540. );
  541. BOOL
  542. IsSessionConnected(
  543. IN ULONG ulSessionId
  544. );
  545. BOOL
  546. IsSessionLocked(
  547. IN ULONG ulSessionId
  548. );
  549. BOOL
  550. IsConsoleSession(
  551. IN ULONG ulSessionId
  552. );
  553. DWORD
  554. CreateUserSynchEvent(
  555. IN HANDLE hUserToken,
  556. IN LPCWSTR lpName,
  557. OUT HANDLE *phEvent
  558. );
  559. BOOL
  560. CreateNoPendingInstallEvent(
  561. VOID
  562. );
  563. DWORD
  564. CreateUserReadNamedPipe(
  565. IN HANDLE hUserToken,
  566. IN LPCWSTR lpName,
  567. IN ULONG ulSize,
  568. OUT HANDLE *phPipe
  569. );
  570. ULONG
  571. CheckEjectPermissions(
  572. IN LPWSTR DeviceId,
  573. OUT PPNP_VETO_TYPE VetoType OPTIONAL,
  574. OUT LPWSTR VetoName OPTIONAL,
  575. IN OUT PULONG VetoNameLength OPTIONAL
  576. );
  577. VOID
  578. LogSurpriseRemovalEvent(
  579. IN LPWSTR MultiSzList
  580. );
  581. PWCHAR
  582. BuildFriendlyName(
  583. IN LPWSTR InstancePath
  584. );
  585. CONFIGRET
  586. DevInstNeedsInstall(
  587. IN LPCWSTR DevInst,
  588. IN BOOL CheckReinstallConfigFlag,
  589. OUT BOOL *NeedsInstall
  590. );
  591. PWSTR
  592. BuildBlockedDriverList(
  593. IN OUT LPGUID GuidList,
  594. IN ULONG GuidCount
  595. );
  596. VOID
  597. SendInvalidIDNotifications(
  598. IN ULONG ulSessionId
  599. );
  600. //
  601. // global data
  602. //
  603. extern HANDLE ghInst; // Module handle
  604. extern HKEY ghEnumKey; // Key to HKLM\CCC\System\Enum - DO NOT MODIFY
  605. extern HKEY ghServicesKey; // Key to HKLM\CCC\System\Services - DO NOT MODIFY
  606. extern HKEY ghClassKey; // Key to HKLM\CCC\System\Class - DO NOT MODIFY
  607. extern DWORD CurrentServiceState; // PlugPlay service state - DO NOT MODIFY
  608. extern PSVCS_GLOBAL_DATA PnPGlobalData; // SCM global data
  609. HANDLE ghInitMutex = NULL;
  610. HANDLE ghUserToken = NULL;
  611. LOCKINFO gTokenLock;
  612. BOOL gbMainSessionLocked = FALSE;
  613. ULONG gNotificationInProg = 0; // 0 -> No notification or unregister underway.
  614. DWORD gAllDrivesMask = 0; // bitmask of all physical volume mountpoints.
  615. BOOL gbSuppressUI = FALSE; // TRUE if PNP should never display UI (newdev, hotplug).
  616. BOOL gbOobeInProgress = FALSE;// TRUE if the OOBE is running during this boot.
  617. BOOL gbPreservePreInstall = FALSE; // TRUE if PNP should respect a device's pre-install settings.
  618. BOOL gbStatelessBoot = FALSE; // TRUE if this is a stateless (rebootless) boot.
  619. const WCHAR RegMemoryManagementKeyName[] =
  620. TEXT("System\\CurrentControlSet\\Control\\Session Manager\\Memory Management");
  621. const WCHAR RegVerifyDriverLevelValueName[] =
  622. TEXT("VerifyDriverLevel");
  623. //
  624. // Device Installer instance handle and necessary entrypoints.
  625. // This data is only referenced by the (non-GUI setup) device install thread
  626. // (ThreadProc_DeviceInstall).
  627. //
  628. typedef HDEVINFO (WINAPI *FP_CREATEDEVICEINFOLIST)(CONST GUID *, HWND);
  629. typedef BOOL (WINAPI *FP_OPENDEVICEINFO)(HDEVINFO, PCWSTR, HWND, DWORD, PSP_DEVINFO_DATA);
  630. typedef BOOL (WINAPI *FP_BUILDDRIVERINFOLIST)(HDEVINFO, PSP_DEVINFO_DATA, DWORD);
  631. typedef BOOL (WINAPI *FP_DESTROYDEVICEINFOLIST)(HDEVINFO);
  632. typedef BOOL (WINAPI *FP_CALLCLASSINSTALLER)(DI_FUNCTION, HDEVINFO, PSP_DEVINFO_DATA);
  633. typedef BOOL (WINAPI *FP_INSTALLCLASS)(HWND, PCWSTR, DWORD, HSPFILEQ);
  634. typedef BOOL (WINAPI *FP_GETSELECTEDDRIVER)(HDEVINFO, PSP_DEVINFO_DATA, PSP_DRVINFO_DATA);
  635. typedef BOOL (WINAPI *FP_GETDRIVERINFODETAIL)(HDEVINFO, PSP_DEVINFO_DATA, PSP_DRVINFO_DATA, PSP_DRVINFO_DETAIL_DATA, DWORD, PDWORD);
  636. typedef BOOL (WINAPI *FP_GETDEVICEINSTALLPARAMS)(HDEVINFO, PSP_DEVINFO_DATA, PSP_DEVINSTALL_PARAMS);
  637. typedef BOOL (WINAPI *FP_SETDEVICEINSTALLPARAMS)(HDEVINFO, PSP_DEVINFO_DATA, PSP_DEVINSTALL_PARAMS);
  638. typedef BOOL (WINAPI *FP_GETDRIVERINSTALLPARAMS)(HDEVINFO, PSP_DEVINFO_DATA, PSP_DRVINFO_DATA, PSP_DRVINSTALL_PARAMS);
  639. typedef BOOL (WINAPI *FP_SETCLASSINSTALLPARAMS)(HDEVINFO, PSP_DEVINFO_DATA, PSP_CLASSINSTALL_HEADER, DWORD);
  640. typedef BOOL (WINAPI *FP_GETCLASSINSTALLPARAMS)(HDEVINFO, PSP_DEVINFO_DATA, PSP_CLASSINSTALL_HEADER, DWORD, PDWORD);
  641. typedef HINF (WINAPI *FP_OPENINFFILE)(PCWSTR, PCWSTR, DWORD, PUINT);
  642. typedef VOID (WINAPI *FP_CLOSEINFFILE)(HINF);
  643. typedef BOOL (WINAPI *FP_FINDFIRSTLINE)(HINF, PCWSTR, PCWSTR, PINFCONTEXT);
  644. typedef BOOL (WINAPI *FP_FINDNEXTMATCHLINE)(PINFCONTEXT, PCWSTR, PINFCONTEXT);
  645. typedef BOOL (WINAPI *FP_GETSTRINGFIELD)(PINFCONTEXT, DWORD, PWSTR, DWORD, PDWORD);
  646. typedef VOID (*FP_SETGLOBALFLAGS)(DWORD);
  647. typedef DWORD (*FP_GETGLOBALFLAGS)(VOID);
  648. typedef PPSP_RUNONCE_NODE (*FP_ACCESSRUNONCENODELIST)(VOID);
  649. typedef VOID (*FP_DESTROYRUNONCENODELIST)(VOID);
  650. HINSTANCE ghDeviceInstallerLib = NULL;
  651. FP_CREATEDEVICEINFOLIST fpCreateDeviceInfoList;
  652. FP_OPENDEVICEINFO fpOpenDeviceInfo;
  653. FP_BUILDDRIVERINFOLIST fpBuildDriverInfoList;
  654. FP_DESTROYDEVICEINFOLIST fpDestroyDeviceInfoList;
  655. FP_CALLCLASSINSTALLER fpCallClassInstaller;
  656. FP_INSTALLCLASS fpInstallClass;
  657. FP_GETSELECTEDDRIVER fpGetSelectedDriver;
  658. FP_GETDRIVERINFODETAIL fpGetDriverInfoDetail;
  659. FP_GETDEVICEINSTALLPARAMS fpGetDeviceInstallParams;
  660. FP_SETDEVICEINSTALLPARAMS fpSetDeviceInstallParams;
  661. FP_GETDRIVERINSTALLPARAMS fpGetDriverInstallParams;
  662. FP_SETCLASSINSTALLPARAMS fpSetClassInstallParams;
  663. FP_GETCLASSINSTALLPARAMS fpGetClassInstallParams;
  664. FP_OPENINFFILE fpOpenInfFile;
  665. FP_CLOSEINFFILE fpCloseInfFile;
  666. FP_FINDFIRSTLINE fpFindFirstLine;
  667. FP_FINDNEXTMATCHLINE fpFindNextMatchLine;
  668. FP_GETSTRINGFIELD fpGetStringField;
  669. FP_SETGLOBALFLAGS fpSetGlobalFlags;
  670. FP_GETGLOBALFLAGS fpGetGlobalFlags;
  671. FP_ACCESSRUNONCENODELIST fpAccessRunOnceNodeList;
  672. FP_DESTROYRUNONCENODELIST fpDestroyRunOnceNodeList;
  673. //
  674. // typdef for comctl32's DestroyPropertySheetPage API, needed in cases where
  675. // class-/co-installers supply wizard pages (that need to be destroyed).
  676. //
  677. typedef BOOL (WINAPI *FP_DESTROYPROPERTYSHEETPAGE)(HPROPSHEETPAGE);
  678. //
  679. // typedefs for ANSI and Unicode variants of rundll32 proc entrypoint.
  680. //
  681. typedef void (WINAPI *RUNDLLPROCA)(HWND hwndStub, HINSTANCE hInstance, LPSTR pszCmdLine, int nCmdShow);
  682. typedef void (WINAPI *RUNDLLPROCW)(HWND hwndStub, HINSTANCE hInstance, LPWSTR pszCmdLine, int nCmdShow);
  683. //
  684. // typedefs for Terminal Services message dispatch routines, in winsta.dll.
  685. //
  686. typedef LONG (*FP_WINSTABROADCASTSYSTEMMESSAGE)(
  687. HANDLE hServer,
  688. BOOL sendToAllWinstations,
  689. ULONG sessionID,
  690. ULONG timeOut,
  691. DWORD dwFlags,
  692. DWORD *lpdwRecipients,
  693. ULONG uiMessage,
  694. WPARAM wParam,
  695. LPARAM lParam,
  696. LONG *pResponse
  697. );
  698. typedef LONG (*FP_WINSTASENDWINDOWMESSAGE)(
  699. HANDLE hServer,
  700. ULONG sessionID,
  701. ULONG timeOut,
  702. ULONG hWnd,
  703. ULONG Msg,
  704. WPARAM wParam,
  705. LPARAM lParam,
  706. LONG *pResponse
  707. );
  708. typedef BOOLEAN (WINAPI * FP_WINSTAQUERYINFORMATIONW)(
  709. HANDLE hServer,
  710. ULONG LogonId,
  711. WINSTATIONINFOCLASS WinStationInformationClass,
  712. PVOID pWinStationInformation,
  713. ULONG WinStationInformationLength,
  714. PULONG pReturnLength
  715. );
  716. HINSTANCE ghWinStaLib = NULL;
  717. FP_WINSTASENDWINDOWMESSAGE fpWinStationSendWindowMessage = NULL;
  718. FP_WINSTABROADCASTSYSTEMMESSAGE fpWinStationBroadcastSystemMessage = NULL;
  719. FP_WINSTAQUERYINFORMATIONW fpWinStationQueryInformationW = NULL;
  720. //
  721. // typedefs for Terminal Services support routines, in wtsapi32.dll.
  722. //
  723. typedef BOOL (*FP_WTSQUERYSESSIONINFORMATION)(
  724. IN HANDLE hServer,
  725. IN DWORD SessionId,
  726. IN WTS_INFO_CLASS WTSInfoClass,
  727. OUT LPWSTR * ppBuffer,
  728. OUT DWORD * pBytesReturned
  729. );
  730. typedef VOID (*FP_WTSFREEMEMORY)(
  731. IN PVOID pMemory
  732. );
  733. HINSTANCE ghWtsApi32Lib = NULL;
  734. FP_WTSQUERYSESSIONINFORMATION fpWTSQuerySessionInformation = NULL;
  735. FP_WTSFREEMEMORY fpWTSFreeMemory = NULL;
  736. //
  737. // Service controller callback routines for authentication and notification to
  738. // services.
  739. //
  740. PSCMCALLBACK_ROUTINE pServiceControlCallback;
  741. PSCMAUTHENTICATION_CALLBACK pSCMAuthenticate;
  742. //
  743. // Device install events
  744. //
  745. #define NUM_INSTALL_EVENTS 2
  746. #define LOGGED_ON_EVENT 0
  747. #define NEEDS_INSTALL_EVENT 1
  748. HANDLE InstallEvents[NUM_INSTALL_EVENTS] = {NULL, NULL};
  749. HANDLE ghNoPendingInstalls = NULL;
  750. //
  751. // Veto definitions
  752. //
  753. #define UnknownVeto(t,n,l) { *(t) = PNP_VetoTypeUnknown; }
  754. #define WinBroadcastVeto(h,t,n,l) { *(t) = PNP_VetoWindowsApp;\
  755. GetWindowsExeFileName(h,n,l); }
  756. #define WindowVeto(e,t,n,l) { *(t) = PNP_VetoWindowsApp;\
  757. GetClientName(e,n,l); }
  758. #define ServiceVeto(e,t,n,l) { *(t) = PNP_VetoWindowsService;\
  759. GetClientName(e,n,l); }
  760. //
  761. // Sentinel for event loop control
  762. //
  763. #define PASS_COMPLETE 0x7fffffff
  764. BOOL
  765. PnpConsoleCtrlHandler(
  766. DWORD dwCtrlType
  767. )
  768. /*++
  769. Routine Description:
  770. This routine handles control signals received by the process for the
  771. session the process is associated with.
  772. Arguments:
  773. dwCtrlType - Indicates the type of control signal received by the handler.
  774. This value is one of the following: CTRL_C_EVENT, CTRL_BREAK_EVENT,
  775. CTRL_CLOSE_EVENT, CTRL_LOGOFF_EVENT, CTRL_SHUTDOWN_EVENT
  776. Return Value:
  777. If the function handles the control signal, it should return TRUE. If it
  778. returns FALSE, the next handler function in the list of handlers for this
  779. process is used.
  780. --*/
  781. {
  782. PINSTALL_CLIENT_ENTRY pDeviceInstallClient;
  783. switch (dwCtrlType) {
  784. case CTRL_LOGOFF_EVENT:
  785. //
  786. // The system sends the logoff event to the registered console ctrl
  787. // handlers for a console process when a user is logging off from the
  788. // session associated with that process. Since UMPNPMGR runs within the
  789. // context of the services.exe process, which always resides in session
  790. // 0, that is the only session for which this handler will receive
  791. // logoff events.
  792. //
  793. KdPrintEx((DPFLTR_PNPMGR_ID,
  794. DBGF_EVENT,
  795. "UMPNPMGR: PnpConsoleCtrlHandler: CTRL_LOGOFF_EVENT: Session %d\n",
  796. MAIN_SESSION));
  797. //
  798. // Close the handle to the user access token for the main session.
  799. //
  800. ASSERT(gTokenLock.LockHandles);
  801. LockPrivateResource(&gTokenLock);
  802. if (ghUserToken) {
  803. CloseHandle(ghUserToken);
  804. ghUserToken = NULL;
  805. }
  806. UnlockPrivateResource(&gTokenLock);
  807. //
  808. // If the main session was the active Console session, (or should be
  809. // treated as the active console session because Fast User Switching is
  810. // disabled) when the user logged off, reset the "logged on" event.
  811. //
  812. if (IsConsoleSession(MAIN_SESSION)) {
  813. if (InstallEvents[LOGGED_ON_EVENT]) {
  814. ResetEvent(InstallEvents[LOGGED_ON_EVENT]);
  815. KdPrintEx((DPFLTR_PNPMGR_ID,
  816. DBGF_INSTALL | DBGF_EVENT,
  817. "UMPNPMGR: PnpConsoleCtrlHandler: CTRL_LOGOFF_EVENT: ResetEvent LOGGED_ON_EVENT\n"));
  818. }
  819. }
  820. //
  821. // If we currently have a device install UI client on this session,
  822. // we should attempt to close it now, before logging off.
  823. //
  824. LockNotifyList(&InstallClientList.Lock);
  825. pDeviceInstallClient = LocateDeviceInstallClient(MAIN_SESSION);
  826. if (pDeviceInstallClient) {
  827. DereferenceDeviceInstallClient(pDeviceInstallClient);
  828. }
  829. UnlockNotifyList(&InstallClientList.Lock);
  830. break;
  831. default:
  832. //
  833. // No special processing for any other events.
  834. //
  835. break;
  836. }
  837. //
  838. // Returning FALSE passes this control to the next registered CtrlHandler in
  839. // the list of handlers for this process (services.exe), so that other
  840. // services will get a chance to look at this.
  841. //
  842. return FALSE;
  843. } // PnpConsoleCtrlHandler
  844. DWORD
  845. InitializePnPManager(
  846. LPDWORD lpParam
  847. )
  848. /*++
  849. Routine Description:
  850. This thread routine is created from srventry.c when services.exe
  851. attempts to start the plug and play service. The init routine in
  852. srventry.c does critical initialize then creates this thread to
  853. do pnp initialization so that it can return back to the service
  854. controller before pnp init completes.
  855. Arguments:
  856. lpParam - Not used.
  857. Return Value:
  858. Currently returns TRUE/FALSE.
  859. --*/
  860. {
  861. DWORD dwStatus = TRUE;
  862. DWORD ThreadID = 0;
  863. HANDLE hThread = NULL, hEventThread = NULL;
  864. HKEY hKey = NULL;
  865. LONG status;
  866. BOOL bGuiModeSetup = FALSE, bFactoryPreInstall = FALSE;
  867. ULONG ulSize, ulValue;
  868. UNREFERENCED_PARAMETER(lpParam);
  869. KdPrintEx((DPFLTR_PNPMGR_ID,
  870. DBGF_EVENT,
  871. "UMPNPMGR: InitializePnPManager\n"));
  872. //
  873. // Initialize events that will control when to install devices later.
  874. //
  875. InstallEvents[LOGGED_ON_EVENT] = CreateEvent(NULL, TRUE, FALSE, NULL);
  876. if (InstallEvents[LOGGED_ON_EVENT] == NULL) {
  877. LogErrorEvent(ERR_CREATING_LOGON_EVENT, GetLastError(), 0);
  878. }
  879. InstallEvents[NEEDS_INSTALL_EVENT] = CreateEvent(NULL, TRUE, FALSE, NULL);
  880. if (InstallEvents[NEEDS_INSTALL_EVENT] == NULL) {
  881. LogErrorEvent(ERR_CREATING_INSTALL_EVENT, GetLastError(), 0);
  882. }
  883. //
  884. // Create the pending install event.
  885. //
  886. if (!CreateNoPendingInstallEvent()) {
  887. LogErrorEvent(ERR_CREATING_PENDING_INSTALL_EVENT, GetLastError(), 0);
  888. }
  889. ASSERT(ghNoPendingInstalls != NULL);
  890. //
  891. // Initialize event to control access to the current session during session
  892. // change events. The event state is initially signalled since this service
  893. // initializes when only session 0 exists (prior to the initialization of
  894. // termsrv, or the creation of any other sessions).
  895. //
  896. ghActiveConsoleSessionEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
  897. if (ghActiveConsoleSessionEvent == NULL) {
  898. KdPrintEx((DPFLTR_PNPMGR_ID,
  899. DBGF_INSTALL | DBGF_ERRORS,
  900. "UMPNPMGR: Failed to initialize ghActiveConsoleSessionEvent!!, error = %d\n",
  901. GetLastError()));
  902. }
  903. //
  904. // Setup a console control handler so that I can keep track of logoffs to
  905. // the main session (SessionId 0). This is still necessary because Terminal
  906. // Services may not always be available. (see PNP_ReportLogOn).
  907. // (I only get logoff notification via this handler so I still
  908. // rely on the kludge in userinit.exe to tell me about logons).
  909. //
  910. if (!SetConsoleCtrlHandler(PnpConsoleCtrlHandler, TRUE)) {
  911. KdPrintEx((DPFLTR_PNPMGR_ID,
  912. DBGF_EVENT | DBGF_ERRORS,
  913. "UMPNPMGR: SetConsoleCtrlHandler failed, error = %d\n",
  914. GetLastError()));
  915. }
  916. //
  917. // Acquire a mutex now to make sure I get through this
  918. // initialization task before getting pinged by a logon
  919. //
  920. ghInitMutex = CreateMutex(NULL, TRUE, NULL);
  921. ASSERT(ghInitMutex != NULL);
  922. if (ghInitMutex == NULL) {
  923. return FALSE;
  924. }
  925. try {
  926. //
  927. // Check if we're running during one of the assorted flavors of setup.
  928. //
  929. status = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  930. TEXT("SYSTEM\\Setup"),
  931. 0,
  932. KEY_READ,
  933. &hKey);
  934. if (status == ERROR_SUCCESS) {
  935. //
  936. // Determine if factory pre-install is in progress.
  937. //
  938. ulValue = 0;
  939. ulSize = sizeof(ulValue);
  940. status = RegQueryValueEx(hKey,
  941. TEXT("FactoryPreInstallInProgress"),
  942. NULL,
  943. NULL,
  944. (LPBYTE)&ulValue,
  945. &ulSize);
  946. if ((status == ERROR_SUCCESS) && (ulValue == 1)) {
  947. bFactoryPreInstall = TRUE;
  948. gbSuppressUI = TRUE;
  949. KdPrintEx((DPFLTR_PNPMGR_ID,
  950. DBGF_INSTALL | DBGF_EVENT,
  951. "UMPNPMGR: Will suppress all UI in Factory Mode\n"));
  952. LogWarningEvent(WRN_FACTORY_UI_SUPPRESSED, 0, NULL);
  953. }
  954. if (!bFactoryPreInstall) {
  955. //
  956. // Determine if Gui Mode Setup is in progress (but not mini-setup).
  957. //
  958. ulValue = 0;
  959. ulSize = sizeof(ulValue);
  960. status = RegQueryValueEx(hKey,
  961. TEXT("SystemSetupInProgress"),
  962. NULL,
  963. NULL,
  964. (LPBYTE)&ulValue,
  965. &ulSize);
  966. if (status == ERROR_SUCCESS) {
  967. bGuiModeSetup = (ulValue == 1);
  968. }
  969. if (bGuiModeSetup) {
  970. //
  971. // Well, we're in GUI-mode setup, but we need to make sure
  972. // we're not in mini-setup, or factory pre-install. We
  973. // treat mini-setup like any other boot of the system, and
  974. // factory pre-install is a delayed version of a normal
  975. // boot.
  976. //
  977. ulValue = 0;
  978. ulSize = sizeof(ulValue);
  979. status = RegQueryValueEx(hKey,
  980. TEXT("MiniSetupInProgress"),
  981. NULL,
  982. NULL,
  983. (LPBYTE)&ulValue,
  984. &ulSize);
  985. if ((status == ERROR_SUCCESS) && (ulValue == 1)) {
  986. //
  987. // Well, we're in mini-setup, but we need to make sure
  988. // that he doesn't want us to do PnP re-enumeration.
  989. //
  990. ulValue = 0;
  991. ulSize = sizeof(ulValue);
  992. status = RegQueryValueEx(hKey,
  993. TEXT("MiniSetupDoPnP"),
  994. NULL,
  995. NULL,
  996. (LPBYTE)&ulValue,
  997. &ulSize);
  998. if ((status != ERROR_SUCCESS) || (ulValue == 0)) {
  999. //
  1000. // Nope. Treat this like any other boot of the
  1001. // system.
  1002. //
  1003. bGuiModeSetup = FALSE;
  1004. }
  1005. }
  1006. }
  1007. }
  1008. //
  1009. // Determine if this is an OOBE boot.
  1010. //
  1011. ulValue = 0;
  1012. ulSize = sizeof(ulValue);
  1013. status = RegQueryValueEx(hKey,
  1014. TEXT("OobeInProgress"),
  1015. NULL,
  1016. NULL,
  1017. (LPBYTE)&ulValue,
  1018. &ulSize);
  1019. if (status == ERROR_SUCCESS) {
  1020. gbOobeInProgress = (ulValue == 1);
  1021. }
  1022. //
  1023. // Close the SYSTEM\Setup key.
  1024. //
  1025. RegCloseKey(hKey);
  1026. hKey = NULL;
  1027. } else {
  1028. KdPrintEx((DPFLTR_PNPMGR_ID,
  1029. DBGF_INSTALL,
  1030. "UMPNPMGR: Failure opening key SYSTEM\\Setup (%d)\n",
  1031. status));
  1032. }
  1033. //
  1034. // Check if we should consider pre-installation of devices complete.
  1035. //
  1036. status = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  1037. TEXT("SYSTEM\\WPA\\PnP"),
  1038. 0,
  1039. KEY_READ,
  1040. &hKey);
  1041. if (status == ERROR_SUCCESS) {
  1042. //
  1043. // Check for the "PreservePreInstall" flag to determine if we should
  1044. // preserve settings on preinstalled devices (i.e. do not reinstall
  1045. // finish-install devices).
  1046. //
  1047. ulValue = 0;
  1048. ulSize = sizeof(ulValue);
  1049. status = RegQueryValueEx(hKey,
  1050. pszRegValuePreservePreInstall,
  1051. NULL,
  1052. NULL,
  1053. (LPBYTE)&ulValue,
  1054. &ulSize);
  1055. if ((status == ERROR_SUCCESS) && (ulValue == 1)) {
  1056. gbPreservePreInstall = TRUE;
  1057. }
  1058. //
  1059. // Check for the "StatelessBoot" flag to determine if this is a
  1060. // stateless boot (no-reboot) boot of the OS.
  1061. //
  1062. ulValue = 0;
  1063. ulSize = sizeof(ulValue);
  1064. status = RegQueryValueEx(hKey,
  1065. TEXT("StatelessBoot"),
  1066. NULL,
  1067. NULL,
  1068. (LPBYTE)&ulValue,
  1069. &ulSize);
  1070. if ((status == ERROR_SUCCESS) && (ulValue == 1)) {
  1071. gbStatelessBoot = TRUE;
  1072. }
  1073. RegCloseKey(hKey);
  1074. hKey = NULL;
  1075. }
  1076. //
  1077. // If this is EmbeddedNT, check whether PNP should display UI.
  1078. // Note that this is only checked once per system boot, when the
  1079. // service is initialized.
  1080. //
  1081. if (IsEmbeddedNT()) {
  1082. if (RegOpenKeyEx(ghServicesKey,
  1083. pszRegKeyPlugPlayServiceParams,
  1084. 0,
  1085. KEY_READ,
  1086. &hKey) == ERROR_SUCCESS) {
  1087. ulValue = 0;
  1088. ulSize = sizeof(ulValue);
  1089. if ((RegQueryValueEx(hKey,
  1090. TEXT("SuppressUI"),
  1091. NULL,
  1092. NULL,
  1093. (LPBYTE)&ulValue,
  1094. &ulSize) == ERROR_SUCCESS) && (ulValue == 1)) {
  1095. gbSuppressUI = TRUE;
  1096. KdPrintEx((DPFLTR_PNPMGR_ID,
  1097. DBGF_INSTALL | DBGF_EVENT,
  1098. "UMPNPMGR: Will suppress all UI on EmbeddedNT\n"));
  1099. LogWarningEvent(WRN_EMBEDDEDNT_UI_SUPPRESSED, 0, NULL);
  1100. }
  1101. RegCloseKey(hKey);
  1102. }
  1103. }
  1104. //
  1105. // Initialize the interfaces to Hydra, if Hydra is running on this system.
  1106. //
  1107. if (IsTerminalServer()) {
  1108. KdPrintEx((DPFLTR_PNPMGR_ID,
  1109. DBGF_EVENT,
  1110. "UMPNPMGR: Initializing interfaces to Terminal Services.\n"));
  1111. if (!InitializeHydraInterface()) {
  1112. KdPrintEx((DPFLTR_PNPMGR_ID,
  1113. DBGF_WARNINGS | DBGF_EVENT,
  1114. "UMPNPMGR: Failed to initialize interfaces to Terminal Services!\n"));
  1115. }
  1116. }
  1117. //
  1118. // Initialize the global drive letter mask
  1119. //
  1120. gAllDrivesMask = GetAllVolumeMountPoints();
  1121. //
  1122. // Create a thread that monitors device events.
  1123. //
  1124. hEventThread = CreateThread(NULL,
  1125. 0,
  1126. (LPTHREAD_START_ROUTINE)ThreadProc_DeviceEvent,
  1127. NULL,
  1128. 0,
  1129. &ThreadID);
  1130. //
  1131. // Create the appropriate thread to handle the device installation.
  1132. // The two cases are when gui mode setup is in progress and for
  1133. // a normal user boot case.
  1134. //
  1135. if (bFactoryPreInstall) {
  1136. //
  1137. // FactoryPreInstallInProgress
  1138. //
  1139. hThread = CreateThread(NULL,
  1140. 0,
  1141. (LPTHREAD_START_ROUTINE)ThreadProc_FactoryPreinstallDeviceInstall,
  1142. NULL,
  1143. 0,
  1144. &ThreadID);
  1145. } else if (bGuiModeSetup) {
  1146. //
  1147. // SystemSetupInProgress,
  1148. // including MiniSetupInProgress with MiniSetupDoPnP
  1149. //
  1150. hThread = CreateThread(NULL,
  1151. 0,
  1152. (LPTHREAD_START_ROUTINE)ThreadProc_GuiSetupDeviceInstall,
  1153. NULL,
  1154. 0,
  1155. &ThreadID);
  1156. } else {
  1157. //
  1158. // Standard system boot, or
  1159. // SystemSetupInProgress with MiniSetupInProgress (but not MiniSetupDoPnP)
  1160. //
  1161. hThread = CreateThread(NULL,
  1162. 0,
  1163. (LPTHREAD_START_ROUTINE)ThreadProc_DeviceInstall,
  1164. NULL,
  1165. 0,
  1166. &ThreadID);
  1167. }
  1168. } except(EXCEPTION_EXECUTE_HANDLER) {
  1169. KdPrintEx((DPFLTR_PNPMGR_ID,
  1170. DBGF_ERRORS | DBGF_EVENT,
  1171. "UMPNPMGR: Exception in InitializePnPManager!\n"));
  1172. ASSERT(0);
  1173. dwStatus = FALSE;
  1174. //
  1175. // Reference the following variables so the compiler will respect
  1176. // statement ordering w.r.t. their assignment.
  1177. //
  1178. hThread = hThread;
  1179. hEventThread = hEventThread;
  1180. }
  1181. //
  1182. // signal the init mutex so that logon init activity can procede
  1183. //
  1184. ReleaseMutex(ghInitMutex);
  1185. if (hThread != NULL) {
  1186. CloseHandle(hThread);
  1187. }
  1188. if (hEventThread != NULL) {
  1189. CloseHandle(hEventThread);
  1190. }
  1191. return dwStatus;
  1192. } // InitializePnPManager
  1193. //------------------------------------------------------------------------
  1194. // Post Log-On routines
  1195. //------------------------------------------------------------------------
  1196. CONFIGRET
  1197. PNP_ReportLogOn(
  1198. IN handle_t hBinding,
  1199. IN BOOL bAdmin,
  1200. IN DWORD ProcessID
  1201. )
  1202. /*++
  1203. Routine Description:
  1204. This routine is used to report logon events. It is called from the
  1205. userinit.exe process during logon, via CMP_Report_LogOn.
  1206. Arguments:
  1207. hBinding - RPC binding handle.
  1208. bAdmin - Not used.
  1209. ProcessID - Process ID of the userinit.exe process that will be used to
  1210. retrieve the access token for the user associated with this
  1211. logon.
  1212. Return Value:
  1213. Return CR_SUCCESS if the function succeeds, CR_FAILURE otherwise.
  1214. Notes:
  1215. When a user logs on to the console session, we signal the "logged on" event,
  1216. which will wake the device installation thread to perform any pending
  1217. client-side device install events.
  1218. Client-side device installation, requires the user access token to create a
  1219. rundll32 process in the logged on user's security context.
  1220. Although Terminal Services is now always running on all flavors of Whistler,
  1221. it is not started during safe mode. It may also not be started by the time
  1222. session 0 is available for logon as the Console session. For those reasons,
  1223. SessionId 0 is still treated differently from the other sessions.
  1224. Since Terminal Services may not be available during a logon to session 0, we
  1225. cache a handle to the access token associated with the userinit.exe process.
  1226. The handle is closed when we receive a logoff event for our process's
  1227. session (SessionId 0), via PnpConsoleCtrlHandler.
  1228. Handles to user access tokens for all other sessions are retrieved on
  1229. demand, using GetWinStationUserToken, since Terminal Services must
  1230. necessarily be available for the creation of those sessions.
  1231. --*/
  1232. {
  1233. CONFIGRET Status = CR_SUCCESS;
  1234. HANDLE hUserProcess = NULL, hUserToken = NULL;
  1235. RPC_STATUS rpcStatus;
  1236. DWORD dwWait;
  1237. ULONG ulSessionId, ulSessionIdCopy;
  1238. PWSTR MultiSzGuidList = NULL;
  1239. UNREFERENCED_PARAMETER(bAdmin);
  1240. //
  1241. // This routine only services requests from local RPC clients.
  1242. //
  1243. if (!IsClientLocal(hBinding)) {
  1244. return CR_ACCESS_DENIED;
  1245. }
  1246. //
  1247. // Wait for the init mutex - this ensures that the pnp init
  1248. // routine (called when the service starts) has had a chance
  1249. // to complete first.
  1250. //
  1251. if (ghInitMutex != NULL) {
  1252. dwWait = WaitForSingleObject(ghInitMutex, 180000); // 3 minutes
  1253. if (dwWait != WAIT_OBJECT_0) {
  1254. //
  1255. // mutex was abandoned or timed out during the wait,
  1256. // don't attempt any further init activity
  1257. //
  1258. return CR_FAILURE;
  1259. }
  1260. }
  1261. try {
  1262. //
  1263. // Make sure that the caller is a member of the interactive group.
  1264. //
  1265. if (!IsClientInteractive(hBinding)) {
  1266. Status = CR_FAILURE;
  1267. goto Clean0;
  1268. }
  1269. //
  1270. // Impersonate the client and retrieve the SessionId.
  1271. //
  1272. rpcStatus = RpcImpersonateClient(hBinding);
  1273. if (rpcStatus != RPC_S_OK) {
  1274. KdPrintEx((DPFLTR_PNPMGR_ID,
  1275. DBGF_ERRORS,
  1276. "UMPNPMGR: PNP_ReportLogOn: RpcImpersonateClient failed, error = %d\n",
  1277. rpcStatus));
  1278. Status = CR_FAILURE;
  1279. goto Clean0;
  1280. }
  1281. //
  1282. // Keep track of the client's session.
  1283. //
  1284. ulSessionId = GetClientLogonId();
  1285. ulSessionIdCopy = ulSessionId;
  1286. KdPrintEx((DPFLTR_PNPMGR_ID,
  1287. DBGF_EVENT,
  1288. "UMPNPMGR: PNP_ReportLogOn: SessionId %d\n",
  1289. ulSessionId));
  1290. //
  1291. // NTRAID #181685-2000/09/11-jamesca:
  1292. //
  1293. // Currently, terminal services send notification of logons to
  1294. // "remote" sessions before the server's process creation thread is
  1295. // running. If we set the logged on event, and there are devices
  1296. // waiting to be installed, we will immediately call
  1297. // CreateProcessAsUser on that session, which will fail. As a
  1298. // (temporary?) workaround, we'll continue to use PNP_ReportLogOn to
  1299. // receive logon notification from userinit.exe, now for all sessions.
  1300. //
  1301. //
  1302. // If this is a logon to SessionId 0, save a handle to the access token
  1303. // associated with the userinit.exe process. We need this later to
  1304. // create a rundll32 process in the logged on user's security context
  1305. // for client-side device installation and hotplug notifications.
  1306. //
  1307. if (ulSessionId == MAIN_SESSION) {
  1308. ASSERT(gTokenLock.LockHandles);
  1309. LockPrivateResource(&gTokenLock);
  1310. //
  1311. // We should have gotten rid of the cached user token during logoff,
  1312. // so if we still have one, ignore this spurious logon report.
  1313. //
  1314. //ASSERT(ghUserToken == NULL);
  1315. if (ghUserToken == NULL) {
  1316. //
  1317. // While still impersonating the client, open a handle to the user
  1318. // access token of the calling process (userinit.exe).
  1319. //
  1320. hUserProcess = OpenProcess(PROCESS_ALL_ACCESS, TRUE, ProcessID);
  1321. if (hUserProcess) {
  1322. if (OpenProcessToken(
  1323. hUserProcess,
  1324. TOKEN_ALL_ACCESS,
  1325. &hUserToken)) {
  1326. ASSERT(hUserToken != NULL);
  1327. //
  1328. // Duplicate the userinit process token so that we have
  1329. // one of our own that we can safely enable/disable
  1330. // privileges for, without affecting that process.
  1331. //
  1332. if (!DuplicateTokenEx(
  1333. hUserToken,
  1334. 0, NULL,
  1335. SecurityImpersonation,
  1336. TokenPrimary,
  1337. &ghUserToken)) {
  1338. KdPrintEx((DPFLTR_PNPMGR_ID,
  1339. DBGF_ERRORS,
  1340. "UMPNPMGR: PNP_ReportLogOn: "
  1341. "DuplicateTokenEx failed, error = %d\n",
  1342. GetLastError()));
  1343. ghUserToken = NULL;
  1344. }
  1345. CloseHandle(hUserToken);
  1346. }
  1347. CloseHandle(hUserProcess);
  1348. } else {
  1349. KdPrintEx((DPFLTR_PNPMGR_ID,
  1350. DBGF_ERRORS,
  1351. "UMPNPMGR: PNP_ReportLogOn: OpenProcess failed, error = %d\n",
  1352. rpcStatus));
  1353. ASSERT(0);
  1354. }
  1355. }
  1356. ASSERT(ghUserToken);
  1357. UnlockPrivateResource(&gTokenLock);
  1358. }
  1359. //
  1360. // Stop impersonating.
  1361. //
  1362. rpcStatus = RpcRevertToSelf();
  1363. if (rpcStatus != RPC_S_OK) {
  1364. KdPrintEx((DPFLTR_PNPMGR_ID,
  1365. DBGF_ERRORS,
  1366. "UMPNPMGR: PNP_ReportLogOn: RpcRevertToSelf failed, error = %d\n",
  1367. rpcStatus));
  1368. ASSERT(rpcStatus == RPC_S_OK);
  1369. }
  1370. //
  1371. // If this is a logon to the "Console" session, signal the event that
  1372. // indicates a Console user is currently logged on.
  1373. //
  1374. if (IsConsoleSession(ulSessionId)) {
  1375. if (InstallEvents[LOGGED_ON_EVENT]) {
  1376. SetEvent(InstallEvents[LOGGED_ON_EVENT]);
  1377. KdPrintEx((DPFLTR_PNPMGR_ID,
  1378. DBGF_EVENT | DBGF_INSTALL,
  1379. "UMPNPMGR: PNP_ReportLogOn: SetEvent LOGGED_ON_EVENT\n"));
  1380. }
  1381. }
  1382. //
  1383. // For every logon to every session, send a generic blocked driver
  1384. // notification if the system has blocked any drivers from loading so
  1385. // far this boot.
  1386. //
  1387. MultiSzGuidList = BuildBlockedDriverList((LPGUID)NULL, 0);
  1388. if (MultiSzGuidList != NULL) {
  1389. SendHotplugNotification((LPGUID)&GUID_DRIVER_BLOCKED,
  1390. NULL,
  1391. MultiSzGuidList,
  1392. &ulSessionId,
  1393. 0);
  1394. HeapFree(ghPnPHeap, 0, MultiSzGuidList);
  1395. MultiSzGuidList = NULL;
  1396. }
  1397. ulSessionId = ulSessionIdCopy;
  1398. //
  1399. // Check if there were any invalid IDs encountered before we started
  1400. // and send notification to user as needed.
  1401. //
  1402. SendInvalidIDNotifications(ulSessionId);
  1403. Clean0:
  1404. NOTHING;
  1405. } except(EXCEPTION_EXECUTE_HANDLER) {
  1406. KdPrintEx((DPFLTR_PNPMGR_ID,
  1407. DBGF_ERRORS,
  1408. "UMPNPMGR: Exception in PNP_ReportLogOn\n"));
  1409. ASSERT(0);
  1410. Status = CR_FAILURE;
  1411. //
  1412. // Reference the following variable so the compiler will respect
  1413. // statement ordering w.r.t. its assignment.
  1414. //
  1415. ghInitMutex = ghInitMutex;
  1416. MultiSzGuidList = MultiSzGuidList;
  1417. }
  1418. if (ghInitMutex != NULL) {
  1419. ReleaseMutex(ghInitMutex);
  1420. }
  1421. if (MultiSzGuidList != NULL) {
  1422. HeapFree(ghPnPHeap, 0, MultiSzGuidList);
  1423. }
  1424. return Status;
  1425. } // PNP_ReportLogon
  1426. typedef struct _DEVICE_INSTALL_ENTRY {
  1427. SIZE_T Index;
  1428. ULONG Depth;
  1429. }DEVICE_INSTALL_ENTRY, *PDEVICE_INSTALL_ENTRY;
  1430. int
  1431. __cdecl
  1432. compare_depth(
  1433. const void *a,
  1434. const void *b
  1435. )
  1436. {
  1437. PDEVICE_INSTALL_ENTRY entry1, entry2;
  1438. entry1 = (PDEVICE_INSTALL_ENTRY)a;
  1439. entry2 = (PDEVICE_INSTALL_ENTRY)b;
  1440. if (entry1->Depth > entry2->Depth) {
  1441. return -1;
  1442. } else if (entry1->Depth < entry2->Depth) {
  1443. return 1;
  1444. }
  1445. return 0;
  1446. }
  1447. DWORD
  1448. ThreadProc_GuiSetupDeviceInstall(
  1449. LPDWORD lpThreadParam
  1450. )
  1451. /*++
  1452. Routine Description:
  1453. This routine is a thread procedure. This thread is only active during GUI-mode setup
  1454. and passes device notifications down a pipe to setup.
  1455. There are two passes, which *must* match exactly with the two passes in GUI-setup
  1456. Once the passes are complete, we proceed to Phase-2 of normal serverside install
  1457. Arguments:
  1458. lpThreadParam - Not used.
  1459. Return Value:
  1460. Not used, currently returns result of ThreadProc_DeviceInstall - which will normally not return
  1461. --*/
  1462. {
  1463. CONFIGRET Status = CR_SUCCESS;
  1464. LPWSTR pDeviceList = NULL, pszDevice = NULL;
  1465. ULONG ulSize = 0, ulConfig, Pass, threadID;
  1466. ULONG ulPostSetupSkipPhase1 = TRUE;
  1467. HANDLE hPipeEvent = NULL, hPipe = NULL, hBatchEvent = NULL, hThread = NULL;
  1468. PPNP_INSTALL_ENTRY entry = NULL;
  1469. PDEVICE_INSTALL_ENTRY pSortArray = NULL;
  1470. LONG lCount;
  1471. BOOL needsInstall;
  1472. ULONG ulReenumerationCount;
  1473. UNREFERENCED_PARAMETER(lpThreadParam);
  1474. try {
  1475. //
  1476. // 2 Passes, must match up with the 2 passes in SysSetup.
  1477. // generally, most, if not all devices, will be picked up
  1478. // and installed by syssetup
  1479. //
  1480. for (Pass = 1; Pass <= 2; Pass++) {
  1481. ulReenumerationCount = 0;
  1482. //
  1483. // If Gui mode setup is in progress, we don't need to wait for a logon
  1484. // event. Just wait on the event that indicates when gui mode setup
  1485. // has opened the pipe and is ready to recieve device names. Attempt to
  1486. // create the event first (in case I beat setup to it), if it exists
  1487. // already then just open it by name. This is a manual reset event.
  1488. //
  1489. hPipeEvent = CreateEvent(NULL, TRUE, FALSE, PNP_CREATE_PIPE_EVENT);
  1490. if (!hPipeEvent) {
  1491. if (GetLastError() == ERROR_ALREADY_EXISTS) {
  1492. hPipeEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, PNP_CREATE_PIPE_EVENT);
  1493. if (!hPipeEvent) {
  1494. Status = CR_FAILURE;
  1495. goto Clean0;
  1496. }
  1497. } else {
  1498. Status = CR_FAILURE;
  1499. goto Clean0;
  1500. }
  1501. }
  1502. if (WaitForSingleObject(hPipeEvent, INFINITE) != WAIT_OBJECT_0) {
  1503. Status = CR_FAILURE;
  1504. goto Clean0; // event must have been abandoned
  1505. }
  1506. //
  1507. // Reset the manual-reset event back to the non-signalled state.
  1508. //
  1509. ResetEvent(hPipeEvent);
  1510. hBatchEvent = CreateEvent(NULL, TRUE, FALSE, PNP_BATCH_PROCESSED_EVENT);
  1511. if (!hBatchEvent) {
  1512. if (GetLastError() == ERROR_ALREADY_EXISTS) {
  1513. hBatchEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, PNP_BATCH_PROCESSED_EVENT);
  1514. if (!hBatchEvent) {
  1515. Status = CR_FAILURE;
  1516. goto Clean0;
  1517. }
  1518. } else {
  1519. Status = CR_FAILURE;
  1520. goto Clean0;
  1521. }
  1522. }
  1523. //
  1524. // Open the client side of the named pipe, the server side was opened
  1525. // by gui mode setup.
  1526. //
  1527. if (!WaitNamedPipe(PNP_NEW_HW_PIPE, PNP_PIPE_TIMEOUT)) {
  1528. KdPrintEx((DPFLTR_PNPMGR_ID,
  1529. DBGF_INSTALL | DBGF_ERRORS,
  1530. "UMPNPMGR: ThreadProc_GuiSetupDeviceInstall: WaitNamedPipe failed!\n"));
  1531. Status = CR_FAILURE;
  1532. goto Clean0;
  1533. }
  1534. hPipe = CreateFile(PNP_NEW_HW_PIPE,
  1535. GENERIC_WRITE,
  1536. 0,
  1537. NULL,
  1538. OPEN_EXISTING,
  1539. FILE_ATTRIBUTE_NORMAL,
  1540. NULL);
  1541. if (hPipe == INVALID_HANDLE_VALUE) {
  1542. LogErrorEvent(ERR_CREATING_SETUP_PIPE, GetLastError(), 0);
  1543. Status = CR_FAILURE;
  1544. goto Clean0;
  1545. }
  1546. //
  1547. // Retreive the list of all devices for all enumerators
  1548. // Start out with a reasonably-sized buffer (16K characters) in
  1549. // hopes of avoiding 2 calls to get the device list.
  1550. //
  1551. ulSize = 16384;
  1552. for ( ; ; ) {
  1553. pDeviceList = HeapAlloc(ghPnPHeap, 0, ulSize * sizeof(WCHAR));
  1554. if (pDeviceList == NULL) {
  1555. Status = CR_OUT_OF_MEMORY;
  1556. goto Clean0;
  1557. }
  1558. Status = PNP_GetDeviceList(NULL, NULL, pDeviceList, &ulSize, 0);
  1559. if (Status == CR_SUCCESS) {
  1560. break;
  1561. } else if(Status == CR_BUFFER_SMALL) {
  1562. //
  1563. // Our initial buffer wasn't large enough. Free the current
  1564. // buffer.
  1565. //
  1566. HeapFree(ghPnPHeap, 0, pDeviceList);
  1567. pDeviceList = NULL;
  1568. //
  1569. // Now, go ahead and make the call to retrieve the actual
  1570. // size required.
  1571. //
  1572. Status = PNP_GetDeviceListSize(NULL, NULL, &ulSize, 0);
  1573. if (Status != CR_SUCCESS) {
  1574. goto Clean0;
  1575. }
  1576. } else {
  1577. //
  1578. // We failed for some reason other than buffer-too-small.
  1579. // Bail now.
  1580. //
  1581. goto Clean0;
  1582. }
  1583. }
  1584. //
  1585. // Count the number of devices we are installing.
  1586. //
  1587. for (pszDevice = pDeviceList, lCount = 0;
  1588. *pszDevice;
  1589. pszDevice += lstrlen(pszDevice) + 1, lCount++) {
  1590. }
  1591. pSortArray = HeapAlloc(ghPnPHeap, 0, lCount * sizeof(DEVICE_INSTALL_ENTRY));
  1592. if (pSortArray) {
  1593. NTSTATUS ntStatus;
  1594. PLUGPLAY_CONTROL_DEPTH_DATA depthData;
  1595. LPWSTR pTempList;
  1596. HRESULT hr;
  1597. //
  1598. // Initialize all the information we need to sort devices.
  1599. //
  1600. for (pszDevice = pDeviceList, lCount = 0;
  1601. *pszDevice;
  1602. pszDevice += lstrlen(pszDevice) + 1, lCount++) {
  1603. pSortArray[lCount].Index = pszDevice - pDeviceList;
  1604. depthData.DeviceDepth = 0;
  1605. RtlInitUnicodeString(&depthData.DeviceInstance, pszDevice);
  1606. ntStatus = NtPlugPlayControl(PlugPlayControlGetDeviceDepth,
  1607. &depthData,
  1608. sizeof(depthData));
  1609. pSortArray[lCount].Depth = depthData.DeviceDepth;
  1610. }
  1611. //
  1612. // Sort the array so that deeper devices are ahead.
  1613. //
  1614. qsort(pSortArray, lCount, sizeof(DEVICE_INSTALL_ENTRY), compare_depth);
  1615. //
  1616. // Copy the data so that the device instance strings are sorted.
  1617. //
  1618. pTempList = HeapAlloc(ghPnPHeap, 0, ulSize * sizeof(WCHAR));
  1619. if (pTempList) {
  1620. for (pszDevice = pTempList, lCount--; lCount >= 0; lCount--) {
  1621. hr = StringCchCopyEx(
  1622. pszDevice,
  1623. ulSize,
  1624. &pDeviceList[pSortArray[lCount].Index],
  1625. NULL, NULL,
  1626. STRSAFE_NULL_ON_FAILURE);
  1627. ASSERT(SUCCEEDED(hr));
  1628. if (SUCCEEDED(hr)) {
  1629. ulSize -= lstrlen(pszDevice);
  1630. }
  1631. pszDevice += lstrlen(pszDevice) + 1;
  1632. }
  1633. *pszDevice = TEXT('\0');
  1634. HeapFree(ghPnPHeap, 0, pDeviceList);
  1635. pDeviceList = pTempList;
  1636. }
  1637. HeapFree(ghPnPHeap, 0, pSortArray);
  1638. }
  1639. //
  1640. // PHASE 1
  1641. //
  1642. // Search the registry for devices to install.
  1643. //
  1644. for (pszDevice = pDeviceList;
  1645. *pszDevice;
  1646. pszDevice += lstrlen(pszDevice) + 1) {
  1647. //
  1648. // Is device present?
  1649. //
  1650. if (IsDeviceIdPresent(pszDevice)) {
  1651. if (Pass == 1) {
  1652. //
  1653. // First time through, pass everything in the registry to
  1654. // guimode setup via the pipe, whether they are marked as
  1655. // needing to be installed or not.
  1656. //
  1657. if (!WriteFile(hPipe,
  1658. pszDevice,
  1659. (lstrlen(pszDevice)+1) * sizeof(WCHAR),
  1660. &ulSize,
  1661. NULL)) {
  1662. LogErrorEvent(ERR_WRITING_SETUP_PIPE, GetLastError(), 0);
  1663. }
  1664. } else {
  1665. //
  1666. // Second time through, only pass along anything
  1667. // that is marked as needing to be installed.
  1668. //
  1669. DevInstNeedsInstall(pszDevice, FALSE, &needsInstall);
  1670. if (needsInstall) {
  1671. if (!WriteFile(hPipe,
  1672. pszDevice,
  1673. (lstrlen(pszDevice)+1) * sizeof(WCHAR),
  1674. &ulSize,
  1675. NULL)) {
  1676. LogErrorEvent(ERR_WRITING_SETUP_PIPE, GetLastError(), 0);
  1677. }
  1678. }
  1679. }
  1680. } else if (Pass == 1) {
  1681. //
  1682. // device ID is not present
  1683. // we should have marked this as needs re-install
  1684. //
  1685. ulConfig = GetDeviceConfigFlags(pszDevice, NULL);
  1686. if ((ulConfig & CONFIGFLAG_REINSTALL)==0) {
  1687. KdPrintEx((DPFLTR_PNPMGR_ID,
  1688. DBGF_INSTALL | DBGF_WARNINGS,
  1689. "UMPNPMGR: Setup - %ws not present and not marked as needing reinstall - setting CONFIGFLAG_REINSTALL\n",
  1690. pszDevice));
  1691. ulConfig |= CONFIGFLAG_REINSTALL;
  1692. PNP_SetDeviceRegProp(NULL,
  1693. pszDevice,
  1694. CM_DRP_CONFIGFLAGS,
  1695. REG_DWORD,
  1696. (LPBYTE)&ulConfig,
  1697. sizeof(ulConfig),
  1698. 0
  1699. );
  1700. }
  1701. }
  1702. }
  1703. //
  1704. // PHASE 2
  1705. //
  1706. do {
  1707. //
  1708. // Write a NULL ID to indicate end of this batch.
  1709. //
  1710. if (!WriteFile(hPipe,
  1711. TEXT(""),
  1712. sizeof(WCHAR),
  1713. &ulSize,
  1714. NULL)) {
  1715. LogErrorEvent(ERR_WRITING_SETUP_PIPE, GetLastError(), 0);
  1716. }
  1717. //
  1718. // Wait for gui mode setup to complete processing of the last
  1719. // batch.
  1720. //
  1721. if (WaitForSingleObject(hBatchEvent,
  1722. PNP_GUISETUP_INSTALL_TIMEOUT) != WAIT_OBJECT_0) {
  1723. //
  1724. // The event was either abandoned or timed out, give up.
  1725. //
  1726. goto Clean1;
  1727. }
  1728. ResetEvent(hBatchEvent);
  1729. //
  1730. // Reenumerate the tree from the ROOT on a separate thread.
  1731. //
  1732. hThread = CreateThread(NULL,
  1733. 0,
  1734. (LPTHREAD_START_ROUTINE)ThreadProc_ReenumerateDeviceTree,
  1735. (LPVOID)pszRegRootEnumerator,
  1736. 0,
  1737. &threadID);
  1738. if (hThread == NULL) {
  1739. goto Clean1;
  1740. }
  1741. if (WaitForSingleObject(hThread,
  1742. PNP_GUISETUP_INSTALL_TIMEOUT) != WAIT_OBJECT_0) {
  1743. //
  1744. // The event was either abandadoned or timed out, give up.
  1745. //
  1746. goto Clean1;
  1747. }
  1748. //
  1749. // Check if we have reenumerated for too long.
  1750. //
  1751. if (++ulReenumerationCount >= MAX_REENUMERATION_COUNT) {
  1752. //
  1753. // Either something is wrong with one of the enumerators in
  1754. // the system (more likely) or this device tree is
  1755. // unreasonably deep. In the latter case, the remaining
  1756. // devices will get installed post setup.
  1757. //
  1758. KdPrintEx((DPFLTR_PNPMGR_ID,
  1759. DBGF_INSTALL | DBGF_ERRORS,
  1760. "UMPNPMGR: ThreadProc_GuiSetupDeviceInstall: "
  1761. "Reenumerated %d times, some enumerator is misbehaving!\n",
  1762. ulReenumerationCount));
  1763. ASSERT(ulReenumerationCount < MAX_REENUMERATION_COUNT);
  1764. goto Clean1;
  1765. }
  1766. //
  1767. // If we dont have any devices in the install list, we are done.
  1768. //
  1769. if (InstallList.Next == NULL) {
  1770. break;
  1771. }
  1772. //
  1773. // Install any new devices found as a result of setting up the
  1774. // previous batch of devices.
  1775. //
  1776. lCount = 0;
  1777. LockNotifyList(&InstallList.Lock);
  1778. while (InstallList.Next != NULL) {
  1779. //
  1780. // Retrieve and remove the first (oldest) entry in the
  1781. // install device list.
  1782. //
  1783. entry = (PPNP_INSTALL_ENTRY)InstallList.Next;
  1784. InstallList.Next = entry->Next;
  1785. UnlockNotifyList(&InstallList.Lock);
  1786. ASSERT(!(entry->Flags & (PIE_SERVER_SIDE_INSTALL_ATTEMPTED | PIE_DEVICE_INSTALL_REQUIRED_REBOOT)));
  1787. //
  1788. // Should we install this device?
  1789. //
  1790. DevInstNeedsInstall(entry->szDeviceId, FALSE, &needsInstall);
  1791. if (needsInstall) {
  1792. //
  1793. // Give this device name to gui mode setup via the pipe
  1794. //
  1795. if (!WriteFile(hPipe,
  1796. entry->szDeviceId,
  1797. (lstrlen(entry->szDeviceId)+1) * sizeof(WCHAR),
  1798. &ulSize,
  1799. NULL)) {
  1800. LogErrorEvent(ERR_WRITING_SETUP_PIPE, GetLastError(), 0);
  1801. } else {
  1802. lCount++;
  1803. }
  1804. }
  1805. HeapFree(ghPnPHeap, 0, entry);
  1806. LockNotifyList(&InstallList.Lock);
  1807. }
  1808. UnlockNotifyList(&InstallList.Lock);
  1809. } while (lCount > 0);
  1810. Clean1:
  1811. CloseHandle(hPipe);
  1812. hPipe = INVALID_HANDLE_VALUE;
  1813. CloseHandle(hPipeEvent);
  1814. hPipeEvent = NULL;
  1815. CloseHandle(hBatchEvent);
  1816. hBatchEvent = NULL;
  1817. if (hThread) {
  1818. CloseHandle(hThread);
  1819. hThread = NULL;
  1820. }
  1821. HeapFree(ghPnPHeap, 0, pDeviceList);
  1822. pDeviceList = NULL;
  1823. } // for
  1824. Clean0:
  1825. NOTHING;
  1826. } except(EXCEPTION_EXECUTE_HANDLER) {
  1827. KdPrintEx((DPFLTR_PNPMGR_ID,
  1828. DBGF_ERRORS | DBGF_INSTALL,
  1829. "UMPNPMGR: Exception in ThreadProc_GuiSetupDeviceInstall\n"));
  1830. ASSERT(0);
  1831. Status = CR_FAILURE;
  1832. //
  1833. // Reference the following variables so the compiler will respect
  1834. // statement ordering w.r.t. their assignment.
  1835. //
  1836. hPipe = hPipe;
  1837. hPipeEvent = hPipeEvent;
  1838. hBatchEvent = hBatchEvent;
  1839. hThread = hThread;
  1840. pDeviceList = pDeviceList;
  1841. }
  1842. if (hPipe != INVALID_HANDLE_VALUE) {
  1843. CloseHandle(hPipe);
  1844. }
  1845. if (hPipeEvent != NULL) {
  1846. CloseHandle(hPipeEvent);
  1847. }
  1848. if (hBatchEvent != NULL) {
  1849. CloseHandle(hBatchEvent);
  1850. }
  1851. if (hThread) {
  1852. CloseHandle(hThread);
  1853. }
  1854. if (pDeviceList != NULL) {
  1855. HeapFree(ghPnPHeap, 0, pDeviceList);
  1856. }
  1857. //
  1858. // will typically never return
  1859. //
  1860. return ThreadProc_DeviceInstall(&ulPostSetupSkipPhase1);
  1861. } // ThreadProc_GuiSetupDeviceInstall
  1862. DWORD
  1863. ThreadProc_FactoryPreinstallDeviceInstall(
  1864. LPDWORD lpThreadParam
  1865. )
  1866. /*++
  1867. Routine Description:
  1868. This routine is a thread procedure. This thread is only active during
  1869. GUI-mode setup when we are doing a factory preinstall.
  1870. This function simply creates and event, and then waits before kicking off
  1871. normal pnp device install
  1872. Arguments:
  1873. lpThreadParam - Not used.
  1874. Return Value:
  1875. Not used, currently returns result of ThreadProc_DeviceInstall - which will
  1876. normally not return.
  1877. --*/
  1878. {
  1879. HANDLE hEvent = NULL;
  1880. CONFIGRET Status = CR_SUCCESS;
  1881. UNREFERENCED_PARAMETER(lpThreadParam);
  1882. try {
  1883. hEvent = CreateEvent(NULL, TRUE, FALSE, PNP_CREATE_PIPE_EVENT);
  1884. if (!hEvent) {
  1885. if (GetLastError() == ERROR_ALREADY_EXISTS) {
  1886. hEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, PNP_CREATE_PIPE_EVENT);
  1887. if (!hEvent) {
  1888. Status = CR_FAILURE;
  1889. goto Clean0;
  1890. }
  1891. } else {
  1892. Status = CR_FAILURE;
  1893. goto Clean0;
  1894. }
  1895. }
  1896. if (WaitForSingleObject(hEvent, INFINITE) != WAIT_OBJECT_0) {
  1897. Status = CR_FAILURE;
  1898. goto Clean0; // event must have been abandoned
  1899. }
  1900. Clean0:
  1901. NOTHING;
  1902. } except(EXCEPTION_EXECUTE_HANDLER) {
  1903. KdPrintEx((DPFLTR_PNPMGR_ID,
  1904. DBGF_ERRORS | DBGF_INSTALL,
  1905. "UMPNPMGR: Exception in ThreadProc_FactoryPreinstallDeviceInstall\n"));
  1906. ASSERT(0);
  1907. Status = CR_FAILURE;
  1908. //
  1909. // Reference the following variable so the compiler will respect
  1910. // statement ordering w.r.t. its assignment.
  1911. //
  1912. hEvent = hEvent;
  1913. }
  1914. if (hEvent != NULL) {
  1915. CloseHandle(hEvent);
  1916. }
  1917. //
  1918. // will typically never return
  1919. //
  1920. return ThreadProc_DeviceInstall(NULL);
  1921. } // ThreadProc_FactoryPreinstallDeviceInstall
  1922. //-----------------------------------------------------------------------------
  1923. // Device enumeration thread - created on demand
  1924. //-----------------------------------------------------------------------------
  1925. DWORD
  1926. ThreadProc_ReenumerateDeviceTree(
  1927. LPVOID lpThreadParam
  1928. )
  1929. /*++
  1930. Routine Description:
  1931. This routine is a thread procedure. This thread is created dynamically to
  1932. perform a synchronous device re-enumeration.
  1933. This thread can be waited on and abandoned after a specified timeout, if
  1934. necessary.
  1935. Arguments:
  1936. lpThreadParam - Specifies a pointer to the device instance path that should
  1937. be re-enumerated.
  1938. Return Value:
  1939. Not used, currently returns 0.
  1940. --*/
  1941. {
  1942. PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA controlData;
  1943. //
  1944. // Reenumerate the tree from the root specified.
  1945. //
  1946. memset(&controlData, 0 , sizeof(PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA));
  1947. controlData.Flags = 0;
  1948. RtlInitUnicodeString(&controlData.DeviceInstance, (PCWSTR)lpThreadParam);
  1949. NtPlugPlayControl(PlugPlayControlEnumerateDevice,
  1950. &controlData,
  1951. sizeof(controlData));
  1952. return 0;
  1953. } // ThreadProc_ReenumerateDeviceTree
  1954. //-----------------------------------------------------------------------------
  1955. // Device installation thread
  1956. //-----------------------------------------------------------------------------
  1957. DWORD
  1958. ThreadProc_DeviceInstall(
  1959. LPDWORD lpParam
  1960. )
  1961. /*++
  1962. Routine Description:
  1963. This routine is a thread procedure.
  1964. It is invoked during a normal boot, or after the GUI-setup special case has finished
  1965. During Phase-1, all devices are checked
  1966. During Phase-2, all new devices are checked as they arrive.
  1967. Arguments:
  1968. lpParam - if given and non-zero (currently only when called from ThreadProc_GuiSetupDeviceInstall)
  1969. skips Phase-1, will never prompt for reboot
  1970. Return Value:
  1971. Not used, currently returns Status failure code, should typically not return
  1972. --*/
  1973. {
  1974. CONFIGRET Status = CR_SUCCESS;
  1975. LPWSTR pDeviceList = NULL, pszDevice = NULL;
  1976. ULONG ulSize = 0, ulProblem = 0, ulStatus, ulConfig;
  1977. DWORD InstallDevStatus, WaitResult;
  1978. PPNP_INSTALL_ENTRY InstallEntry = NULL;
  1979. PPNP_INSTALL_ENTRY current, TempInstallList, CurDupeNode, PrevDupeNode;
  1980. BOOL InstallListLocked = FALSE;
  1981. BOOL RebootRequired, needsInstall;
  1982. BOOL DeviceHasProblem = FALSE, SingleDeviceHasProblem = FALSE;
  1983. BOOL bStillInGuiModeSetup = lpParam ? (BOOL)lpParam[0] : FALSE;
  1984. ULONG ulClientSessionId = INVALID_SESSION;
  1985. ULONG ulFlags = 0;
  1986. HANDLE hAutoStartEvent;
  1987. HRESULT hr;
  1988. if (!bStillInGuiModeSetup) {
  1989. //
  1990. // If the OOBE is not running, wait until the service control manager
  1991. // has begun starting autostart services before we attempt to install
  1992. // any devices. When the OOBE is running, we don't wait for anything
  1993. // because the OOBE waits on us (via CMP_WaitNoPendingInstallEvents) to
  1994. // finish server-side installing any devices that we can before it lets
  1995. // the SCM autostart services and set this event.
  1996. //
  1997. if (!gbOobeInProgress) {
  1998. hAutoStartEvent = OpenEvent(SYNCHRONIZE,
  1999. FALSE,
  2000. SC_AUTOSTART_EVENT_NAME);
  2001. if (hAutoStartEvent) {
  2002. //
  2003. // Wait until the service controller allows other services to
  2004. // start before we try to install any devices in phases 1 and 2,
  2005. // below.
  2006. //
  2007. WaitResult = WaitForSingleObject(hAutoStartEvent, INFINITE);
  2008. ASSERT(WaitResult == WAIT_OBJECT_0);
  2009. CloseHandle(hAutoStartEvent);
  2010. } else {
  2011. //
  2012. // The service controller always creates this event, so it must
  2013. // exist by the time our service is started.
  2014. //
  2015. KdPrintEx((DPFLTR_PNPMGR_ID,
  2016. DBGF_INSTALL | DBGF_ERRORS,
  2017. "UMPNPMGR: Failed to open %ws event, error = %d\n",
  2018. SC_AUTOSTART_EVENT_NAME,
  2019. GetLastError()));
  2020. ASSERT(0);
  2021. }
  2022. }
  2023. try {
  2024. //
  2025. // Phase 1:
  2026. //
  2027. // Check the enum branch in the registry and attempt to install, one
  2028. // right after the other, any devices that need to be installed right
  2029. // now. Typically these devices showed up during boot.
  2030. //
  2031. // Retrieve the list of devices that currently need to be installed.
  2032. // Start out with a reasonably-sized buffer (16K characters) in hopes
  2033. // of avoiding 2 calls to get the device list.
  2034. //
  2035. // this phase is skipped during GUI-mode setup, and is handled by
  2036. // ThreadProc_GuiSetupDeviceInstall
  2037. //
  2038. ulSize = 16384;
  2039. for ( ; ; ) {
  2040. pDeviceList = HeapAlloc(ghPnPHeap, 0, ulSize * sizeof(WCHAR));
  2041. if (pDeviceList == NULL) {
  2042. Status = CR_OUT_OF_MEMORY;
  2043. goto Clean1;
  2044. }
  2045. Status = PNP_GetDeviceList(NULL, NULL, pDeviceList, &ulSize, 0);
  2046. if (Status == CR_SUCCESS) {
  2047. break;
  2048. } else if(Status == CR_BUFFER_SMALL) {
  2049. //
  2050. // Our initial buffer wasn't large enough. Free the current
  2051. // buffer.
  2052. //
  2053. HeapFree(ghPnPHeap, 0, pDeviceList);
  2054. pDeviceList = NULL;
  2055. //
  2056. // Now, go ahead and make the call to retrieve the actual size
  2057. // required.
  2058. //
  2059. Status = PNP_GetDeviceListSize(NULL, NULL, &ulSize, 0);
  2060. if (Status != CR_SUCCESS) {
  2061. goto Clean1;
  2062. }
  2063. } else {
  2064. //
  2065. // We failed for some reason other than buffer-too-small. Bail
  2066. // now.
  2067. //
  2068. goto Clean1;
  2069. }
  2070. }
  2071. //
  2072. // Make sure we have the device installer APIs at our disposal
  2073. // before starting server-side install.
  2074. //
  2075. InstallDevStatus = LoadDeviceInstaller();
  2076. if (InstallDevStatus != NO_ERROR) {
  2077. goto Clean1;
  2078. }
  2079. //
  2080. // Get the config flag for each device, and install any that need to be
  2081. // installed.
  2082. //
  2083. for (pszDevice = pDeviceList;
  2084. *pszDevice;
  2085. pszDevice += lstrlen(pszDevice) + 1) {
  2086. //
  2087. // Should the device be installed?
  2088. //
  2089. if (DevInstNeedsInstall(pszDevice, FALSE, &needsInstall) == CR_SUCCESS) {
  2090. if (needsInstall) {
  2091. KdPrintEx((DPFLTR_PNPMGR_ID,
  2092. DBGF_INSTALL,
  2093. "UMPNPMGR: Installing device (%ws) server-side\n",
  2094. pszDevice));
  2095. RebootRequired = FALSE;
  2096. //
  2097. // Make sure we have the device installer APIs at our disposal
  2098. // before starting server-side install.
  2099. //
  2100. InstallDevStatus = LoadDeviceInstaller();
  2101. if (InstallDevStatus == NO_ERROR) {
  2102. if (IsFastUserSwitchingEnabled()) {
  2103. ulFlags = DEVICE_INSTALL_DISPLAY_ON_CONSOLE;
  2104. } else {
  2105. ulClientSessionId = MAIN_SESSION;
  2106. ulFlags = 0;
  2107. }
  2108. //
  2109. // Attempt server-side installation of this device.
  2110. //
  2111. InstallDevStatus = InstallDeviceServerSide(pszDevice,
  2112. &RebootRequired,
  2113. &SingleDeviceHasProblem,
  2114. &ulClientSessionId,
  2115. ulFlags);
  2116. }
  2117. if(InstallDevStatus == NO_ERROR) {
  2118. KdPrintEx((DPFLTR_PNPMGR_ID,
  2119. DBGF_INSTALL,
  2120. "UMPNPMGR: Installing device (%ws), Server-side installation succeeded!\n",
  2121. pszDevice));
  2122. if (SingleDeviceHasProblem) {
  2123. DeviceHasProblem = TRUE;
  2124. }
  2125. } else {
  2126. KdPrintEx((DPFLTR_PNPMGR_ID,
  2127. DBGF_INSTALL,
  2128. "UMPNPMGR: Installing device (%ws), Server-side installation failed (Status = 0x%08X)\n",
  2129. pszDevice,
  2130. InstallDevStatus));
  2131. }
  2132. if((InstallDevStatus != NO_ERROR) || RebootRequired) {
  2133. //
  2134. // Allocate and initialize a new device install entry
  2135. // block.
  2136. //
  2137. InstallEntry = HeapAlloc(ghPnPHeap, 0, sizeof(PNP_INSTALL_ENTRY));
  2138. if(!InstallEntry) {
  2139. Status = CR_OUT_OF_MEMORY;
  2140. goto Clean1;
  2141. }
  2142. InstallEntry->Next = NULL;
  2143. InstallEntry->Flags = PIE_SERVER_SIDE_INSTALL_ATTEMPTED;
  2144. if(InstallDevStatus == NO_ERROR) {
  2145. //
  2146. // We didn't get here because the install failed,
  2147. // so it must've been because the installation
  2148. // requires a reboot.
  2149. //
  2150. ASSERT(RebootRequired);
  2151. InstallEntry->Flags |= PIE_DEVICE_INSTALL_REQUIRED_REBOOT;
  2152. //
  2153. // Set the global server side device install
  2154. // reboot needed bool to TRUE.
  2155. //
  2156. gServerSideDeviceInstallRebootNeeded = TRUE;
  2157. }
  2158. //
  2159. // Copy the Device ID to the install list entry.
  2160. // Upon failure, we will just end up adding an
  2161. // install entry with a NULL device id to the list.
  2162. // Non ideal, but we should still do it so that we
  2163. // can preserve the flags that may indicate a reboot
  2164. // is required.
  2165. //
  2166. hr = StringCchCopyEx(InstallEntry->szDeviceId,
  2167. MAX_DEVICE_ID_LEN,
  2168. pszDevice,
  2169. NULL, NULL,
  2170. STRSAFE_NULL_ON_FAILURE);
  2171. ASSERT(SUCCEEDED(hr));
  2172. //
  2173. // Insert this entry in the device install list.
  2174. //
  2175. LockNotifyList(&InstallList.Lock);
  2176. InstallListLocked = TRUE;
  2177. current = (PPNP_INSTALL_ENTRY)InstallList.Next;
  2178. if(!current) {
  2179. InstallList.Next = InstallEntry;
  2180. } else {
  2181. while((PPNP_INSTALL_ENTRY)current->Next) {
  2182. current = (PPNP_INSTALL_ENTRY)current->Next;
  2183. }
  2184. current->Next = InstallEntry;
  2185. }
  2186. //
  2187. // Newly-allocated entry now added to the list--NULL
  2188. // out the pointer so we won't try to free it if we
  2189. // happen to encounter an exception later.
  2190. //
  2191. InstallEntry = NULL;
  2192. UnlockNotifyList(&InstallList.Lock);
  2193. InstallListLocked = FALSE;
  2194. SetEvent(InstallEvents[NEEDS_INSTALL_EVENT]);
  2195. }
  2196. }
  2197. } else {
  2198. KdPrintEx((DPFLTR_PNPMGR_ID,
  2199. DBGF_INSTALL,
  2200. "UMPNPMGR: Ignoring not present device (%ws)\n",
  2201. pszDevice));
  2202. }
  2203. }
  2204. Clean1:
  2205. //
  2206. // Up to this point, we have only attempted server-side installation
  2207. // of devices, so any device install clients we might have launched
  2208. // would have been for UI only. Since we are done installing
  2209. // devices for the time being, we should unload the device installer
  2210. // APIs, and get rid of any device install clients that currently
  2211. // exist.
  2212. //
  2213. UnloadDeviceInstaller();
  2214. } except(EXCEPTION_EXECUTE_HANDLER) {
  2215. KdPrintEx((DPFLTR_PNPMGR_ID,
  2216. DBGF_ERRORS | DBGF_INSTALL,
  2217. "UMPNPMGR: Exception in ThreadProc_DeviceInstall\n"));
  2218. ASSERT(0);
  2219. Status = CR_FAILURE;
  2220. //
  2221. // Reference the following variables so the compiler will respect
  2222. // statement ordering w.r.t. assignment.
  2223. //
  2224. pDeviceList = pDeviceList;
  2225. InstallListLocked = InstallListLocked;
  2226. InstallEntry = InstallEntry;
  2227. }
  2228. if(InstallEntry) {
  2229. HeapFree(ghPnPHeap, 0, InstallEntry);
  2230. InstallEntry = NULL;
  2231. }
  2232. if(InstallListLocked) {
  2233. UnlockNotifyList(&InstallList.Lock);
  2234. InstallListLocked = FALSE;
  2235. }
  2236. if(pDeviceList != NULL) {
  2237. HeapFree(ghPnPHeap, 0, pDeviceList);
  2238. }
  2239. }
  2240. //
  2241. // NOTE: We should remove this line if we ever hook up the 'finished
  2242. // installing hardware' balloon so that it comes up if we install devices
  2243. // before a user logs on.
  2244. //
  2245. DeviceHasProblem = FALSE;
  2246. //
  2247. // Maintain a temporary list of PNP_INSTALL_ENTRY nodes that we needed to
  2248. // initiate client-side installation for, but couldn't these nodes get
  2249. // re-added to the master InstallList once all entries have been processed.
  2250. // Also, keep a pointer to the end of the list for efficient appending of
  2251. // nodes to the queue.
  2252. //
  2253. current = TempInstallList = NULL;
  2254. try {
  2255. //
  2256. // Phase 2: Hang around and be prepared to install any devices
  2257. // that come on line for the first time while we're
  2258. // running.
  2259. // we may come into Phase 2 (skipping Phase 1) in GUI-Setup
  2260. //
  2261. for ( ; ; ) {
  2262. //
  2263. // Before starting an indefinite wait, test the event state and set
  2264. // the ghNoPendingInstalls event accordingly. This event is just a
  2265. // backdoor way for device manager (and others) to see if we're
  2266. // still installing things.
  2267. //
  2268. if(WaitForSingleObject(InstallEvents[NEEDS_INSTALL_EVENT], 0) != WAIT_OBJECT_0) {
  2269. //
  2270. // There's nothing waiting to be installed--set the event.
  2271. //
  2272. SetEvent(ghNoPendingInstalls);
  2273. }
  2274. //
  2275. // Wait until the device event thread tells us we need to
  2276. // dynamically install a new device (or until somebody logs on).
  2277. //
  2278. WaitForMultipleObjects(NUM_INSTALL_EVENTS,
  2279. InstallEvents,
  2280. FALSE, // wake up on either event
  2281. INFINITE // I can wait all day
  2282. );
  2283. //
  2284. // After I empty the list, this thread can sleep until another new
  2285. // device needs to be installed...
  2286. //
  2287. ResetEvent(InstallEvents[NEEDS_INSTALL_EVENT]);
  2288. //
  2289. // ...or until a user logs in (note that we only want to awake once
  2290. // per log-in.
  2291. //
  2292. ResetEvent(InstallEvents[LOGGED_ON_EVENT]);
  2293. //
  2294. // We now have something to do, so reset the event that lets folks
  2295. // like DevMgr know when we're idle.
  2296. //
  2297. ResetEvent(ghNoPendingInstalls);
  2298. #if DBG
  2299. RtlValidateHeap(ghPnPHeap,0,NULL);
  2300. #endif
  2301. //
  2302. // Process each device that needs to be installed.
  2303. //
  2304. while (InstallList.Next != NULL) {
  2305. //
  2306. // Retrieve and remove the first (oldest) entry in the
  2307. // install device list.
  2308. //
  2309. LockNotifyList(&InstallList.Lock);
  2310. InstallListLocked = TRUE;
  2311. InstallEntry = (PPNP_INSTALL_ENTRY)InstallList.Next;
  2312. InstallList.Next = InstallEntry->Next;
  2313. //
  2314. // Now, scan the rest of the list looking for additional nodes
  2315. // related to this same device. If we find any, OR their flags
  2316. // into our 'master' node, and remove the duplicated nodes from
  2317. // the list. We can get duplicates due to the fact that both
  2318. // the event thread and this thread can be placing items in the
  2319. // list. We don't want to be attempting (failing) server-side
  2320. // installations multiple times.
  2321. //
  2322. CurDupeNode = (PPNP_INSTALL_ENTRY)InstallList.Next;
  2323. PrevDupeNode = NULL;
  2324. while(CurDupeNode) {
  2325. if (CompareString(
  2326. LOCALE_INVARIANT, NORM_IGNORECASE,
  2327. InstallEntry->szDeviceId, -1,
  2328. CurDupeNode->szDeviceId, -1) == CSTR_EQUAL) {
  2329. //
  2330. // We have a duplicate! OR the flags into those of
  2331. // the install entry we retrieved from the head of
  2332. // the list.
  2333. //
  2334. InstallEntry->Flags |= CurDupeNode->Flags;
  2335. //
  2336. // Now remove this duplicate node from the list.
  2337. //
  2338. if(PrevDupeNode) {
  2339. PrevDupeNode->Next = CurDupeNode->Next;
  2340. } else {
  2341. InstallList.Next = CurDupeNode->Next;
  2342. }
  2343. HeapFree(ghPnPHeap, 0, CurDupeNode);
  2344. if(PrevDupeNode) {
  2345. CurDupeNode = (PPNP_INSTALL_ENTRY)PrevDupeNode->Next;
  2346. } else {
  2347. CurDupeNode = (PPNP_INSTALL_ENTRY)InstallList.Next;
  2348. }
  2349. } else {
  2350. PrevDupeNode = CurDupeNode;
  2351. CurDupeNode = (PPNP_INSTALL_ENTRY)CurDupeNode->Next;
  2352. }
  2353. }
  2354. UnlockNotifyList(&InstallList.Lock);
  2355. InstallListLocked = FALSE;
  2356. if(InstallEntry->Flags & PIE_DEVICE_INSTALL_REQUIRED_REBOOT) {
  2357. //
  2358. // We've already performed a (successful) server-side
  2359. // installation on this device. Remember the fact that a
  2360. // reboot is needed, so we'll prompt after processing this
  2361. // batch of new hardware.
  2362. //
  2363. // This will be our last chance to prompt for reboot on this
  2364. // node, because the next thing we're going to do is free
  2365. // this install entry!
  2366. //
  2367. gServerSideDeviceInstallRebootNeeded = TRUE;
  2368. } else {
  2369. //
  2370. // Verify that device really needs to be installed
  2371. //
  2372. ulConfig = GetDeviceConfigFlags(InstallEntry->szDeviceId, NULL);
  2373. Status = GetDeviceStatus(InstallEntry->szDeviceId, &ulStatus, &ulProblem);
  2374. if (Status == CR_SUCCESS) {
  2375. //
  2376. // Note that we must explicitly check below for the
  2377. // presence of the CONFIGFLAG_REINSTALL config flag. We
  2378. // can't simply rely on the CM_PROB_REINSTALL problem
  2379. // being set, because we may have encountered a device
  2380. // during our phase 1 processing whose installation was
  2381. // deferred because it provided finish-install wizard
  2382. // pages. Since we only discover that this is the case
  2383. // _after_ successful completion of DIF_INSTALLDEVICE,
  2384. // it's too late to set the problem (kernel-mode PnP
  2385. // manager only allows us to set a problem of needs-
  2386. // reboot for a running devnode).
  2387. //
  2388. Status =
  2389. DevInstNeedsInstall(
  2390. InstallEntry->szDeviceId,
  2391. TRUE,
  2392. &needsInstall);
  2393. if ((Status == CR_SUCCESS) && needsInstall) {
  2394. if(!(InstallEntry->Flags & PIE_SERVER_SIDE_INSTALL_ATTEMPTED)) {
  2395. //
  2396. // We haven't tried to install this device
  2397. // server-side yet, so try that now.
  2398. //
  2399. KdPrintEx((DPFLTR_PNPMGR_ID,
  2400. DBGF_INSTALL,
  2401. "UMPNPMGR: Installing device (%ws) server-side\n\t Status = 0x%08X\n\t Problem = %d\n\t ConfigFlags = 0x%08X\n",
  2402. InstallEntry->szDeviceId,
  2403. ulStatus,
  2404. ulProblem,
  2405. ulConfig));
  2406. //
  2407. // Make sure we have the device installer APIs at our disposal
  2408. // before starting server-side install.
  2409. //
  2410. InstallDevStatus = LoadDeviceInstaller();
  2411. if (InstallDevStatus == NO_ERROR) {
  2412. if (IsFastUserSwitchingEnabled()) {
  2413. ulFlags = DEVICE_INSTALL_DISPLAY_ON_CONSOLE;
  2414. } else {
  2415. ulClientSessionId = MAIN_SESSION;
  2416. ulFlags = 0;
  2417. }
  2418. InstallDevStatus = InstallDeviceServerSide(
  2419. InstallEntry->szDeviceId,
  2420. &gServerSideDeviceInstallRebootNeeded,
  2421. &SingleDeviceHasProblem,
  2422. &ulClientSessionId,
  2423. ulFlags);
  2424. }
  2425. if(InstallDevStatus == NO_ERROR) {
  2426. KdPrintEx((DPFLTR_PNPMGR_ID,
  2427. DBGF_INSTALL,
  2428. "UMPNPMGR: Installing device (%ws), Server-side installation succeeded!\n",
  2429. InstallEntry->szDeviceId));
  2430. if (SingleDeviceHasProblem) {
  2431. DeviceHasProblem = TRUE;
  2432. }
  2433. } else {
  2434. KdPrintEx((DPFLTR_PNPMGR_ID,
  2435. DBGF_INSTALL,
  2436. "UMPNPMGR: Installing device (%ws), Server-side installation failed (Status = 0x%08X)\n",
  2437. InstallEntry->szDeviceId,
  2438. InstallDevStatus));
  2439. InstallEntry->Flags |= PIE_SERVER_SIDE_INSTALL_ATTEMPTED;
  2440. }
  2441. } else {
  2442. //
  2443. // Set some bogus error so we'll drop into the
  2444. // non-server install codepath below.
  2445. //
  2446. InstallDevStatus = ERROR_INVALID_DATA;
  2447. }
  2448. if(InstallDevStatus != NO_ERROR) {
  2449. KdPrintEx((DPFLTR_PNPMGR_ID,
  2450. DBGF_INSTALL,
  2451. "UMPNPMGR: Installing device (%ws) client-side\n\t Status = 0x%08X\n\t Problem = %d\n\t ConfigFlags = 0x%08X\n",
  2452. InstallEntry->szDeviceId,
  2453. ulStatus,
  2454. ulProblem,
  2455. ulConfig));
  2456. if (IsFastUserSwitchingEnabled()) {
  2457. ulFlags = DEVICE_INSTALL_DISPLAY_ON_CONSOLE;
  2458. } else {
  2459. ulClientSessionId = MAIN_SESSION;
  2460. ulFlags = 0;
  2461. }
  2462. if (!InstallDevice(InstallEntry->szDeviceId,
  2463. &ulClientSessionId,
  2464. ulFlags)) {
  2465. //
  2466. // We weren't able to kick off a device
  2467. // install on the client side (probably
  2468. // because no one was logged in). Stick
  2469. // this PNP_INSTALL_ENTRY node into a
  2470. // temporary list that we'll re-add into
  2471. // the InstallList queue once we've emptied
  2472. // it.
  2473. //
  2474. if(current) {
  2475. current->Next = InstallEntry;
  2476. current = InstallEntry;
  2477. } else {
  2478. ASSERT(!TempInstallList);
  2479. TempInstallList = current = InstallEntry;
  2480. }
  2481. //
  2482. // NULL out the InstallEntry pointer so we
  2483. // don't try to free it later.
  2484. //
  2485. InstallEntry = NULL;
  2486. }
  2487. }
  2488. } else if((ulStatus & DN_HAS_PROBLEM) &&
  2489. (ulProblem == CM_PROB_NEED_RESTART)) {
  2490. //
  2491. // This device was percolated up from kernel-mode
  2492. // for the sole purpose of requesting a reboot.
  2493. // This presently only happens when we encounter a
  2494. // duplicate devnode, and we then "unique-ify" it
  2495. // to keep from bugchecking. We don't want the
  2496. // unique-ified devnode to actually be installed/
  2497. // used. Instead, we just want to give the user a
  2498. // prompt to reboot, and after they reboot, all
  2499. // should be well. The scenario where this has
  2500. // arisen is in relation to a USB printer (with a
  2501. // serial number) that is moved from one port to
  2502. // another during a suspend. When we resume, we
  2503. // have both an arrival and a removal to process,
  2504. // and if we process the arrival first, we think
  2505. // we've found a dupe.
  2506. //
  2507. KdPrintEx((DPFLTR_PNPMGR_ID,
  2508. DBGF_INSTALL,
  2509. "UMPNPMGR: Duplicate device detected (%ws), need to prompt user to reboot!\n",
  2510. InstallEntry->szDeviceId));
  2511. //
  2512. // Stick this entry into our temporary list to deal
  2513. // with later...
  2514. //
  2515. if(current) {
  2516. current->Next = InstallEntry;
  2517. current = InstallEntry;
  2518. } else {
  2519. ASSERT(!TempInstallList);
  2520. TempInstallList = current = InstallEntry;
  2521. }
  2522. //
  2523. // If possible, we want to prompt for reboot right
  2524. // away (that is, after all install events are
  2525. // drained)...
  2526. //
  2527. gServerSideDeviceInstallRebootNeeded = TRUE;
  2528. //
  2529. // If no user is logged in yet, flag this install
  2530. // entry so we'll try to prompt for reboot the next
  2531. // time we're awakened (which hopefully will be due
  2532. // to a user logging in).
  2533. //
  2534. InstallEntry->Flags |= PIE_DEVICE_INSTALL_REQUIRED_REBOOT;
  2535. //
  2536. // NULL out the InstallEntry pointer so we
  2537. // don't try to free it later.
  2538. //
  2539. InstallEntry = NULL;
  2540. }
  2541. }
  2542. }
  2543. if(InstallEntry) {
  2544. HeapFree(ghPnPHeap, 0, InstallEntry);
  2545. InstallEntry = NULL;
  2546. }
  2547. }
  2548. //
  2549. // We've processed all device install events known to us at this
  2550. // time. If we encountered any device whose installation requires
  2551. // a reboot, prompt the logged-in user (if any) to reboot now.
  2552. //
  2553. if (gServerSideDeviceInstallRebootNeeded) {
  2554. ulFlags = DEVICE_INSTALL_FINISHED_REBOOT;
  2555. if (IsFastUserSwitchingEnabled()) {
  2556. ulFlags |= DEVICE_INSTALL_DISPLAY_ON_CONSOLE;
  2557. } else {
  2558. ulClientSessionId = MAIN_SESSION;
  2559. }
  2560. if (bStillInGuiModeSetup) {
  2561. //
  2562. // if we're still in GUI setup, we're going to suppress
  2563. // any reboot prompts
  2564. //
  2565. gServerSideDeviceInstallRebootNeeded = FALSE;
  2566. } else if (PromptUser(&ulClientSessionId,
  2567. ulFlags)) {
  2568. //
  2569. // We successfully delivered the reboot prompt, so if the
  2570. // user chose to ignore it, we don't want to prompt again
  2571. // for reboot the next time new hardware shows up (unless
  2572. // that hardware also requires a reboot).
  2573. //
  2574. gServerSideDeviceInstallRebootNeeded = FALSE;
  2575. }
  2576. }
  2577. if(TempInstallList) {
  2578. //
  2579. // Add our temporary list of PNP_INSTALL_ENTRY nodes back into
  2580. // the InstallList queue. We _do not_ set the event that says
  2581. // there's more to do, so these nodes will be seen again only
  2582. // if (a) somebody logs in or (b) more new hardware shows up.
  2583. //
  2584. // Note: we cannot assume that the list is empty, because there
  2585. // may have been an insertion after the last time we checked it
  2586. // above. We want to add our stuff to the beginning of the
  2587. // InstallList queue, since the items we just finished
  2588. // processing appeared before any new entries that might be
  2589. // there now.
  2590. //
  2591. LockNotifyList(&InstallList.Lock);
  2592. InstallListLocked = TRUE;
  2593. ASSERT(current);
  2594. current->Next = InstallList.Next;
  2595. InstallList.Next = TempInstallList;
  2596. //
  2597. // Null out our temporary install list pointers to indicate
  2598. // that the list is now empty.
  2599. //
  2600. current = TempInstallList = NULL;
  2601. UnlockNotifyList(&InstallList.Lock);
  2602. InstallListLocked = FALSE;
  2603. }
  2604. //
  2605. // Before starting an indefinite wait, test the InstallEvents to see
  2606. // if there any new devices to install, or there are still devices
  2607. // to be installed in the InstallList. If neither of these is the
  2608. // case after waiting a few seconds, we'll notify the user that
  2609. // we're done installing devices for now, unload setupapi, and close
  2610. // all device install clients.
  2611. //
  2612. WaitResult = WaitForMultipleObjects(NUM_INSTALL_EVENTS,
  2613. InstallEvents,
  2614. FALSE,
  2615. DEVICE_INSTALL_COMPLETE_WAIT_TIME);
  2616. if ((WaitResult != (WAIT_OBJECT_0 + NEEDS_INSTALL_EVENT)) &&
  2617. (InstallList.Next == NULL)) {
  2618. KdPrintEx((DPFLTR_PNPMGR_ID,
  2619. DBGF_INSTALL,
  2620. "UMPNPMGR: ThreadProc_DeviceInstall: no more devices to install.\n"));
  2621. //
  2622. // There's nothing waiting to be installed--set the event.
  2623. //
  2624. SetEvent(ghNoPendingInstalls);
  2625. //
  2626. // Notify the user (if any), that we think we're done installing
  2627. // devices for now. Note that if we never used a device install
  2628. // client at any time in this pass (all server-side or silent
  2629. // installs), then we won't prompt about this.
  2630. //
  2631. ulFlags = DEVICE_INSTALL_BATCH_COMPLETE;
  2632. if (DeviceHasProblem) {
  2633. ulFlags |= DEVICE_INSTALL_PROBLEM;
  2634. }
  2635. if (IsFastUserSwitchingEnabled()) {
  2636. ulFlags |= DEVICE_INSTALL_DISPLAY_ON_CONSOLE;
  2637. } else {
  2638. ulClientSessionId = MAIN_SESSION;
  2639. }
  2640. PromptUser(&ulClientSessionId,
  2641. ulFlags);
  2642. //
  2643. // Clear the DeviceHasProblem boolean since we just notified the
  2644. // user.
  2645. //
  2646. DeviceHasProblem = FALSE;
  2647. //
  2648. // We notified the user, now wait around for 10 more seconds
  2649. // from the time of prompting before closing the client to make
  2650. // sure that some new device doesn't arrive, in which case we
  2651. // would just immediately load the installer again.
  2652. //
  2653. WaitResult = WaitForMultipleObjects(NUM_INSTALL_EVENTS,
  2654. InstallEvents,
  2655. FALSE,
  2656. DEVICE_INSTALL_COMPLETE_DISPLAY_TIME);
  2657. if ((WaitResult != (WAIT_OBJECT_0 + NEEDS_INSTALL_EVENT)) &&
  2658. (InstallList.Next == NULL)) {
  2659. //
  2660. // Unload the device installer, and get rid of any device
  2661. // install clients that currently exist on any sessions.
  2662. // Note that closing the device install client will make the
  2663. // above prompt go away.
  2664. //
  2665. UnloadDeviceInstaller();
  2666. }
  2667. }
  2668. }
  2669. } except(EXCEPTION_EXECUTE_HANDLER) {
  2670. KdPrintEx((DPFLTR_PNPMGR_ID,
  2671. DBGF_ERRORS | DBGF_INSTALL,
  2672. "UMPNPMGR: Exception in ThreadProc_DeviceInstall\n"));
  2673. ASSERT(0);
  2674. Status = CR_FAILURE;
  2675. //
  2676. // Reference the following variables so the compiler will respect
  2677. // statement ordering w.r.t. their assignment.
  2678. //
  2679. InstallListLocked = InstallListLocked;
  2680. InstallEntry = InstallEntry;
  2681. TempInstallList = TempInstallList;
  2682. }
  2683. if(InstallListLocked) {
  2684. UnlockNotifyList(&InstallList.Lock);
  2685. }
  2686. if(InstallEntry) {
  2687. HeapFree(ghPnPHeap, 0, InstallEntry);
  2688. }
  2689. while(TempInstallList) {
  2690. current = (PPNP_INSTALL_ENTRY)(TempInstallList->Next);
  2691. HeapFree(ghPnPHeap, 0, TempInstallList);
  2692. TempInstallList = current;
  2693. }
  2694. //
  2695. // meaningless return value, since this thread should never exit.
  2696. //
  2697. return (DWORD)Status;
  2698. } // ThreadProc_DeviceInstall
  2699. BOOL
  2700. InstallDevice(
  2701. IN LPWSTR pszDeviceId,
  2702. IN OUT PULONG SessionId,
  2703. IN ULONG Flags
  2704. )
  2705. /*++
  2706. Routine Description:
  2707. This routine initiates a device installation with the device install client
  2708. (newdev.dll) on the current active console session, creating one if
  2709. necessary. This routine waits for the client to signal completion, the
  2710. process to signal that it has terminated, or this service to signal that we
  2711. have disconnected ourselves from the client.
  2712. Arguments:
  2713. pszDeviceId - device instance ID of the devnode to be installed.
  2714. SessionId - Supplies the address of a variable containing the SessionId on
  2715. which the device install client is to be displayed. If successful, the
  2716. SessionId will contain the session on which the device install client
  2717. process was launched. Otherwise, will contain an invalid SessionId,
  2718. INVALID_SESSION (0xFFFFFFFF).
  2719. Flags - Specifies flags describing the behavior of the device install client.
  2720. The following flags are currently defined:
  2721. DEVICE_INSTALL_DISPLAY_ON_CONSOLE - if specified, the value in the
  2722. SessionId variable will be ignored, and the device installclient will
  2723. always be displayed on the current active console session. The
  2724. SessionId of the current active console session will be returned in
  2725. the SessionId.
  2726. Return Value:
  2727. Returns TRUE is the device installation was completed by the device install
  2728. client, FALSE otherwise.
  2729. --*/
  2730. {
  2731. BOOL b;
  2732. HANDLE hFinishEvents[3] = { NULL, NULL, NULL };
  2733. DWORD dwWait;
  2734. PINSTALL_CLIENT_ENTRY pDeviceInstallClient = NULL;
  2735. HRESULT hr;
  2736. //
  2737. // Assume failure
  2738. //
  2739. b = FALSE;
  2740. //
  2741. // Validate parameters
  2742. //
  2743. ASSERT(SessionId);
  2744. if (SessionId == NULL) {
  2745. return FALSE;
  2746. }
  2747. try {
  2748. //
  2749. // Calling DoDeviceInstallClient will create the newdev.dll process and open a named
  2750. // pipe if it isn't already been done earlier. It will then send the Device Id to
  2751. // newdev.dll over the pipe.
  2752. //
  2753. if (DoDeviceInstallClient(pszDeviceId,
  2754. SessionId,
  2755. Flags,
  2756. &pDeviceInstallClient)) {
  2757. ASSERT(pDeviceInstallClient);
  2758. ASSERT(pDeviceInstallClient->ulSessionId == *SessionId);
  2759. //
  2760. // Keep track of the device id last sent to this client before we
  2761. // disconnected from it. This will avoid duplicate popups if we
  2762. // reconnect to this session again, and attempt to client-side
  2763. // install the same device.
  2764. //
  2765. hr = StringCchCopyEx(pDeviceInstallClient->LastDeviceId,
  2766. MAX_DEVICE_ID_LEN,
  2767. pszDeviceId,
  2768. NULL, NULL,
  2769. STRSAFE_NULL_ON_FAILURE);
  2770. //
  2771. // Upon failure, the recorded LastDeviceId is NULL, so we will not
  2772. // be able to determine what device this client last handled. Not
  2773. // ideal, but not fatal either.
  2774. //
  2775. ASSERT(SUCCEEDED(hr));
  2776. //
  2777. // Wait for the device install to be signaled from newdev.dll
  2778. // to let us know that it has completed the installation.
  2779. //
  2780. // Wait on the client's process as well, to catch the case
  2781. // where the process crashes (or goes away) without signaling the
  2782. // device install event.
  2783. //
  2784. // Also wait on the disconnect event in case we have explicitly
  2785. // disconnected from the client while switching sessions.
  2786. //
  2787. hFinishEvents[0] = pDeviceInstallClient->hProcess;
  2788. hFinishEvents[1] = pDeviceInstallClient->hEvent;
  2789. hFinishEvents[2] = pDeviceInstallClient->hDisconnectEvent;
  2790. dwWait = WaitForMultipleObjects(3, hFinishEvents, FALSE, INFINITE);
  2791. if (dwWait == WAIT_OBJECT_0) {
  2792. //
  2793. // If the return is WAIT_OBJECT_0 then the newdev.dll
  2794. // process has gone away.
  2795. //
  2796. KdPrintEx((DPFLTR_PNPMGR_ID,
  2797. DBGF_INSTALL,
  2798. "UMPNPMGR: InstallDevice: process signalled, closing device install client!\n"));
  2799. } else if (dwWait == (WAIT_OBJECT_0 + 1)) {
  2800. //
  2801. // If the return is WAIT_OBJECT_0 + 1 then the device
  2802. // installer successfully received the request.
  2803. //
  2804. KdPrintEx((DPFLTR_PNPMGR_ID,
  2805. DBGF_INSTALL,
  2806. "UMPNPMGR: InstallDevice: device install succeeded\n"));
  2807. b = TRUE;
  2808. //
  2809. // This device install client is no longer processing any
  2810. // devices, so clear the device id.
  2811. //
  2812. *pDeviceInstallClient->LastDeviceId = L'\0';
  2813. } else if (dwWait == (WAIT_OBJECT_0 + 2)) {
  2814. //
  2815. // If the return is WAIT_OBJECT_0 + 2 then we were explicitly
  2816. // disconnected from the device install client. Consider the
  2817. // device install unsuccessful so that this device remains in
  2818. // the install list.
  2819. //
  2820. KdPrintEx((DPFLTR_PNPMGR_ID,
  2821. DBGF_INSTALL,
  2822. "UMPNPMGR: InstallDevice: device install client disconnected\n"));
  2823. } else {
  2824. //
  2825. // The wait was satisfied for some reason other than the
  2826. // specified objects. This should never happen, but just in
  2827. // case, we'll close the client.
  2828. //
  2829. KdPrintEx((DPFLTR_PNPMGR_ID,
  2830. DBGF_INSTALL | DBGF_ERRORS,
  2831. "UMPNPMGR: InstallDevice: wait completed unexpectedly!\n"));
  2832. }
  2833. //
  2834. // Remove the reference placed on the client while it was in use.
  2835. //
  2836. LockNotifyList(&InstallClientList.Lock);
  2837. DereferenceDeviceInstallClient(pDeviceInstallClient);
  2838. if ((dwWait != (WAIT_OBJECT_0 + 1)) &&
  2839. (dwWait != (WAIT_OBJECT_0 + 2))) {
  2840. //
  2841. // Unless the client signalled successful receipt of the
  2842. // request, or the client's session was disconnected from the
  2843. // console, the attempt to use this client was unsuccessful.
  2844. // Remove the initial reference so all associated handles will
  2845. // be closed and the entry will be freed when it is no longer in
  2846. // use.
  2847. //
  2848. //
  2849. // Note that if we were unsuccessful because of a
  2850. // logoff, we would have already dereferenced the
  2851. // client then, in which case the above dereference
  2852. // was the final one, and pDeviceInstallClient would
  2853. // be invalid. Instead, attempt to re-locate the
  2854. // client by the session id.
  2855. //
  2856. pDeviceInstallClient = LocateDeviceInstallClient(*SessionId);
  2857. if (pDeviceInstallClient) {
  2858. ASSERT(pDeviceInstallClient->RefCount == 1);
  2859. DereferenceDeviceInstallClient(pDeviceInstallClient);
  2860. }
  2861. }
  2862. UnlockNotifyList(&InstallClientList.Lock);
  2863. }
  2864. } except (EXCEPTION_EXECUTE_HANDLER) {
  2865. KdPrintEx((DPFLTR_PNPMGR_ID,
  2866. DBGF_ERRORS | DBGF_INSTALL,
  2867. "UMPNPMGR: Exception in InstallDevice!\n"));
  2868. ASSERT(0);
  2869. b = FALSE;
  2870. }
  2871. return b;
  2872. } // InstallDevice
  2873. //-----------------------------------------------------------------------------
  2874. // Device event server-side rpc routines
  2875. //-----------------------------------------------------------------------------
  2876. CONFIGRET
  2877. PNP_RegisterNotification(
  2878. IN handle_t hBinding,
  2879. IN ULONG_PTR hRecipient,
  2880. IN LPWSTR ServiceName,
  2881. IN LPBYTE NotificationFilter,
  2882. IN ULONG ulSize,
  2883. IN DWORD Flags,
  2884. OUT PNP_NOTIFICATION_CONTEXT *Context,
  2885. IN ULONG ProcessId,
  2886. IN ULONG64 *ClientContext
  2887. )
  2888. /*++
  2889. Routine Description:
  2890. This routine is the rpc server-side of the CMP_RegisterNotification routine.
  2891. It performs the remaining parameter validation and actually registers the
  2892. notification request appropriately.
  2893. Arguments:
  2894. hBinding - RPC binding handle.
  2895. hRecipient - The Flags value specifies what type of handle this is,
  2896. currently it's either a window handle or a service handle.
  2897. NotificationFilter - Specifies a pointer to one of the DEV_BROADCAST_XXX
  2898. structures.
  2899. ulSize - Specifies the size of the NotificationFilter structure.
  2900. Flags - Specifies additional paramters used to describe the client or
  2901. the supplied parameters. The Flags parameter is subdivided
  2902. into multiple fields that are interpreted separately, as
  2903. described below.
  2904. ** The Flags parameter contains a field that describes the type
  2905. of the hRecipient handle passed in. This field should be
  2906. interpreted as an enum, and can be extracted from the Flags
  2907. parameter using the following mask:
  2908. DEVICE_NOTIFY_HANDLE_MASK
  2909. Currently one of the following values must be specified by
  2910. this field:
  2911. DEVICE_NOTIFY_WINDOW_HANDLE - hRecipient is a window handle
  2912. (HWND) for a window whose WNDPROC will be registered to
  2913. receive WM_DEVICECHANGE window messages for the filtered
  2914. events specified by the supplied NotificationFilter.
  2915. DEVICE_NOTIFY_SERVICE_HANDLE - hRecipient is a service status
  2916. handle (SERVICE_STATUS_HANDLE) for a service whose
  2917. HandlerEx routine will be registered to receive
  2918. SERVICE_CONTROL_DEVICEEVENT service controls for the
  2919. filtered events specified by the supplied
  2920. NotificationFilter.
  2921. NOTE: in reality - hRecipient is just the name of the
  2922. service, as resolved by the cfgmgr32 client. the SCM will
  2923. actually resolve this name for us to the true
  2924. SERVICE_STATUS_HANDLE for this service.
  2925. DEVICE_NOTIFY_COMPLETION_HANDLE - not currently implemented.
  2926. ** The Flags parameter contains a field that described additional
  2927. properties for the notification. This field should be
  2928. interpreted as a bitmask, and can be extracted from the Flags
  2929. parameter using the following mask:
  2930. DEVICE_NOTIFY_PROPERTY_MASK
  2931. Currently, the following flags are defined for this field:
  2932. DEVICE_NOTIFY_ALL_INTERFACE_CLASSES - This flag is only valid
  2933. when a DBT_DEVTYP_DEVICEINTERFACE type notification filter
  2934. is supplied. This flag specifies that the caller wishes
  2935. to receive notification of events for device interfaces of
  2936. all classes. If this flag is specified, the
  2937. dbcc_classguid member of the NotificationFilter structure
  2938. is ignored.
  2939. ** The Flags parameter also contains a "Reserved" field, that is
  2940. reserved for use by the cfgmgr32 client to this interface
  2941. only. This field should be interpreted as a bitmask, and can
  2942. be extracted from the Flags parameter using the following
  2943. mask:
  2944. DEVICE_NOTIFY_RESERVED_MASK
  2945. Currently, the following flags are defined for this field:
  2946. DEVICE_NOTIFY_WOW64_CLIENT - Specifies to a 64-bit server
  2947. caller is a 32-bit process running on WOW64. The 64-bit
  2948. server uses this information to construct 32-bit
  2949. compatible notification filters for the client.
  2950. Context - On return, this value returns the server notification context
  2951. to the client, that is supplied when unregistering this
  2952. notification request.
  2953. hProcess - Process Id of the calling application.
  2954. ClientContext - Specifies a pointer to a 64-bit value that contains the
  2955. client-context pointer. This value is the HDEVNOTIFY
  2956. notification handle returned to caller upon successful
  2957. registration. It is actually a pointer to the client memory
  2958. that will reference the returned server-notification context
  2959. pointer - but is never used as a pointer here on the
  2960. server-side. It is only used by the server to be specified as
  2961. the dbch_hdevnotify member of the DEV_BROADCAST_HANDLE
  2962. notification structure, supplied to the caller on
  2963. DBT_DEVTYP_HANDLE notification events.
  2964. NOTE: This value is truncated to 32-bits on 32-bit platforms,
  2965. but is always transmitted as a 64-bit value by the RPC
  2966. interface - for consistent marshalling of the data by RPC for
  2967. all 32-bit / 64-bit client / server combinations.
  2968. Return Value:
  2969. Return CR_SUCCESS if the function succeeds, otherwise it returns one
  2970. of the CR_* errors.
  2971. Notes:
  2972. This RPC server interface is used by local RPC clients only; it is never
  2973. called remotely.
  2974. --*/
  2975. {
  2976. CONFIGRET Status = CR_SUCCESS;
  2977. RPC_STATUS rpcStatus;
  2978. DEV_BROADCAST_HDR UNALIGNED *p;
  2979. PPNP_NOTIFY_ENTRY entry = NULL;
  2980. ULONG hashValue, ulSessionId;
  2981. HANDLE hProcess = NULL, localHandle = NULL;
  2982. PPNP_NOTIFY_LIST notifyList = NULL;
  2983. BOOLEAN bLocked = FALSE, bCritSecHeld = FALSE;
  2984. //
  2985. // This routine only services requests from local RPC clients.
  2986. //
  2987. if (!IsClientLocal(hBinding)) {
  2988. return CR_ACCESS_DENIED;
  2989. }
  2990. try {
  2991. //
  2992. // Validate parameters.
  2993. //
  2994. if (!ARGUMENT_PRESENT(Context)) {
  2995. Status = CR_INVALID_POINTER;
  2996. goto Clean0;
  2997. }
  2998. *Context = NULL;
  2999. if ((!ARGUMENT_PRESENT(NotificationFilter)) ||
  3000. (!ARGUMENT_PRESENT(ClientContext)) ||
  3001. (*ClientContext == 0)) {
  3002. Status = CR_INVALID_POINTER;
  3003. goto Clean0;
  3004. }
  3005. //
  3006. // the RPC interface specifies ServiceName as a [ref] parameter,
  3007. // so it should never be NULL.
  3008. //
  3009. ASSERT(ARGUMENT_PRESENT(ServiceName));
  3010. //
  3011. // DEVICE_NOTIFY_BITS is a private mask, defined specifically for
  3012. // validation by the client and server. It contains the bitmask for all
  3013. // handle types (DEVICE_NOTIFY_COMPLETION_HANDLE specifically excluded
  3014. // below), and all other flags that are currently defined - both public
  3015. // and reserved.
  3016. //
  3017. if (INVALID_FLAGS(Flags, DEVICE_NOTIFY_BITS)) {
  3018. Status = CR_INVALID_FLAG;
  3019. goto Clean0;
  3020. }
  3021. //
  3022. // Completion handles are not currently implemented.
  3023. // DEVICE_NOTIFY_COMPLETION_HANDLE defined privately in winuserp.h,
  3024. // reserved for future use (??).
  3025. //
  3026. if ((Flags & DEVICE_NOTIFY_HANDLE_MASK) ==
  3027. DEVICE_NOTIFY_COMPLETION_HANDLE) {
  3028. Status = CR_INVALID_FLAG;
  3029. goto Clean0;
  3030. }
  3031. //
  3032. // Make sure the Notification filter is a valid size.
  3033. //
  3034. if ((ulSize < sizeof(DEV_BROADCAST_HDR)) ||
  3035. (((PDEV_BROADCAST_HDR)NotificationFilter)->dbch_size < sizeof(DEV_BROADCAST_HDR))) {
  3036. Status = CR_BUFFER_SMALL;
  3037. goto Clean0;
  3038. }
  3039. ASSERT(ulSize == ((PDEV_BROADCAST_HDR)NotificationFilter)->dbch_size);
  3040. //
  3041. // Impersonate the client and retrieve the SessionId.
  3042. //
  3043. rpcStatus = RpcImpersonateClient(hBinding);
  3044. if (rpcStatus != RPC_S_OK) {
  3045. KdPrintEx((DPFLTR_PNPMGR_ID,
  3046. DBGF_ERRORS,
  3047. "UMPNPMGR: RpcImpersonateClient failed, error = %d\n",
  3048. rpcStatus));
  3049. Status = CR_FAILURE;
  3050. goto Clean0;
  3051. }
  3052. ulSessionId = GetClientLogonId();
  3053. rpcStatus = RpcRevertToSelf();
  3054. if (rpcStatus != RPC_S_OK) {
  3055. KdPrintEx((DPFLTR_PNPMGR_ID,
  3056. DBGF_ERRORS,
  3057. "UMPNPMGR: RpcRevertToSelf failed, error = %d\n",
  3058. rpcStatus));
  3059. ASSERT(rpcStatus == RPC_S_OK);
  3060. }
  3061. //
  3062. // Handle the different types of notification filters.
  3063. //
  3064. p = (PDEV_BROADCAST_HDR)NotificationFilter;
  3065. switch (p->dbch_devicetype) {
  3066. case DBT_DEVTYP_OEM:
  3067. case DBT_DEVTYP_VOLUME:
  3068. case DBT_DEVTYP_PORT:
  3069. case DBT_DEVTYP_NET:
  3070. //
  3071. // These structures are either obsolete, or used for broadcast-only
  3072. // notifications.
  3073. //
  3074. Status = CR_INVALID_DATA;
  3075. break;
  3076. case DBT_DEVTYP_HANDLE: {
  3077. //
  3078. // DEV_BROADCAST_HANDLE based notification.
  3079. //
  3080. DEV_BROADCAST_HANDLE UNALIGNED *filter = (PDEV_BROADCAST_HANDLE)NotificationFilter;
  3081. PLUGPLAY_CONTROL_TARGET_RELATION_DATA controlData;
  3082. NTSTATUS ntStatus;
  3083. #ifdef _WIN64
  3084. DEV_BROADCAST_HANDLE64 UNALIGNED filter64;
  3085. //
  3086. // Check if the client is running on WOW64.
  3087. //
  3088. if (Flags & DEVICE_NOTIFY_WOW64_CLIENT) {
  3089. //
  3090. // Convert the 32-bit DEV_BROADCAST_HANDLE notification filter
  3091. // to 64-bit.
  3092. //
  3093. DEV_BROADCAST_HANDLE32 UNALIGNED *filter32 = (PDEV_BROADCAST_HANDLE32)NotificationFilter;
  3094. //
  3095. // Validate the 32-bit input filter data
  3096. //
  3097. ASSERT(filter32->dbch_size >= sizeof(DEV_BROADCAST_HANDLE32));
  3098. if (filter32->dbch_size < sizeof(DEV_BROADCAST_HANDLE32) ||
  3099. ulSize < sizeof(DEV_BROADCAST_HANDLE32)) {
  3100. Status = CR_INVALID_DATA;
  3101. goto Clean0;
  3102. }
  3103. memset(&filter64, 0, sizeof(DEV_BROADCAST_HANDLE64));
  3104. filter64.dbch_size = sizeof(DEV_BROADCAST_HANDLE64);
  3105. filter64.dbch_devicetype = DBT_DEVTYP_HANDLE;
  3106. filter64.dbch_handle = (ULONG64)filter32->dbch_handle;
  3107. //
  3108. // use the converted 64-bit filter and size from now on, instead
  3109. // of the caller supplied 32-bit filter.
  3110. //
  3111. filter = (PDEV_BROADCAST_HANDLE)&filter64;
  3112. ulSize = sizeof(DEV_BROADCAST_HANDLE64);
  3113. }
  3114. #endif // _WIN64
  3115. //
  3116. // Validate the input filter data
  3117. //
  3118. if (filter->dbch_size < sizeof(DEV_BROADCAST_HANDLE) ||
  3119. ulSize < sizeof(DEV_BROADCAST_HANDLE)) {
  3120. Status = CR_INVALID_DATA;
  3121. goto Clean0;
  3122. }
  3123. //
  3124. // The DEVICE_NOTIFY_INCLUDE_ALL_INTERFACE_CLASSES flag is only
  3125. // valid for the DBT_DEVTYP_DEVICEINTERFACE notification filter
  3126. // type.
  3127. //
  3128. if ((Flags & DEVICE_NOTIFY_PROPERTY_MASK) &
  3129. DEVICE_NOTIFY_ALL_INTERFACE_CLASSES) {
  3130. Status = CR_INVALID_FLAG;
  3131. goto Clean0;
  3132. }
  3133. entry = HeapAlloc(ghPnPHeap, 0, sizeof(PNP_NOTIFY_ENTRY));
  3134. if (entry == NULL) {
  3135. Status = CR_OUT_OF_MEMORY;
  3136. goto Clean0;
  3137. }
  3138. //
  3139. // Find the device id that corresponds to this file handle.
  3140. // In this case, use a duplicated instance of the file handle
  3141. // for this process, not the caller's process.
  3142. //
  3143. rpcStatus = RpcImpersonateClient(hBinding);
  3144. if (rpcStatus != RPC_S_OK) {
  3145. KdPrintEx((DPFLTR_PNPMGR_ID,
  3146. DBGF_ERRORS,
  3147. "UMPNPMGR: RpcImpersonateClient failed, error = %d\n",
  3148. rpcStatus));
  3149. Status = CR_FAILURE;
  3150. HeapFree(ghPnPHeap, 0, entry);
  3151. goto Clean0;
  3152. }
  3153. hProcess = OpenProcess(PROCESS_DUP_HANDLE, FALSE, ProcessId);
  3154. if (hProcess == NULL) {
  3155. //
  3156. // Last error set by OpenProcess routine
  3157. //
  3158. Status = CR_FAILURE;
  3159. HeapFree(ghPnPHeap, 0, entry);
  3160. rpcStatus = RpcRevertToSelf();
  3161. if (rpcStatus != RPC_S_OK) {
  3162. KdPrintEx((DPFLTR_PNPMGR_ID,
  3163. DBGF_ERRORS,
  3164. "UMPNPMGR: RpcRevertToSelf failed, error = %d\n",
  3165. rpcStatus));
  3166. ASSERT(rpcStatus == RPC_S_OK);
  3167. }
  3168. goto Clean0;
  3169. }
  3170. if (!DuplicateHandle(hProcess,
  3171. (HANDLE)filter->dbch_handle,
  3172. GetCurrentProcess(),
  3173. &localHandle,
  3174. 0,
  3175. FALSE,
  3176. DUPLICATE_SAME_ACCESS)) {
  3177. //
  3178. // Last error set by DuplicateHandle routine
  3179. //
  3180. Status = CR_FAILURE;
  3181. HeapFree(ghPnPHeap, 0, entry);
  3182. CloseHandle(hProcess);
  3183. rpcStatus = RpcRevertToSelf();
  3184. if (rpcStatus != RPC_S_OK) {
  3185. KdPrintEx((DPFLTR_PNPMGR_ID,
  3186. DBGF_ERRORS,
  3187. "UMPNPMGR: RpcRevertToSelf failed, error = %d\n",
  3188. rpcStatus));
  3189. ASSERT(rpcStatus == RPC_S_OK);
  3190. }
  3191. goto Clean0;
  3192. }
  3193. rpcStatus = RpcRevertToSelf();
  3194. if (rpcStatus != RPC_S_OK) {
  3195. KdPrintEx((DPFLTR_PNPMGR_ID,
  3196. DBGF_ERRORS,
  3197. "UMPNPMGR: RpcRevertToSelf failed, error = %d\n",
  3198. rpcStatus));
  3199. ASSERT(rpcStatus == RPC_S_OK);
  3200. }
  3201. memset(&controlData, 0 , sizeof(PLUGPLAY_CONTROL_TARGET_RELATION_DATA));
  3202. controlData.UserFileHandle = localHandle;
  3203. controlData.DeviceInstance = entry->u.Target.DeviceId;
  3204. controlData.DeviceInstanceLen = sizeof(entry->u.Target.DeviceId);
  3205. ntStatus = NtPlugPlayControl(PlugPlayControlTargetDeviceRelation,
  3206. &controlData,
  3207. sizeof(controlData));
  3208. CloseHandle(localHandle);
  3209. CloseHandle(hProcess);
  3210. if (!NT_SUCCESS(ntStatus)) {
  3211. Status = MapNtStatusToCmError(ntStatus);
  3212. HeapFree(ghPnPHeap, 0, entry);
  3213. goto Clean0;
  3214. }
  3215. //
  3216. // Sanitize the device id
  3217. //
  3218. FixUpDeviceId(entry->u.Target.DeviceId);
  3219. //
  3220. // Copy the client name for the window or service, supplied by
  3221. // ServiceName. The maximum service name buffer length required for
  3222. // services is MAX_SERVICE_NAME_LEN (256 characters), which should
  3223. // be a reasonable limit for both.
  3224. //
  3225. if (ARGUMENT_PRESENT(ServiceName)) {
  3226. HRESULT hr;
  3227. size_t ServiceNameLen = 0;
  3228. hr = StringCchLength(ServiceName,
  3229. MAX_SERVICE_NAME_LEN,
  3230. &ServiceNameLen);
  3231. if (FAILED(hr)) {
  3232. ServiceNameLen = MAX_SERVICE_NAME_LEN - 1;
  3233. }
  3234. entry->ClientName =
  3235. (LPWSTR)HeapAlloc(
  3236. ghPnPHeap, 0,
  3237. (ServiceNameLen+1)*sizeof(WCHAR));
  3238. if (entry->ClientName == NULL) {
  3239. KdPrintEx((DPFLTR_PNPMGR_ID,
  3240. DBGF_WARNINGS,
  3241. "UMPNPMGR: PNP_RegisterNotification "
  3242. "failed to allocate memory for ClientName!\n"));
  3243. Status = CR_OUT_OF_MEMORY;
  3244. HeapFree (ghPnPHeap,0,entry);
  3245. goto Clean0;
  3246. }
  3247. //
  3248. // Copy to the allocated buffer, truncating if necessary.
  3249. //
  3250. hr = StringCchCopy(entry->ClientName,
  3251. ServiceNameLen + 1,
  3252. ServiceName);
  3253. ASSERT(SUCCEEDED(hr));
  3254. } else {
  3255. entry->ClientName = NULL;
  3256. }
  3257. //
  3258. // Resolve the service status handle from the supplied service name.
  3259. //
  3260. if ((Flags & DEVICE_NOTIFY_HANDLE_MASK) == DEVICE_NOTIFY_SERVICE_HANDLE) {
  3261. hRecipient = (ULONG_PTR)NULL;
  3262. if ((pSCMAuthenticate != NULL) &&
  3263. (ARGUMENT_PRESENT(ServiceName))) {
  3264. SERVICE_STATUS_HANDLE serviceHandle;
  3265. if (pSCMAuthenticate(ServiceName, &serviceHandle) == NO_ERROR) {
  3266. hRecipient = (ULONG_PTR)serviceHandle;
  3267. }
  3268. }
  3269. if (!hRecipient) {
  3270. Status = CR_INVALID_DATA;
  3271. if (entry->ClientName) {
  3272. HeapFree(ghPnPHeap, 0, entry->ClientName);
  3273. }
  3274. HeapFree(ghPnPHeap, 0, entry);
  3275. *Context = NULL;
  3276. goto Clean0;
  3277. }
  3278. }
  3279. //
  3280. // Add this entry to the target list
  3281. //
  3282. entry->Signature = TARGET_ENTRY_SIGNATURE;
  3283. entry->Handle = (HANDLE)hRecipient;
  3284. entry->Flags = Flags;
  3285. entry->Unregistered = FALSE;
  3286. entry->Freed = 0;
  3287. entry->SessionId = ulSessionId;
  3288. //
  3289. // Save the caller's file handle (to pass back to caller
  3290. // during notification).
  3291. //
  3292. entry->u.Target.FileHandle = filter->dbch_handle;
  3293. EnterCriticalSection(&RegistrationCS);
  3294. bCritSecHeld = TRUE;
  3295. if (gNotificationInProg != 0) {
  3296. //
  3297. // If a notification is happening, add this entry to the list of
  3298. // deferred registrations.
  3299. //
  3300. PPNP_DEFERRED_LIST regNode;
  3301. regNode = (PPNP_DEFERRED_LIST)
  3302. HeapAlloc(ghPnPHeap,
  3303. 0,
  3304. sizeof (PNP_DEFERRED_LIST));
  3305. if (!regNode) {
  3306. KdPrintEx((DPFLTR_PNPMGR_ID,
  3307. DBGF_ERRORS | DBGF_WARNINGS,
  3308. "UMPNPMGR: Error allocating deferred list entry during registration!\n"));
  3309. Status = CR_OUT_OF_MEMORY;
  3310. if (entry->ClientName) {
  3311. HeapFree(ghPnPHeap, 0, entry->ClientName);
  3312. }
  3313. HeapFree(ghPnPHeap, 0, entry);
  3314. LeaveCriticalSection(&RegistrationCS);
  3315. bCritSecHeld = FALSE;
  3316. goto Clean0;
  3317. }
  3318. //
  3319. // Do not notify this entry until after the current
  3320. // notification is finished.
  3321. //
  3322. entry->Unregistered = TRUE;
  3323. regNode->hBinding = 0;
  3324. regNode->Entry = entry;
  3325. regNode->Next = RegisterList;
  3326. RegisterList = regNode;
  3327. }
  3328. hashValue = HashString(entry->u.Target.DeviceId, TARGET_HASH_BUCKETS);
  3329. notifyList = &TargetList[hashValue];
  3330. MarkEntryWithList(entry,hashValue);
  3331. LockNotifyList(&notifyList->Lock);
  3332. bLocked = TRUE;
  3333. AddNotifyEntry(&TargetList[hashValue], entry);
  3334. entry->ClientCtxPtr = (ULONG64)*ClientContext;
  3335. *Context = entry;
  3336. UnlockNotifyList(&notifyList->Lock);
  3337. bLocked = FALSE;
  3338. LeaveCriticalSection(&RegistrationCS);
  3339. bCritSecHeld = FALSE;
  3340. break;
  3341. }
  3342. case DBT_DEVTYP_DEVICEINTERFACE: {
  3343. DEV_BROADCAST_DEVICEINTERFACE UNALIGNED *filter = (PDEV_BROADCAST_DEVICEINTERFACE)NotificationFilter;
  3344. //
  3345. // Validate the input filter data
  3346. //
  3347. if (filter->dbcc_size < sizeof(DEV_BROADCAST_DEVICEINTERFACE) ||
  3348. ulSize < sizeof (DEV_BROADCAST_DEVICEINTERFACE) ) {
  3349. Status = CR_INVALID_DATA;
  3350. goto Clean0;
  3351. }
  3352. //
  3353. // We no longer support the private GUID_DEVNODE_CHANGE interface so return
  3354. // CR_INVALID_DATA if this GUID is passed in.
  3355. //
  3356. if (GuidEqual(&GUID_DEVNODE_CHANGE, &filter->dbcc_classguid)) {
  3357. KdPrintEx((DPFLTR_PNPMGR_ID,
  3358. DBGF_WARNINGS,
  3359. "UMPNPMGR: RegisterDeviceNotification using GUID_DEVNODE_CHANGE is not supported!\n"));
  3360. Status = CR_INVALID_DATA;
  3361. goto Clean0;
  3362. }
  3363. //
  3364. // The GUID_DEVINTERFACE_INCLUDE_ALL_INTERFACE_CLASSES interface is
  3365. // not supported directly. It is for internal use only. Return
  3366. // CR_INVALID_DATA if this GUID is passed in.
  3367. //
  3368. if (GuidEqual(&GUID_DEVINTERFACE_INCLUDE_ALL_INTERFACE_CLASSES,
  3369. &filter->dbcc_classguid)) {
  3370. KdPrintEx((DPFLTR_PNPMGR_ID,
  3371. DBGF_WARNINGS,
  3372. "UMPNPMGR: RegisterDeviceNotification using this class GUID is not supported!\n"));
  3373. Status = CR_INVALID_DATA;
  3374. goto Clean0;
  3375. }
  3376. entry = HeapAlloc(ghPnPHeap, 0, sizeof(PNP_NOTIFY_ENTRY));
  3377. if (entry == NULL) {
  3378. Status = CR_OUT_OF_MEMORY;
  3379. goto Clean0;
  3380. }
  3381. //
  3382. // Copy the client name for the window or service, supplied by
  3383. // ServiceName. The maximum service name buffer length required for
  3384. // services is MAX_SERVICE_NAME_LEN (256 characters), which should
  3385. // be a reasonable limit for both.
  3386. //
  3387. if (ARGUMENT_PRESENT(ServiceName)) {
  3388. HRESULT hr;
  3389. size_t ServiceNameLen = 0;
  3390. hr = StringCchLength(ServiceName,
  3391. MAX_SERVICE_NAME_LEN,
  3392. &ServiceNameLen);
  3393. if (FAILED(hr)) {
  3394. ServiceNameLen = MAX_SERVICE_NAME_LEN - 1;
  3395. }
  3396. entry->ClientName =
  3397. (LPWSTR)HeapAlloc(
  3398. ghPnPHeap, 0,
  3399. (ServiceNameLen+1)*sizeof(WCHAR));
  3400. if (entry->ClientName == NULL) {
  3401. KdPrintEx((DPFLTR_PNPMGR_ID,
  3402. DBGF_WARNINGS,
  3403. "UMPNPMGR: PNP_RegisterNotification "
  3404. "failed to allocate memory for ClientName!\n"));
  3405. Status = CR_OUT_OF_MEMORY;
  3406. HeapFree (ghPnPHeap,0,entry);
  3407. goto Clean0;
  3408. }
  3409. //
  3410. // Copy to the allocated buffer, truncating if necessary.
  3411. //
  3412. hr = StringCchCopy(entry->ClientName,
  3413. ServiceNameLen + 1,
  3414. ServiceName);
  3415. ASSERT(SUCCEEDED(hr));
  3416. } else {
  3417. entry->ClientName = NULL;
  3418. }
  3419. //
  3420. // Resolve the service status handle from the supplied service name.
  3421. //
  3422. if ((Flags & DEVICE_NOTIFY_HANDLE_MASK) == DEVICE_NOTIFY_SERVICE_HANDLE) {
  3423. hRecipient = (ULONG_PTR)NULL;
  3424. if (pSCMAuthenticate && ServiceName) {
  3425. SERVICE_STATUS_HANDLE serviceHandle;
  3426. if (pSCMAuthenticate(ServiceName, &serviceHandle) == NO_ERROR) {
  3427. hRecipient = (ULONG_PTR)serviceHandle;
  3428. }
  3429. }
  3430. if (!hRecipient) {
  3431. Status = CR_INVALID_DATA;
  3432. if (entry->ClientName) {
  3433. HeapFree(ghPnPHeap, 0, entry->ClientName);
  3434. }
  3435. HeapFree(ghPnPHeap, 0, entry);
  3436. *Context = NULL;
  3437. goto Clean0;
  3438. }
  3439. }
  3440. //
  3441. // Add this entry to the class list
  3442. //
  3443. entry->Signature = CLASS_ENTRY_SIGNATURE;
  3444. entry->Handle = (HANDLE)hRecipient;
  3445. entry->Flags = Flags;
  3446. entry->Unregistered = FALSE;
  3447. entry->Freed = 0;
  3448. entry->SessionId = ulSessionId;
  3449. //
  3450. // If the caller is registering for all interface class events,
  3451. // ignore the caller supplied class GUID and use a private GUID.
  3452. // Otherwise, copy the caller supplied GUID to the notification list
  3453. // entry.
  3454. //
  3455. if ((Flags & DEVICE_NOTIFY_PROPERTY_MASK) &
  3456. DEVICE_NOTIFY_ALL_INTERFACE_CLASSES) {
  3457. memcpy(&entry->u.Class.ClassGuid,
  3458. &GUID_DEVINTERFACE_INCLUDE_ALL_INTERFACE_CLASSES,
  3459. sizeof(GUID));
  3460. } else {
  3461. memcpy(&entry->u.Class.ClassGuid,
  3462. &filter->dbcc_classguid,
  3463. sizeof(GUID));
  3464. }
  3465. EnterCriticalSection(&RegistrationCS);
  3466. bCritSecHeld = TRUE;
  3467. if (gNotificationInProg != 0) {
  3468. //
  3469. // If a notification is happening, add this entry to the list of
  3470. // deferred registrations.
  3471. //
  3472. PPNP_DEFERRED_LIST regNode;
  3473. regNode = (PPNP_DEFERRED_LIST)
  3474. HeapAlloc(ghPnPHeap,
  3475. 0,
  3476. sizeof (PNP_DEFERRED_LIST));
  3477. if (!regNode) {
  3478. KdPrintEx((DPFLTR_PNPMGR_ID,
  3479. DBGF_ERRORS | DBGF_WARNINGS,
  3480. "UMPNPMGR: Error allocating deferred list entry during registration!\n"));
  3481. Status = CR_OUT_OF_MEMORY;
  3482. if (entry->ClientName) {
  3483. HeapFree(ghPnPHeap, 0, entry->ClientName);
  3484. }
  3485. HeapFree(ghPnPHeap, 0, entry);
  3486. LeaveCriticalSection(&RegistrationCS);
  3487. bCritSecHeld = FALSE;
  3488. goto Clean0;
  3489. }
  3490. //
  3491. // Do not notify this entry until after the current
  3492. // notification is finished.
  3493. //
  3494. entry->Unregistered = TRUE;
  3495. regNode->hBinding = 0;
  3496. regNode->Entry = entry;
  3497. regNode->Next = RegisterList;
  3498. RegisterList = regNode;
  3499. }
  3500. hashValue = HashClassGuid(&entry->u.Class.ClassGuid);
  3501. notifyList = &ClassList[hashValue];
  3502. MarkEntryWithList(entry,hashValue);
  3503. LockNotifyList(&notifyList->Lock);
  3504. bLocked = TRUE;
  3505. AddNotifyEntry(&ClassList[hashValue],entry);
  3506. entry->ClientCtxPtr = (ULONG64)*ClientContext;
  3507. *Context = entry;
  3508. UnlockNotifyList(&notifyList->Lock);
  3509. bLocked = FALSE;
  3510. LeaveCriticalSection(&RegistrationCS);
  3511. bCritSecHeld = FALSE;
  3512. break;
  3513. }
  3514. default:
  3515. Status = CR_INVALID_DATA;
  3516. goto Clean0;
  3517. }
  3518. Clean0:
  3519. NOTHING;
  3520. } except(EXCEPTION_EXECUTE_HANDLER) {
  3521. KdPrintEx((DPFLTR_PNPMGR_ID,
  3522. DBGF_ERRORS | DBGF_EVENT,
  3523. "UMPNPMGR: Exception in PNP_RegisterNotification\n"));
  3524. ASSERT(0);
  3525. Status = CR_FAILURE;
  3526. if (bLocked) {
  3527. UnlockNotifyList(&notifyList->Lock);
  3528. }
  3529. if (bCritSecHeld) {
  3530. LeaveCriticalSection(&RegistrationCS);
  3531. }
  3532. }
  3533. return Status;
  3534. } // PNP_RegisterNotification
  3535. CONFIGRET
  3536. PNP_UnregisterNotification(
  3537. IN handle_t hBinding,
  3538. IN PPNP_NOTIFICATION_CONTEXT Context
  3539. )
  3540. /*++
  3541. Routine Description:
  3542. This routine is the rpc server-side of the CMP_UnregisterNotification routine.
  3543. It performs the remaining parameter validation and unregisters the
  3544. corresponding notification entry.
  3545. Arguments:
  3546. hBinding - RPC binding handle (not used).
  3547. Context - Contains the address of a HDEVNOTIFY notification handle that
  3548. was supplied when this notification request was registered.
  3549. Note that when the server context is freed, this context
  3550. handle must be set to NULL.
  3551. Return Value:
  3552. Return CR_SUCCESS if the function succeeds, otherwise it returns one
  3553. of the CR_* errors.
  3554. Notes:
  3555. Note that the Context comes in as a PNP_NOTIFICATION_CONTEXT pointer
  3556. It is NOT one of those. The case is correct. This is to work around
  3557. RPC and user.
  3558. This RPC server interface is used by local RPC clients only; it is never
  3559. called remotely.
  3560. --*/
  3561. {
  3562. CONFIGRET Status = CR_SUCCESS;
  3563. ULONG hashValue = 0;
  3564. PPNP_DEFERRED_LIST unregNode;
  3565. PPNP_NOTIFY_LIST notifyList = NULL;
  3566. BOOLEAN bLocked = FALSE;
  3567. //
  3568. // This routine only services requests from local RPC clients.
  3569. //
  3570. if (!IsClientLocal(hBinding)) {
  3571. return CR_ACCESS_DENIED;
  3572. }
  3573. try {
  3574. //
  3575. // validate notification handle
  3576. //
  3577. PPNP_NOTIFY_ENTRY entry = (PPNP_NOTIFY_ENTRY)*Context;
  3578. EnterCriticalSection (&RegistrationCS);
  3579. if (entry == NULL) {
  3580. Status = CR_INVALID_DATA;
  3581. goto Clean0;
  3582. }
  3583. if (gNotificationInProg != 0) {
  3584. if (RegisterList) {
  3585. //
  3586. // Check to see if this entry is in the deferred RegisterList.
  3587. //
  3588. PPNP_DEFERRED_LIST currReg,prevReg;
  3589. currReg = RegisterList;
  3590. prevReg = NULL;
  3591. while (currReg) {
  3592. //
  3593. // Entries in the deferred RegisterList are to be skipped
  3594. // during notification.
  3595. //
  3596. ASSERT(currReg->Entry->Unregistered);
  3597. if (currReg->Entry == entry) {
  3598. //
  3599. // Remove this entry from the deferred RegisterList.
  3600. //
  3601. if (prevReg) {
  3602. prevReg->Next = currReg->Next;
  3603. } else {
  3604. RegisterList = currReg->Next;
  3605. }
  3606. HeapFree(ghPnPHeap, 0, currReg);
  3607. if (prevReg) {
  3608. currReg = prevReg->Next;
  3609. } else {
  3610. currReg = RegisterList;
  3611. }
  3612. } else {
  3613. prevReg = currReg;
  3614. currReg = currReg->Next;
  3615. }
  3616. }
  3617. }
  3618. switch (entry->Signature & LIST_ENTRY_SIGNATURE_MASK) {
  3619. case CLASS_ENTRY_SIGNATURE:
  3620. case TARGET_ENTRY_SIGNATURE: {
  3621. unregNode = (PPNP_DEFERRED_LIST)
  3622. HeapAlloc(ghPnPHeap,
  3623. 0,
  3624. sizeof (PNP_DEFERRED_LIST));
  3625. if (!unregNode) {
  3626. KdPrintEx((DPFLTR_PNPMGR_ID,
  3627. DBGF_ERRORS | DBGF_WARNINGS,
  3628. "UMPNPMGR: Error allocating deferred list entry during unregistration!\n"));
  3629. Status = CR_OUT_OF_MEMORY;
  3630. goto Clean0;
  3631. }
  3632. //
  3633. // This param is not used, if this changes, change this line too.
  3634. //
  3635. unregNode->hBinding= 0;
  3636. notifyList = GetNotifyListForEntry(entry);
  3637. if (notifyList) {
  3638. //
  3639. // The entry is part of a notification list, so make
  3640. // sure not to notify on it.
  3641. //
  3642. LockNotifyList(&notifyList->Lock);
  3643. bLocked = TRUE;
  3644. entry->Unregistered = TRUE;
  3645. UnlockNotifyList(&notifyList->Lock);
  3646. bLocked = FALSE;
  3647. }
  3648. unregNode->Entry = entry;
  3649. unregNode->Next = UnregisterList;
  3650. UnregisterList = unregNode;
  3651. *Context = NULL;
  3652. break;
  3653. }
  3654. default:
  3655. Status = CR_INVALID_DATA;
  3656. KdPrintEx((DPFLTR_PNPMGR_ID,
  3657. DBGF_WARNINGS | DBGF_ERRORS,
  3658. "UMPNPMGR: PNP_UnregisterNotification: invalid signature on entry at %x\n",
  3659. entry));
  3660. break;
  3661. }
  3662. goto Clean0;
  3663. }
  3664. //
  3665. // Free the notification entry from the appropriate list.
  3666. //
  3667. switch (entry->Signature & LIST_ENTRY_SIGNATURE_MASK) {
  3668. case CLASS_ENTRY_SIGNATURE:
  3669. hashValue = HashClassGuid(&entry->u.Class.ClassGuid);
  3670. notifyList = &ClassList[hashValue];
  3671. LockNotifyList(&notifyList->Lock);
  3672. bLocked = TRUE;
  3673. entry->Freed |= (PNP_UNREG_FREE|PNP_UNREG_CLASS);
  3674. DeleteNotifyEntry(entry,TRUE);
  3675. UnlockNotifyList(&notifyList->Lock);
  3676. bLocked = FALSE;
  3677. *Context = NULL;
  3678. break;
  3679. case TARGET_ENTRY_SIGNATURE:
  3680. hashValue = HashString(entry->u.Target.DeviceId, TARGET_HASH_BUCKETS);
  3681. notifyList = &TargetList[hashValue];
  3682. LockNotifyList(&notifyList->Lock);
  3683. bLocked = TRUE;
  3684. entry->Freed |= (PNP_UNREG_FREE|PNP_UNREG_TARGET);
  3685. DeleteNotifyEntry(entry,TRUE);
  3686. UnlockNotifyList(&notifyList->Lock);
  3687. bLocked = FALSE;
  3688. *Context = NULL;
  3689. break;
  3690. default:
  3691. Status = CR_INVALID_DATA;
  3692. KdPrintEx((DPFLTR_PNPMGR_ID,
  3693. DBGF_WARNINGS | DBGF_ERRORS,
  3694. "UMPNPMGR: PNP_UnregisterNotification: invalid signature on entry at %x\n",
  3695. entry));
  3696. }
  3697. Clean0:
  3698. LeaveCriticalSection(&RegistrationCS);
  3699. } except(EXCEPTION_EXECUTE_HANDLER) {
  3700. KdPrintEx((DPFLTR_PNPMGR_ID,
  3701. DBGF_ERRORS | DBGF_EVENT,
  3702. "UMPNPMGR: PNP_UnregisterNotification caused an exception!\n"));
  3703. ASSERT(0);
  3704. SetLastError(ERROR_EXCEPTION_IN_SERVICE);
  3705. Status = CR_FAILURE;
  3706. if (bLocked) {
  3707. UnlockNotifyList(&notifyList->Lock);
  3708. }
  3709. LeaveCriticalSection(&RegistrationCS);
  3710. }
  3711. return Status;
  3712. } // PNP_UnregisterNotification
  3713. //-----------------------------------------------------------------------------
  3714. // Dynamic Event Notification Support
  3715. //-----------------------------------------------------------------------------
  3716. DWORD
  3717. ThreadProc_DeviceEvent(
  3718. LPDWORD lpParam
  3719. )
  3720. /*++
  3721. Routine Description:
  3722. This routine is a thread procedure. This thread handles all device event
  3723. notification from kernel-mode.
  3724. Arguments:
  3725. lpParam - Not used.
  3726. Return Value:
  3727. Currently returns TRUE/FALSE.
  3728. --*/
  3729. {
  3730. DWORD status = TRUE, result = 0;
  3731. NTSTATUS ntStatus = STATUS_SUCCESS;
  3732. PPLUGPLAY_EVENT_BLOCK eventBlock = NULL;
  3733. ULONG totalSize, variableSize;
  3734. BOOL notDone = TRUE;
  3735. PVOID p = NULL;
  3736. PNP_VETO_TYPE vetoType;
  3737. WCHAR vetoName[MAX_VETO_NAME_LENGTH];
  3738. ULONG vetoNameLength;
  3739. PLUGPLAY_CONTROL_USER_RESPONSE_DATA userResponse;
  3740. PPNP_NOTIFY_LIST notifyList = NULL;
  3741. PPNP_DEFERRED_LIST reg,regFree,unreg,unregFree,rundown,rundownFree;
  3742. UNREFERENCED_PARAMETER(lpParam);
  3743. try {
  3744. //
  3745. // Initialize event buffer used to pass info back from kernel-mode in.
  3746. //
  3747. variableSize = 4096 - sizeof(PLUGPLAY_EVENT_BLOCK);
  3748. totalSize = sizeof(PLUGPLAY_EVENT_BLOCK) + variableSize;
  3749. eventBlock = (PPLUGPLAY_EVENT_BLOCK)HeapAlloc(ghPnPHeap, 0, totalSize);
  3750. if (eventBlock == NULL) {
  3751. LogErrorEvent(ERR_ALLOCATING_EVENT_BLOCK, ERROR_NOT_ENOUGH_MEMORY, 0);
  3752. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  3753. status = FALSE;
  3754. ASSERT(0);
  3755. goto Clean0;
  3756. }
  3757. //
  3758. // Retrieve device events synchronously (this is more efficient
  3759. // than using apcs).
  3760. //
  3761. while (notDone) {
  3762. ntStatus = NtGetPlugPlayEvent(NULL,
  3763. NULL, // Context
  3764. eventBlock,
  3765. totalSize);
  3766. if (ntStatus == STATUS_BUFFER_TOO_SMALL) {
  3767. //
  3768. // Kernel-mode side couldn't transfer the event because
  3769. // my buffer is too small, realloc and attempt to retrieve
  3770. // the event again.
  3771. //
  3772. variableSize += 1024;
  3773. totalSize = variableSize + sizeof(PLUGPLAY_EVENT_BLOCK);
  3774. p = HeapReAlloc(ghPnPHeap, 0, eventBlock, totalSize);
  3775. if (p == NULL) {
  3776. KdPrintEx((DPFLTR_PNPMGR_ID,
  3777. DBGF_ERRORS,
  3778. "UMPNPMGR: Couldn't reallocate event block to size %d\n",
  3779. totalSize));
  3780. LogErrorEvent(ERR_ALLOCATING_EVENT_BLOCK, ERROR_NOT_ENOUGH_MEMORY, 0);
  3781. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  3782. status = FALSE;
  3783. ASSERT(0);
  3784. goto Clean0;
  3785. }
  3786. eventBlock = (PPLUGPLAY_EVENT_BLOCK)p;
  3787. }
  3788. if (ntStatus == STATUS_SUCCESS) {
  3789. //
  3790. // An event was retrieved, process it.
  3791. //
  3792. gNotificationInProg = 1;
  3793. vetoType = PNP_VetoTypeUnknown;
  3794. vetoName[0] = L'\0';
  3795. vetoNameLength = MAX_VETO_NAME_LENGTH;
  3796. try {
  3797. //
  3798. // Process the device event.
  3799. //
  3800. result = ProcessDeviceEvent(eventBlock,
  3801. totalSize,
  3802. &vetoType,
  3803. vetoName,
  3804. &vetoNameLength);
  3805. } except(EXCEPTION_EXECUTE_HANDLER) {
  3806. KdPrintEx((DPFLTR_PNPMGR_ID,
  3807. DBGF_ERRORS | DBGF_EVENT,
  3808. "UMPNPMGR: Exception in ProcessDeviceEvent!\n"));
  3809. ASSERT(0);
  3810. //
  3811. // An exception while processing the event should not be
  3812. // considered a failure of the event itself.
  3813. //
  3814. result = TRUE;
  3815. vetoType = PNP_VetoTypeUnknown;
  3816. vetoName[0] = L'\0';
  3817. vetoNameLength = 0;
  3818. }
  3819. ASSERT(vetoNameLength < MAX_VETO_NAME_LENGTH &&
  3820. vetoName[vetoNameLength] == L'\0');
  3821. //
  3822. // Notify kernel-mode of the user-mode result.
  3823. //
  3824. userResponse.Response = result;
  3825. userResponse.VetoType = vetoType;
  3826. userResponse.VetoName = vetoName;
  3827. userResponse.VetoNameLength = vetoNameLength;
  3828. NtPlugPlayControl(PlugPlayControlUserResponse,
  3829. &userResponse,
  3830. sizeof(userResponse));
  3831. EnterCriticalSection (&RegistrationCS);
  3832. if (RegisterList != NULL) {
  3833. //
  3834. // Complete Registrations requested during notification.
  3835. //
  3836. reg = RegisterList;
  3837. RegisterList=NULL;
  3838. } else {
  3839. reg = NULL;
  3840. }
  3841. if (UnregisterList != NULL) {
  3842. //
  3843. // Complete Unregistrations requested during notification.
  3844. //
  3845. unreg = UnregisterList;
  3846. UnregisterList = NULL;
  3847. } else {
  3848. unreg = NULL;
  3849. }
  3850. if (RundownList != NULL) {
  3851. //
  3852. // Complete Unregistrations requested during notification.
  3853. //
  3854. rundown = RundownList;
  3855. RundownList = NULL;
  3856. } else {
  3857. rundown = NULL;
  3858. }
  3859. gNotificationInProg = 0;
  3860. while (reg) {
  3861. //
  3862. // This entry has already been added to the appropriate
  3863. // notification list. Allow this entry to receive
  3864. // notifications.
  3865. //
  3866. notifyList = GetNotifyListForEntry(reg->Entry);
  3867. ASSERT(notifyList);
  3868. if (notifyList) {
  3869. LockNotifyList(&notifyList->Lock);
  3870. }
  3871. reg->Entry->Unregistered = FALSE;
  3872. if (notifyList) {
  3873. UnlockNotifyList(&notifyList->Lock);
  3874. }
  3875. //
  3876. // Remove the entry from the deferred registration list.
  3877. //
  3878. regFree = reg;
  3879. reg = reg->Next;
  3880. HeapFree(ghPnPHeap, 0, regFree);
  3881. }
  3882. while (unreg) {
  3883. PNP_UnregisterNotification(unreg->hBinding,&unreg->Entry);
  3884. //
  3885. // Remove the entry from the deferred unregistration list.
  3886. //
  3887. unregFree = unreg;
  3888. unreg = unreg->Next;
  3889. HeapFree(ghPnPHeap, 0, unregFree);
  3890. }
  3891. while (rundown) {
  3892. PNP_NOTIFICATION_CONTEXT_rundown(rundown->Entry);
  3893. //
  3894. // Remove the entry from the deferred rundown list.
  3895. //
  3896. rundownFree = rundown;
  3897. rundown = rundown->Next;
  3898. HeapFree(ghPnPHeap, 0, rundownFree);
  3899. }
  3900. LeaveCriticalSection(&RegistrationCS);
  3901. }
  3902. if (ntStatus == STATUS_NOT_IMPLEMENTED) {
  3903. KdPrintEx((DPFLTR_PNPMGR_ID,
  3904. DBGF_ERRORS,
  3905. "UMPNPMGR: NtGetPlugPlayEvent returned STATUS_NOT_IMPLEMENTED\n"));
  3906. ASSERT(FALSE);
  3907. }
  3908. if (ntStatus == STATUS_USER_APC) {
  3909. KdPrintEx((DPFLTR_PNPMGR_ID,
  3910. DBGF_ERRORS,
  3911. "UMPNPMGR: ThreadProc_DeviceEvent exiting on STATUS_USER_APC\n"));
  3912. ASSERT(FALSE);
  3913. }
  3914. }
  3915. Clean0:
  3916. NOTHING;
  3917. } except(EXCEPTION_EXECUTE_HANDLER) {
  3918. KdPrintEx((DPFLTR_PNPMGR_ID,
  3919. DBGF_ERRORS | DBGF_EVENT,
  3920. "UMPNPMGR: Exception in ThreadProc_DeviceEvent!\n"));
  3921. ASSERT(0);
  3922. status = FALSE;
  3923. //
  3924. // Reference the following variable so the compiler will respect
  3925. // statement ordering w.r.t. its assignment.
  3926. //
  3927. eventBlock = eventBlock;
  3928. }
  3929. KdPrintEx((DPFLTR_PNPMGR_ID,
  3930. DBGF_ERRORS | DBGF_EVENT,
  3931. "UMPNPMGR: Exiting ThreadProc_DeviceEvent!!!!\n"));
  3932. TermNotification();
  3933. if (eventBlock != NULL) {
  3934. HeapFree(ghPnPHeap, 0, eventBlock);
  3935. }
  3936. return status;
  3937. } // ThreadProc_DeviceEvent
  3938. BOOL
  3939. InitNotification(
  3940. VOID
  3941. )
  3942. /*++
  3943. Routine Description:
  3944. This routine allocates and initializes notification lists, etc.
  3945. Arguments:
  3946. Not used.
  3947. Return Value:
  3948. Currently returns TRUE/FALSE.
  3949. --*/
  3950. {
  3951. ULONG i;
  3952. //
  3953. // Initialize the interface device (class) list
  3954. //
  3955. memset(ClassList, 0, sizeof(PNP_NOTIFY_LIST) * CLASS_GUID_HASH_BUCKETS);
  3956. for (i = 0; i < CLASS_GUID_HASH_BUCKETS; i++) {
  3957. ClassList[i].Next = NULL;
  3958. ClassList[i].Previous = NULL;
  3959. InitPrivateResource(&ClassList[i].Lock);
  3960. }
  3961. //
  3962. // Initialize the target device list
  3963. //
  3964. memset(TargetList, 0, sizeof(PNP_NOTIFY_LIST) * TARGET_HASH_BUCKETS);
  3965. for (i = 0; i < TARGET_HASH_BUCKETS; i++) {
  3966. TargetList[i].Next = NULL;
  3967. TargetList[i].Previous = NULL;
  3968. InitPrivateResource(&TargetList[i].Lock);
  3969. }
  3970. //
  3971. // Initialize the install list
  3972. //
  3973. InstallList.Next = NULL;
  3974. InitPrivateResource(&InstallList.Lock);
  3975. //
  3976. // Initialize the install client list
  3977. //
  3978. InstallClientList.Next = NULL;
  3979. InitPrivateResource(&InstallClientList.Lock);
  3980. //
  3981. // Initialize the lock for user token access
  3982. //
  3983. InitPrivateResource(&gTokenLock);
  3984. //
  3985. // Initialize the service handle list
  3986. //
  3987. memset(ServiceList, 0, sizeof(PNP_NOTIFY_LIST) * SERVICE_NUM_CONTROLS);
  3988. for (i = 0; i < SERVICE_NUM_CONTROLS; i++) {
  3989. ServiceList[i].Next = NULL;
  3990. ServiceList[i].Previous = NULL;
  3991. InitPrivateResource(&ServiceList[i].Lock);
  3992. }
  3993. //
  3994. // Initialize Registration/Unregistration CS.
  3995. //
  3996. try {
  3997. InitializeCriticalSection(&RegistrationCS);
  3998. } except(EXCEPTION_EXECUTE_HANDLER) {
  3999. return FALSE;
  4000. }
  4001. //
  4002. // Initialize deferred Registration/Unregistration lists.
  4003. //
  4004. RegisterList = NULL;
  4005. UnregisterList = NULL;
  4006. RundownList = NULL;
  4007. //
  4008. // Initialize gNotificationInProg flag.
  4009. //
  4010. gNotificationInProg = 0;
  4011. return TRUE;
  4012. } // InitNotification
  4013. VOID
  4014. TermNotification(
  4015. VOID
  4016. )
  4017. /*++
  4018. Routine Description:
  4019. This routine frees notification resources.
  4020. Arguments:
  4021. Not used.
  4022. Return Value:
  4023. No return.
  4024. --*/
  4025. {
  4026. ULONG i;
  4027. //
  4028. // Free the interface device (class) list locks
  4029. //
  4030. for (i = 0; i < CLASS_GUID_HASH_BUCKETS; i++) {
  4031. if (LockNotifyList(&ClassList[i].Lock)) {
  4032. DestroyPrivateResource(&ClassList[i].Lock);
  4033. }
  4034. }
  4035. //
  4036. // Free the target device list locks
  4037. //
  4038. for (i = 0; i < TARGET_HASH_BUCKETS; i++) {
  4039. if (LockNotifyList(&TargetList[i].Lock)) {
  4040. DestroyPrivateResource(&TargetList[i].Lock);
  4041. }
  4042. }
  4043. //
  4044. // Free the service notification list locks
  4045. //
  4046. for (i = 0; i < SERVICE_NUM_CONTROLS; i++) {
  4047. if (LockNotifyList(&ServiceList[i].Lock)) {
  4048. DestroyPrivateResource(&ServiceList[i].Lock);
  4049. }
  4050. }
  4051. //
  4052. // Free the install list lock
  4053. //
  4054. if (LockNotifyList(&InstallList.Lock)) {
  4055. DestroyPrivateResource(&InstallList.Lock);
  4056. }
  4057. //
  4058. // Free the lock for user token access
  4059. //
  4060. if (LockNotifyList(&gTokenLock)) {
  4061. DestroyPrivateResource(&gTokenLock);
  4062. }
  4063. //
  4064. // Free the install client list lock
  4065. //
  4066. if (LockNotifyList(&InstallClientList.Lock)) {
  4067. DestroyPrivateResource(&InstallClientList.Lock);
  4068. }
  4069. //
  4070. // Close the handle to winsta.dll
  4071. //
  4072. if (ghWinStaLib) {
  4073. fpWinStationSendWindowMessage = NULL;
  4074. fpWinStationBroadcastSystemMessage = NULL;
  4075. FreeLibrary(ghWinStaLib);
  4076. ghWinStaLib = NULL;
  4077. }
  4078. //
  4079. // Close the handle to wtsapi32.dll
  4080. //
  4081. if (ghWtsApi32Lib) {
  4082. fpWTSQuerySessionInformation = NULL;
  4083. fpWTSFreeMemory = NULL;
  4084. FreeLibrary(ghWtsApi32Lib);
  4085. ghWtsApi32Lib = NULL;
  4086. }
  4087. return;
  4088. } // TermNotification
  4089. ULONG
  4090. ProcessDeviceEvent(
  4091. IN PPLUGPLAY_EVENT_BLOCK EventBlock,
  4092. IN DWORD EventBufferSize,
  4093. OUT PPNP_VETO_TYPE VetoType,
  4094. OUT LPWSTR VetoName,
  4095. IN OUT PULONG VetoNameLength
  4096. )
  4097. /*++
  4098. Routine Description:
  4099. This routine processes device events recieved from the kernel-mode pnp
  4100. manager.
  4101. Arguments:
  4102. EventBlock - contains the event data.
  4103. EventBlockSize - specifies the size (in bytes) of EventBlock.
  4104. Return Value:
  4105. Returns FALSE if unsuccessful, or in the case of a vetoed query event.
  4106. Returns TRUE otherwise.
  4107. Notes:
  4108. This routine takes part in translating kernel mode PnP events into user mode
  4109. notifications. Currently, the notification code is dispersed and duplicated
  4110. throughout several routines. All notifications can be said to have the
  4111. following form though:
  4112. result = DeliverMessage(
  4113. MessageFlags, // [MSG_POST, MSG_SEND, MSG_QUERY] |
  4114. // [MSG_WALK_LIST_FORWARD, MSG_WALK_LIST_BACKWARDS]
  4115. Target, // A local window handle, hydra window handle (with
  4116. // session ID), service handle, or "broadcast".
  4117. // Better yet, it could take lists...
  4118. wParam, // DBT_* (or corresponding SERVICE_CONTROL_* message)
  4119. lParam, // Appropriate data (note: user has hardcoded knowledge
  4120. // about these via DBT_ type).
  4121. queueTimeout, // Exceeded if there exists messages in the queue but
  4122. // no message has been drained in the given time. Note
  4123. // that this means a message can fail immediately.
  4124. responseTimeout, // Exceeded if *this* message has not been processed in
  4125. // the elasped time.
  4126. VetoName, // For queries, the name of the vetoer.
  4127. VetoType // Type of vetoer component (window, service, ...)
  4128. );
  4129. DeviceEventWorker implements targeted sends and posts (normal exported Win32
  4130. API cannot be used as they won't reach other desktops). Currently User32
  4131. does not allow posts of DBT_* messages with lParam data, mainly because
  4132. a caller might send the message to itself, in which case no copy is made.
  4133. This in theory presents the caller with no opportunity to free that data
  4134. (note that this scenario would never occur with UmPnpMgr however, as we
  4135. have no WndProc). User implements this function with a fixed
  4136. responseTimeout of thirty seconds. This API can but should not be used for
  4137. broadcasts.
  4138. WinStationSendWindowMessage sends messages to windows within Hydra clients
  4139. on a machine. There is no corresponding WinStationPostWindowMessage. All
  4140. the code in this component passes a ResponseTimeout of five seconds. There
  4141. is no queueTimeout.
  4142. BroadcastSystemMessage implements broadcasts to all applications and desktops
  4143. in the non-console (ie non-Hydra) session. As with DeviceEventWorker,
  4144. User32 does not allow posts of DBT_* messages with lParam data (regardless
  4145. of whether you pass in BSF_IGNORECURRENTTASK). All code in this component
  4146. passes a ResponseTimeout of thirty seconds. QueueTimeout is optional,
  4147. fixed five seconds. ResponseTimeout cannot be specified, but the maximum
  4148. value would be five seconds per top level window. There is no information
  4149. returned on which window vetoed a query.
  4150. WinStationBroadcastSystemMessage broadcasts to all applications and desktops
  4151. on a given machine's Hydra sessions. No posts of any kind may be done
  4152. through this API. All code in this component passes a ResponseTimeout of
  4153. five seconds. QueueTimeout is an optional, fixed five seconds. There is no
  4154. information on which window vetoed a query.
  4155. ServiceControlCallback sends messages to registered services. There is no
  4156. posting or timeout facilities of any kind.
  4157. Actually, each queued registration entry should be queued with a callback.
  4158. We implement the callback, and there it hides the underlying complexities.
  4159. --*/
  4160. {
  4161. DWORD eventId, serviceControl, flags, status = TRUE;
  4162. LPWSTR p = NULL;
  4163. ULONG vetoNameSize;
  4164. ULONG ulLength, ulCustomDataLength, ulClientSessionId, ulHotplugFlags;
  4165. HRESULT hr;
  4166. UNREFERENCED_PARAMETER(EventBufferSize);
  4167. //
  4168. // Validate parameters
  4169. //
  4170. ASSERT(EventBlock->TotalSize >= sizeof(PLUGPLAY_EVENT_BLOCK));
  4171. if (EventBlock->TotalSize < sizeof(PLUGPLAY_EVENT_BLOCK)) {
  4172. return FALSE;
  4173. }
  4174. //
  4175. // Convert the event guid into a dbt style event id.
  4176. //
  4177. if (!EventIdFromEventGuid(&EventBlock->EventGuid,
  4178. &eventId,
  4179. &flags,
  4180. &serviceControl)) {
  4181. if (VetoNameLength != NULL) {
  4182. *VetoNameLength = 0;
  4183. }
  4184. return FALSE;
  4185. }
  4186. if (VetoNameLength != NULL &&
  4187. !((EventBlock->EventCategory == TargetDeviceChangeEvent) ||
  4188. (EventBlock->EventCategory == CustomDeviceEvent) ||
  4189. (EventBlock->EventCategory == HardwareProfileChangeEvent) ||
  4190. (EventBlock->EventCategory == PowerEvent) ) ){
  4191. *VetoNameLength = 0;
  4192. }
  4193. vetoNameSize = *VetoNameLength;
  4194. switch (EventBlock->EventCategory) {
  4195. case TargetDeviceChangeEvent:
  4196. case VetoEvent:
  4197. case BlockedDriverEvent:
  4198. case InvalidIDEvent:
  4199. if (IsFastUserSwitchingEnabled()) {
  4200. ulHotplugFlags = HOTPLUG_DISPLAY_ON_CONSOLE;
  4201. ulClientSessionId = INVALID_SESSION;
  4202. } else {
  4203. ulHotplugFlags = 0;
  4204. ulClientSessionId = MAIN_SESSION;
  4205. }
  4206. break;
  4207. default:
  4208. ulHotplugFlags = 0;
  4209. ulClientSessionId = INVALID_SESSION;
  4210. break;
  4211. }
  4212. //
  4213. // Notify registered callers first (class changes will also send generic
  4214. // broadcast if the type is volume or port).
  4215. //
  4216. switch (EventBlock->EventCategory) {
  4217. case CustomDeviceEvent: {
  4218. //
  4219. // Convert the pnp event block into a dbt style structure.
  4220. //
  4221. PDEV_BROADCAST_HANDLE pNotify;
  4222. PLUGPLAY_CUSTOM_NOTIFICATION *pTarget;
  4223. if (*EventBlock->u.CustomNotification.DeviceIds == L'\0') {
  4224. //
  4225. // There are no device IDs, can't do notification in this case
  4226. // just return
  4227. //
  4228. if (VetoNameLength != NULL) {
  4229. *VetoNameLength = 0;
  4230. }
  4231. KdPrintEx((DPFLTR_PNPMGR_ID,
  4232. DBGF_ERRORS,
  4233. "UMPNPMGR: Ignoring CustomDeviceEvent with no Device IDs\n"));
  4234. return FALSE;
  4235. }
  4236. //
  4237. // Custom events should always be this GUID, and that guid should always
  4238. // be converted into the below eventId.
  4239. //
  4240. ASSERT(GuidEqual(&EventBlock->EventGuid, &GUID_PNP_CUSTOM_NOTIFICATION));
  4241. ASSERT(eventId == DBT_CUSTOMEVENT);
  4242. //
  4243. // Handle and Marshall the custom notification.
  4244. //
  4245. //
  4246. // The amount of space allocated for the EventBlock + IDs is always a
  4247. // multiple of sizeof(PVOID) in order to keep the notification structure
  4248. // aligned.
  4249. //
  4250. ulLength = sizeof(PLUGPLAY_EVENT_BLOCK) + (lstrlen(EventBlock->u.CustomNotification.DeviceIds) + 1) * sizeof(WCHAR);
  4251. ulLength += sizeof(PVOID) - 1;
  4252. ulLength &= ~(sizeof(PVOID) - 1);
  4253. //
  4254. // The notification structure follows the Event Block and IDs
  4255. //
  4256. pTarget = (PPLUGPLAY_CUSTOM_NOTIFICATION)((PUCHAR)EventBlock + ulLength);
  4257. ulCustomDataLength = pTarget->HeaderInfo.Size - FIELD_OFFSET(PLUGPLAY_CUSTOM_NOTIFICATION,CustomDataBuffer);
  4258. pNotify = HeapAlloc(ghPnPHeap, 0, sizeof(DEV_BROADCAST_HANDLE) + ulCustomDataLength);
  4259. if (pNotify == NULL) {
  4260. LogErrorEvent(ERR_ALLOCATING_NOTIFICATION_STRUCTURE, ERROR_NOT_ENOUGH_MEMORY, 0);
  4261. status = FALSE;
  4262. break;
  4263. }
  4264. memset(pNotify, 0, sizeof(DEV_BROADCAST_HANDLE) + ulCustomDataLength);
  4265. pNotify->dbch_size = sizeof(DEV_BROADCAST_HANDLE) + ulCustomDataLength;
  4266. pNotify->dbch_devicetype = DBT_DEVTYP_HANDLE;
  4267. pNotify->dbch_nameoffset = pTarget->NameBufferOffset;
  4268. pNotify->dbch_eventguid = pTarget->HeaderInfo.Event;
  4269. memcpy( pNotify->dbch_data, pTarget->CustomDataBuffer, ulCustomDataLength);
  4270. *VetoNameLength = vetoNameSize;
  4271. status = NotifyTargetDeviceChange( serviceControl,
  4272. eventId,
  4273. flags,
  4274. pNotify,
  4275. EventBlock->u.CustomNotification.DeviceIds,
  4276. VetoType,
  4277. VetoName,
  4278. VetoNameLength);
  4279. if (GuidEqual(&pNotify->dbch_eventguid, (LPGUID)&GUID_IO_VOLUME_NAME_CHANGE)) {
  4280. //
  4281. // Broadcast compatible volume removal and arrival notifications
  4282. // (if any) after the custom name change event has been sent to
  4283. // all recipients.
  4284. //
  4285. BroadcastVolumeNameChange();
  4286. }
  4287. HeapFree(ghPnPHeap, 0, pNotify);
  4288. break;
  4289. }
  4290. case TargetDeviceChangeEvent: {
  4291. //
  4292. // Convert the pnp event block into a dbt style structure.
  4293. //
  4294. PDEV_BROADCAST_HANDLE pNotify;
  4295. if (*EventBlock->u.TargetDevice.DeviceIds == L'\0') {
  4296. //
  4297. // There are no device IDs, can't do notification in this case
  4298. // just return
  4299. //
  4300. if (VetoNameLength != NULL) {
  4301. *VetoNameLength = 0;
  4302. }
  4303. KdPrintEx((DPFLTR_PNPMGR_ID,
  4304. DBGF_ERRORS,
  4305. "UMPNPMGR: Ignoring TargetDeviceChangeEvent with no Device IDs\n"));
  4306. return FALSE;
  4307. }
  4308. //
  4309. // If this is a surprise removal event then call HOTPLUG.DLL to display
  4310. // a warning to the user before sending this event to other apps.
  4311. //
  4312. if (GuidEqual(&EventBlock->EventGuid,&GUID_DEVICE_SAFE_REMOVAL)) {
  4313. SendHotplugNotification(
  4314. &EventBlock->EventGuid,
  4315. NULL,
  4316. EventBlock->u.TargetDevice.DeviceIds,
  4317. &ulClientSessionId,
  4318. ulHotplugFlags
  4319. );
  4320. } else if (GuidEqual(&EventBlock->EventGuid, &GUID_DEVICE_KERNEL_INITIATED_EJECT)) {
  4321. *VetoNameLength = vetoNameSize;
  4322. status = CheckEjectPermissions(
  4323. EventBlock->u.TargetDevice.DeviceIds,
  4324. VetoType,
  4325. VetoName,
  4326. VetoNameLength
  4327. );
  4328. } else if (GuidEqual(&EventBlock->EventGuid,&GUID_DEVICE_SURPRISE_REMOVAL)) {
  4329. LogSurpriseRemovalEvent(EventBlock->u.TargetDevice.DeviceIds);
  4330. #if 0 // We don't display surpise-removal bubbles anymore...
  4331. SendHotplugNotification(
  4332. &EventBlock->EventGuid,
  4333. NULL,
  4334. EventBlock->u.TargetDevice.DeviceIds,
  4335. &ulClientSessionId,
  4336. ulHotplugFlags
  4337. );
  4338. #endif
  4339. }
  4340. if (eventId == 0) {
  4341. //
  4342. // Internal event, no broadcasting should be done.
  4343. //
  4344. if (VetoNameLength != NULL) {
  4345. *VetoNameLength = 0;
  4346. }
  4347. break;
  4348. }
  4349. pNotify = HeapAlloc(ghPnPHeap, 0, sizeof(DEV_BROADCAST_HANDLE));
  4350. if (pNotify == NULL) {
  4351. LogErrorEvent(ERR_ALLOCATING_BROADCAST_HANDLE, ERROR_NOT_ENOUGH_MEMORY, 0);
  4352. status = FALSE;
  4353. if (VetoNameLength != NULL) {
  4354. *VetoNameLength = 0;
  4355. }
  4356. break;
  4357. }
  4358. memset(pNotify, 0, sizeof(DEV_BROADCAST_HANDLE));
  4359. pNotify->dbch_nameoffset = -1; // empty except for custom events
  4360. pNotify->dbch_size = sizeof(DEV_BROADCAST_HANDLE);
  4361. pNotify->dbch_devicetype = DBT_DEVTYP_HANDLE;
  4362. for (p = EventBlock->u.TargetDevice.DeviceIds;
  4363. *p;
  4364. p += lstrlen(p) + 1) {
  4365. *VetoNameLength = vetoNameSize;
  4366. KdPrintEx((DPFLTR_PNPMGR_ID,
  4367. DBGF_EVENT,
  4368. "UMPNPMGR: Processing TargetDeviceChangeEvent (0x%lx) for %ws\n",
  4369. eventId, p));
  4370. status = NotifyTargetDeviceChange(serviceControl,
  4371. eventId,
  4372. flags,
  4373. pNotify,
  4374. p,
  4375. VetoType,
  4376. VetoName,
  4377. VetoNameLength);
  4378. if (!status && (flags & BSF_QUERY)) {
  4379. LPWSTR pFail = p;
  4380. DWORD dwCancelEventId;
  4381. //
  4382. // Use the appropriate cancel device event id that corresponds to the
  4383. // original query device event id.
  4384. //
  4385. dwCancelEventId = MapQueryEventToCancelEvent(eventId);
  4386. for (p = EventBlock->u.TargetDevice.DeviceIds;
  4387. *p && p != pFail;
  4388. p += lstrlen(p) + 1) {
  4389. KdPrintEx((DPFLTR_PNPMGR_ID,
  4390. DBGF_EVENT,
  4391. "UMPNPMGR: Processing TargetDeviceChangeEvent (0x%lx) for %ws\n",
  4392. dwCancelEventId, p));
  4393. NotifyTargetDeviceChange( serviceControl,
  4394. dwCancelEventId,
  4395. BSF_NOHANG,
  4396. pNotify,
  4397. p,
  4398. NULL,
  4399. NULL,
  4400. NULL);
  4401. }
  4402. break;
  4403. }
  4404. }
  4405. HeapFree(ghPnPHeap, 0, pNotify);
  4406. break;
  4407. }
  4408. case DeviceClassChangeEvent: {
  4409. //
  4410. // Convert the pnp event block into a dbt style structure.
  4411. //
  4412. PDEV_BROADCAST_DEVICEINTERFACE pNotify;
  4413. ULONG ulSize;
  4414. ulSize = sizeof(DEV_BROADCAST_DEVICEINTERFACE) +
  4415. (lstrlen(EventBlock->u.DeviceClass.SymbolicLinkName) * sizeof(WCHAR));
  4416. KdPrintEx((DPFLTR_PNPMGR_ID,
  4417. DBGF_EVENT,
  4418. "UMPNPMGR: Processing DeviceClassChangeEvent (0x%lx) for %ws\n",
  4419. eventId, EventBlock->u.DeviceClass.SymbolicLinkName));
  4420. pNotify = HeapAlloc(ghPnPHeap, 0, ulSize);
  4421. if (pNotify == NULL) {
  4422. LogErrorEvent(ERR_ALLOCATING_BROADCAST_INTERFACE, ERROR_NOT_ENOUGH_MEMORY, 0);
  4423. status = FALSE;
  4424. break;
  4425. }
  4426. pNotify->dbcc_size = ulSize;
  4427. pNotify->dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
  4428. pNotify->dbcc_reserved = 0;
  4429. memcpy(&pNotify->dbcc_classguid, &EventBlock->u.DeviceClass.ClassGuid, sizeof(GUID));
  4430. hr = StringCbCopy(pNotify->dbcc_name,
  4431. ulSize
  4432. - sizeof(DEV_BROADCAST_DEVICEINTERFACE)
  4433. + sizeof(WCHAR),
  4434. EventBlock->u.DeviceClass.SymbolicLinkName);
  4435. ASSERT(SUCCEEDED(hr));
  4436. if (FAILED(hr)) {
  4437. status = FALSE;
  4438. break;
  4439. }
  4440. //
  4441. // Note: the symbolic link name is passed in kernel-mode format (\??\),
  4442. // convert to user-mode format (\\?\) before sending notification.
  4443. // Note that the only difference is the second character.
  4444. //
  4445. pNotify->dbcc_name[1] = L'\\';
  4446. status = NotifyInterfaceClassChange(serviceControl,
  4447. eventId,
  4448. flags,
  4449. pNotify);
  4450. break;
  4451. }
  4452. case HardwareProfileChangeEvent:
  4453. KdPrintEx((DPFLTR_PNPMGR_ID,
  4454. DBGF_EVENT,
  4455. "UMPNPMGR: Processing HardwareProfileChangeEvent (0x%lx)\n",
  4456. eventId));
  4457. *VetoNameLength = vetoNameSize;
  4458. status = NotifyHardwareProfileChange(serviceControl,
  4459. eventId,
  4460. flags,
  4461. VetoType,
  4462. VetoName,
  4463. VetoNameLength);
  4464. break;
  4465. case PowerEvent:
  4466. *VetoNameLength = vetoNameSize;
  4467. //
  4468. // Since all power events arrive under a single event GUID,
  4469. // EventIdFromEventGuid cannot correctly determine the event id or query
  4470. // flags from it. Instead, we get the event id directly from the device
  4471. // event block, and add the BSF_QUERY flag here, if appropriate.
  4472. //
  4473. eventId = EventBlock->u.PowerNotification.NotificationCode;
  4474. KdPrintEx((DPFLTR_PNPMGR_ID,
  4475. DBGF_EVENT,
  4476. "UMPNPMGR: Processing PowerEvent (0x%lx)\n",
  4477. eventId));
  4478. if ((eventId == PBT_APMQUERYSUSPEND) ||
  4479. (eventId == PBT_APMQUERYSTANDBY)) {
  4480. flags |= BSF_QUERY;
  4481. } else {
  4482. flags &= ~BSF_QUERY;
  4483. }
  4484. status = NotifyPower(serviceControl,
  4485. eventId,
  4486. EventBlock->u.PowerNotification.NotificationData,
  4487. flags,
  4488. VetoType,
  4489. VetoName,
  4490. VetoNameLength);
  4491. break;
  4492. case VetoEvent:
  4493. KdPrintEx((DPFLTR_PNPMGR_ID,
  4494. DBGF_EVENT,
  4495. "UMPNPMGR: Processing VetoEvent\n"));
  4496. status = SendHotplugNotification(
  4497. &EventBlock->EventGuid,
  4498. &EventBlock->u.VetoNotification.VetoType,
  4499. EventBlock->u.VetoNotification.DeviceIdVetoNameBuffer,
  4500. &ulClientSessionId,
  4501. ulHotplugFlags
  4502. );
  4503. break;
  4504. case DeviceInstallEvent: {
  4505. //
  4506. // Initiate installation; we can't wait around here for a user, but
  4507. // after installation is complete, kernel-mode will be notified
  4508. // that they can attempt to start the device now.
  4509. //
  4510. PPNP_INSTALL_ENTRY entry = NULL, current = NULL;
  4511. KdPrintEx((DPFLTR_PNPMGR_ID,
  4512. DBGF_EVENT,
  4513. "UMPNPMGR: Processing DeviceInstallEvent for %ws\n",
  4514. EventBlock->u.InstallDevice.DeviceId));
  4515. //
  4516. // Device install events should always be this GUID, and that guid
  4517. // should always be converted into the below eventId, serviceControl and
  4518. // flags.
  4519. //
  4520. ASSERT(GuidEqual(&EventBlock->EventGuid, &GUID_DEVICE_ENUMERATED));
  4521. ASSERT((eventId == DBT_DEVICEARRIVAL) && (serviceControl == 0) && (flags == 0));
  4522. //
  4523. // Allocate and initialize a new device install entry block.
  4524. //
  4525. entry = HeapAlloc(ghPnPHeap, 0, sizeof(PNP_INSTALL_ENTRY));
  4526. if (!entry) {
  4527. break;
  4528. }
  4529. entry->Next = NULL;
  4530. entry->Flags = 0;
  4531. hr = StringCchCopy(entry->szDeviceId,
  4532. MAX_DEVICE_ID_LEN,
  4533. EventBlock->u.InstallDevice.DeviceId);
  4534. ASSERT(SUCCEEDED(hr));
  4535. //
  4536. // Insert this entry in the device install list.
  4537. //
  4538. LockNotifyList(&InstallList.Lock);
  4539. current = (PPNP_INSTALL_ENTRY)InstallList.Next;
  4540. if (current == NULL) {
  4541. InstallList.Next = entry;
  4542. } else {
  4543. while ((PPNP_INSTALL_ENTRY)current->Next != NULL) {
  4544. current = (PPNP_INSTALL_ENTRY)current->Next;
  4545. }
  4546. current->Next = entry;
  4547. }
  4548. UnlockNotifyList(&InstallList.Lock);
  4549. SetEvent(InstallEvents[NEEDS_INSTALL_EVENT]);
  4550. //
  4551. // Generate a devnode changed message
  4552. //
  4553. NotifyTargetDeviceChange(serviceControl,
  4554. eventId,
  4555. flags,
  4556. NULL,
  4557. EventBlock->u.InstallDevice.DeviceId,
  4558. NULL,
  4559. NULL,
  4560. NULL);
  4561. break;
  4562. }
  4563. case BlockedDriverEvent: {
  4564. LPGUID BlockedDriverGuid;
  4565. PWSTR MultiSzGuidList = NULL;
  4566. //
  4567. // Display notification to the Console session that the system just
  4568. // blocked a driver from loading on the system.
  4569. //
  4570. ASSERT(GuidEqual(&EventBlock->EventGuid, &GUID_DRIVER_BLOCKED));
  4571. //
  4572. // We currently only ever have one blocked driver GUID per event,
  4573. // but SendHotplugNotification and hotplug.dll are setup to deal
  4574. // with multi-sz lists, so we'll just construct one for them. This
  4575. // keeps hotplug.dll extensible, should we decide in the future to
  4576. // have the kernel-mode pnpmgr "batch" blocked drivers per devnode.
  4577. //
  4578. BlockedDriverGuid = (LPGUID)&EventBlock->u.BlockedDriverNotification.BlockedDriverGuid;
  4579. KdPrintEx((DPFLTR_PNPMGR_ID,
  4580. DBGF_EVENT,
  4581. "UMPNPMGR: Processing BlockedDriverEvent for GUID = "
  4582. "{%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n",
  4583. BlockedDriverGuid->Data1,
  4584. BlockedDriverGuid->Data2,
  4585. BlockedDriverGuid->Data3,
  4586. BlockedDriverGuid->Data4[0],
  4587. BlockedDriverGuid->Data4[1],
  4588. BlockedDriverGuid->Data4[2],
  4589. BlockedDriverGuid->Data4[3],
  4590. BlockedDriverGuid->Data4[4],
  4591. BlockedDriverGuid->Data4[5],
  4592. BlockedDriverGuid->Data4[6],
  4593. BlockedDriverGuid->Data4[7]));
  4594. MultiSzGuidList = BuildBlockedDriverList(BlockedDriverGuid, 1);
  4595. if (MultiSzGuidList != NULL) {
  4596. SendHotplugNotification((LPGUID)&GUID_DRIVER_BLOCKED,
  4597. NULL,
  4598. MultiSzGuidList,
  4599. &ulClientSessionId,
  4600. ulHotplugFlags);
  4601. HeapFree(ghPnPHeap, 0, MultiSzGuidList);
  4602. MultiSzGuidList = NULL;
  4603. }
  4604. break;
  4605. }
  4606. case InvalidIDEvent: {
  4607. ASSERT(GuidEqual(&EventBlock->EventGuid, &GUID_DEVICE_INVALID_ID));
  4608. //
  4609. // Display notification to the Console session that the system just
  4610. // encountered an invalid ID from a device.
  4611. //
  4612. SendHotplugNotification((LPGUID)&GUID_DEVICE_INVALID_ID,
  4613. NULL,
  4614. &EventBlock->u.InvalidIDNotification.ParentId[0],
  4615. &ulClientSessionId,
  4616. ulHotplugFlags);
  4617. break;
  4618. }
  4619. default:
  4620. break;
  4621. }
  4622. return status;
  4623. } // ProcessDeviceEvent
  4624. ULONG
  4625. NotifyInterfaceClassChange(
  4626. IN DWORD ServiceControl,
  4627. IN DWORD EventId,
  4628. IN DWORD Flags,
  4629. IN PDEV_BROADCAST_DEVICEINTERFACE ClassData
  4630. )
  4631. /*++
  4632. Routine Description:
  4633. This routine notifies registered services and windows of device interface
  4634. change events.
  4635. Arguments:
  4636. ServiceControl - Specifies class of service event (power, device, hwprofile
  4637. change).
  4638. EventId - Specifies the DBT style event id for the device event.
  4639. (see sdk\inc\dbt.h for defined device events)
  4640. Flags - Unused (Specifies BroadcastSystemMessage BSF_ flags.)
  4641. ClassData - Pointer to a PDEV_BROADCAST_DEVICEINTERFACE structure that
  4642. is already filled out with the pertinent data for this
  4643. event.
  4644. Return Value:
  4645. Returns TRUE.
  4646. --*/
  4647. {
  4648. NTSTATUS ntStatus = STATUS_SUCCESS;
  4649. DWORD result;
  4650. ULONG hashValue, pass, i;
  4651. PPNP_NOTIFY_ENTRY classEntry = NULL, nextEntry = NULL;
  4652. PPNP_NOTIFY_LIST notifyList;
  4653. LPGUID entryGuid[3];
  4654. UNREFERENCED_PARAMETER(Flags);
  4655. //
  4656. // Search the notification lists twice - once to notify entries registered
  4657. // on the device interface class for this device interface, and again to
  4658. // notify entries registered for all device interfaces.
  4659. //
  4660. entryGuid[0] = (LPGUID)&ClassData->dbcc_classguid;
  4661. entryGuid[1] = (LPGUID)&GUID_DEVINTERFACE_INCLUDE_ALL_INTERFACE_CLASSES;
  4662. entryGuid[2] = (LPGUID)NULL;
  4663. for (i = 0; entryGuid[i] != NULL; i++) {
  4664. //
  4665. // The list of registered callers is hashed for quicker access and
  4666. // comparison. Walk the list of registered callers and notify anyone
  4667. // that registered an interest in this device interface class guid.
  4668. //
  4669. hashValue = HashClassGuid(entryGuid[i]);
  4670. notifyList = &ClassList[hashValue];
  4671. LockNotifyList(&notifyList->Lock);
  4672. classEntry = GetFirstNotifyEntry(&ClassList[hashValue], 0);
  4673. pass = GetFirstPass(FALSE);
  4674. while (pass != PASS_COMPLETE) {
  4675. while (classEntry) {
  4676. nextEntry = GetNextNotifyEntry(classEntry, 0);
  4677. if (classEntry->Unregistered) {
  4678. classEntry = nextEntry;
  4679. continue;
  4680. }
  4681. if (GuidEqual(entryGuid[i], &classEntry->u.Class.ClassGuid)) {
  4682. if (GuidEqual(&classEntry->u.Class.ClassGuid,
  4683. &GUID_DEVINTERFACE_INCLUDE_ALL_INTERFACE_CLASSES)) {
  4684. //
  4685. // If the entry is marked with our special GUID, make
  4686. // sure it is because it was registered with the
  4687. // appropriate flag.
  4688. //
  4689. ASSERT((classEntry->Flags & DEVICE_NOTIFY_PROPERTY_MASK) &
  4690. DEVICE_NOTIFY_ALL_INTERFACE_CLASSES);
  4691. }
  4692. if ((pass == DEVICE_NOTIFY_WINDOW_HANDLE) &&
  4693. (GetPassFromEntry(classEntry) == DEVICE_NOTIFY_WINDOW_HANDLE)) {
  4694. //
  4695. // Note, class changes currently only support non-query type
  4696. // messages so special processing is not required (PostMessage
  4697. // only). Unfortunately, the PostMessage call currently fails
  4698. // if the high bit of the wParam value is set (which it is in
  4699. // this case), so we are forced to Send the message (rather than
  4700. // Post it). USER group implemented it this way because the original
  4701. // Win95 spec doesn't call for the recipient to free the message
  4702. // so we have to free it and we have no idea when it's safe
  4703. // with a PostMessage call.
  4704. //
  4705. UnlockNotifyList(&notifyList->Lock);
  4706. if (classEntry->SessionId == MAIN_SESSION) {
  4707. ntStatus = DeviceEventWorker(classEntry->Handle,
  4708. EventId,
  4709. (LPARAM)ClassData,
  4710. TRUE,
  4711. &result);
  4712. } else {
  4713. if (fpWinStationSendWindowMessage) {
  4714. try {
  4715. if (fpWinStationSendWindowMessage(SERVERNAME_CURRENT,
  4716. classEntry->SessionId,
  4717. DEFAULT_SEND_TIME_OUT,
  4718. HandleToUlong(classEntry->Handle),
  4719. WM_DEVICECHANGE,
  4720. (WPARAM)EventId,
  4721. (LPARAM)ClassData,
  4722. (LONG*)&result)) {
  4723. ntStatus = STATUS_SUCCESS;
  4724. } else {
  4725. ntStatus = STATUS_UNSUCCESSFUL;
  4726. }
  4727. } except (EXCEPTION_EXECUTE_HANDLER) {
  4728. KdPrintEx((DPFLTR_PNPMGR_ID,
  4729. DBGF_ERRORS,
  4730. "UMPNPMGR: Exception calling WinStationSendWindowMessage!\n"));
  4731. ASSERT(0);
  4732. ntStatus = STATUS_SUCCESS;
  4733. }
  4734. }
  4735. }
  4736. LockNotifyList(&notifyList->Lock);
  4737. if (!NT_SUCCESS(ntStatus)) {
  4738. if (ntStatus == STATUS_INVALID_HANDLE) {
  4739. //
  4740. // window handle no longer exists, cleanup this entry
  4741. //
  4742. KdPrintEx((DPFLTR_PNPMGR_ID,
  4743. DBGF_WARNINGS | DBGF_ERRORS,
  4744. "UMPNPMGR: Invalid window handle for '%ws' during DeviceClassChangeEvent, removing entry.\n",
  4745. classEntry->ClientName));
  4746. classEntry->Freed |= (PNP_UNREG_FREE|PNP_UNREG_DEFER|PNP_UNREG_WIN);
  4747. DeleteNotifyEntry(classEntry,FALSE);
  4748. } else if (ntStatus == STATUS_UNSUCCESSFUL) {
  4749. KdPrintEx((DPFLTR_PNPMGR_ID,
  4750. DBGF_WARNINGS | DBGF_ERRORS,
  4751. "UMPNPMGR: Window '%ws' timed out on DeviceClassChangeEvent\n",
  4752. classEntry->ClientName));
  4753. LogWarningEvent(WRN_INTERFACE_CHANGE_TIMED_OUT, 1, classEntry->ClientName);
  4754. }
  4755. }
  4756. } else if ((pass == DEVICE_NOTIFY_SERVICE_HANDLE) &&
  4757. (GetPassFromEntry(classEntry) == DEVICE_NOTIFY_SERVICE_HANDLE)) {
  4758. //
  4759. // Call the services handler routine...
  4760. //
  4761. if (pServiceControlCallback) {
  4762. UnlockNotifyList(&notifyList->Lock);
  4763. result = NO_ERROR;
  4764. try {
  4765. (pServiceControlCallback)((SERVICE_STATUS_HANDLE)classEntry->Handle,
  4766. ServiceControl,
  4767. EventId,
  4768. (LPARAM)ClassData,
  4769. &result);
  4770. } except (EXCEPTION_EXECUTE_HANDLER) {
  4771. KdPrintEx((DPFLTR_PNPMGR_ID,
  4772. DBGF_ERRORS,
  4773. "UMPNPMGR: Exception calling Service Control Manager!\n"));
  4774. result = NO_ERROR;
  4775. ASSERT(0);
  4776. }
  4777. LockNotifyList(&notifyList->Lock);
  4778. }
  4779. } else if ((pass == DEVICE_NOTIFY_COMPLETION_HANDLE) &&
  4780. (GetPassFromEntry(classEntry) == DEVICE_NOTIFY_COMPLETION_HANDLE)) {
  4781. //
  4782. // Complete the notification handle.
  4783. // NOTE: Notification completion handles not implemented.
  4784. //
  4785. NOTHING;
  4786. }
  4787. }
  4788. classEntry = nextEntry;
  4789. }
  4790. pass=GetNextPass(pass,FALSE);
  4791. classEntry = GetFirstNotifyEntry (&ClassList[hashValue],0);
  4792. }
  4793. UnlockNotifyList(&notifyList->Lock);
  4794. }
  4795. //
  4796. // Perform Win9x compatible device interface arrival and removal notification.
  4797. //
  4798. BroadcastCompatibleDeviceMsg(EventId, ClassData, NULL);
  4799. HeapFree(ghPnPHeap, 0, ClassData);
  4800. //
  4801. // For device interface notification, there are no query type events, by
  4802. // definition, so we always return TRUE from this routine (no veto).
  4803. //
  4804. return TRUE;
  4805. } // NotifyInterfaceClassChange
  4806. ULONG
  4807. NotifyTargetDeviceChange(
  4808. IN DWORD ServiceControl,
  4809. IN DWORD EventId,
  4810. IN DWORD Flags,
  4811. IN PDEV_BROADCAST_HANDLE HandleData,
  4812. IN LPWSTR DeviceId,
  4813. OUT PPNP_VETO_TYPE VetoType OPTIONAL,
  4814. OUT LPWSTR VetoName OPTIONAL,
  4815. IN OUT PULONG VetoNameLength OPTIONAL
  4816. )
  4817. /*++
  4818. Routine Description:
  4819. This routine notifies registered services and windows of target device
  4820. change events.
  4821. Arguments:
  4822. ServiceControl - Specifies class of service event (power, device, hwprofile
  4823. change).
  4824. EventId - Specifies the DBT style event id for the device event.
  4825. (see sdk\inc\dbt.h for defined device events)
  4826. Flags - Specifies BroadcastSystemMessage BSF_ flags.
  4827. Note that BroadcastSystemMessage is not actually used for
  4828. target device events, but the specified BSF_ flags are used
  4829. to determine query and cancel event notification ordering.
  4830. HandleData - Pointer to a PDEV_BROADCAST_HANDLE structure that is
  4831. already filled out with most of the pertinent data for this
  4832. event.
  4833. DeviceId - Supplies the device instance id of the target device for
  4834. this event.
  4835. VetoType - For query-type events, supplies the address of a variable to
  4836. receive, upon failure, the type of the component responsible
  4837. for vetoing the request.
  4838. VetoName - For query-type events, supplies the address of a variable to
  4839. receive, upon failure, the name of the component
  4840. responsible for vetoing the request.
  4841. VetoNameLength - For query-type events, supplies the address of a variable
  4842. specifying the size of the of buffer specified by the
  4843. VetoName parameter. Upon failure, this address will specify
  4844. the length of the string stored in that buffer by this
  4845. routine.
  4846. Return Value:
  4847. Returns FALSE in the case of a vetoed query event, TRUE otherwise.
  4848. Note:
  4849. For DBT_DEVICEARRIVAL, DBT_DEVICEREMOVEPENDING, and DBT_DEVICEREMOVECOMPLETE
  4850. events this routine also broadcasts a WM_DEVICECHANGE / DBT_DEVNODES_CHANGED
  4851. message to all windows. There is no additional device-specific data for
  4852. this message; it is only used by components like device manager to refresh
  4853. the list of devices in the system.
  4854. Also note that the DBT_DEVNODES_CHANGED message is the only notification
  4855. sent for DBT_DEVICEARRIVAL (kernel GUID_DEVICE_ARRIVAL) events.
  4856. --*/
  4857. {
  4858. NTSTATUS ntStatus = STATUS_SUCCESS;
  4859. DWORD result = 0;
  4860. ULONG hashValue, pass;
  4861. PPNP_NOTIFY_ENTRY targetEntry, nextEntry;
  4862. PPNP_NOTIFY_LIST notifyList = NULL;
  4863. BOOL bLocked = FALSE;
  4864. DWORD err;
  4865. BOOL serviceVetoedQuery;
  4866. DWORD recipients = BSM_ALLDESKTOPS | BSM_APPLICATIONS;
  4867. LONG response;
  4868. #ifdef _WIN64
  4869. DEV_BROADCAST_HANDLE32 UNALIGNED *HandleData32 = NULL;
  4870. ULONG ulHandleDataSize;
  4871. #endif // _WIN64
  4872. PVOID pHandleData;
  4873. serviceVetoedQuery = FALSE;
  4874. //
  4875. // If we're doing a query, then VetoType, VetoName, and VetoNameLength must
  4876. // all be specified.
  4877. //
  4878. ASSERT(!(Flags & BSF_QUERY) || (VetoType && VetoName && VetoNameLength));
  4879. if (!(Flags & BSF_QUERY) && (VetoNameLength != NULL)) {
  4880. //
  4881. // Not vetoable.
  4882. //
  4883. *VetoNameLength = 0;
  4884. }
  4885. //
  4886. // Broadcast the DBT_DEVNODES_CHANGED message before any other notification
  4887. // events, so components listening for those can update themselves in a
  4888. // timely manner, and not be delayed by apps/services hung on their
  4889. // notification event. This broadcasts is a post, so it will return
  4890. // immediately, and complete asynchronously.
  4891. //
  4892. if ((EventId == DBT_DEVICEARRIVAL) ||
  4893. (EventId == DBT_DEVICEREMOVEPENDING) ||
  4894. (EventId == DBT_DEVICEREMOVECOMPLETE)) {
  4895. BroadcastSystemMessage(BSF_POSTMESSAGE,
  4896. &recipients,
  4897. WM_DEVICECHANGE,
  4898. DBT_DEVNODES_CHANGED,
  4899. (LPARAM)NULL);
  4900. if (fpWinStationBroadcastSystemMessage) {
  4901. try {
  4902. fpWinStationBroadcastSystemMessage(SERVERNAME_CURRENT,
  4903. TRUE,
  4904. 0,
  4905. DEFAULT_BROADCAST_TIME_OUT,
  4906. BSF_NOHANG | BSF_POSTMESSAGE,
  4907. &recipients,
  4908. WM_DEVICECHANGE,
  4909. (WPARAM)DBT_DEVNODES_CHANGED,
  4910. (LPARAM)NULL,
  4911. &response);
  4912. } except (EXCEPTION_EXECUTE_HANDLER) {
  4913. KdPrintEx((DPFLTR_PNPMGR_ID,
  4914. DBGF_ERRORS,
  4915. "UMPNPMGR: Exception calling WinStationBroadcastSystemMessage!\n"));
  4916. ASSERT(0);
  4917. }
  4918. }
  4919. }
  4920. //
  4921. // For target device arrival events, no additional notification is
  4922. // performed.
  4923. //
  4924. if (EventId == DBT_DEVICEARRIVAL) {
  4925. goto Clean0;
  4926. }
  4927. #ifdef _WIN64
  4928. //
  4929. // Prepare a 32-bit notification structure, which we'll need to send to any
  4930. // WOW64 clients that are registered.
  4931. //
  4932. ASSERT(sizeof(DEV_BROADCAST_HANDLE) == sizeof(DEV_BROADCAST_HANDLE64));
  4933. ASSERT(HandleData->dbch_size >= sizeof(DEV_BROADCAST_HANDLE64));
  4934. ulHandleDataSize = HandleData->dbch_size -
  4935. sizeof(DEV_BROADCAST_HANDLE64) +
  4936. sizeof(DEV_BROADCAST_HANDLE32);
  4937. ASSERT(ulHandleDataSize >= sizeof(DEV_BROADCAST_HANDLE32));
  4938. HandleData32 = HeapAlloc(ghPnPHeap, 0, ulHandleDataSize);
  4939. if (HandleData32 == NULL) {
  4940. goto Clean0;
  4941. }
  4942. memset(HandleData32, 0, ulHandleDataSize);
  4943. HandleData32->dbch_size = ulHandleDataSize;
  4944. HandleData32->dbch_devicetype = DBT_DEVTYP_HANDLE;
  4945. HandleData32->dbch_nameoffset = HandleData->dbch_nameoffset;
  4946. memcpy(&HandleData32->dbch_eventguid,
  4947. &HandleData->dbch_eventguid,
  4948. sizeof(GUID));
  4949. memcpy(&HandleData32->dbch_data,
  4950. &HandleData->dbch_data,
  4951. (HandleData->dbch_size - FIELD_OFFSET(DEV_BROADCAST_HANDLE, dbch_data)));
  4952. #endif // _WIN64
  4953. //
  4954. // Sanitize the device id
  4955. //
  4956. FixUpDeviceId(DeviceId);
  4957. //
  4958. // The list of registered callers is hashed for quicker access and
  4959. // comparison. Walk the list of registered callers and notify anyone
  4960. // that registered an interest in this device instance.
  4961. //
  4962. hashValue = HashString(DeviceId, TARGET_HASH_BUCKETS);
  4963. notifyList = &TargetList[hashValue];
  4964. LockNotifyList(&notifyList->Lock);
  4965. bLocked = TRUE;
  4966. pass = GetFirstPass(Flags & BSF_QUERY);
  4967. do {
  4968. targetEntry = GetFirstNotifyEntry (notifyList,Flags);
  4969. while (targetEntry) {
  4970. nextEntry = GetNextNotifyEntry(targetEntry,Flags);
  4971. if (targetEntry->Unregistered) {
  4972. targetEntry = nextEntry;
  4973. continue;
  4974. }
  4975. if (CompareString(
  4976. LOCALE_INVARIANT, NORM_IGNORECASE,
  4977. DeviceId, -1,
  4978. targetEntry->u.Target.DeviceId, -1) == CSTR_EQUAL) {
  4979. if ((pass == DEVICE_NOTIFY_WINDOW_HANDLE) &&
  4980. (GetPassFromEntry(targetEntry) == DEVICE_NOTIFY_WINDOW_HANDLE)) {
  4981. //
  4982. // Note: we could get away with only doing a send message
  4983. // if the Flags has BSF_QUERY set and do a post message in
  4984. // all other cases. Unfortunately, the PostMessage call currently
  4985. // fails if the high bit of the wParam value is set (which it is in
  4986. // this case), so we are forced to Send the message (rather than
  4987. // Post it). USER group implemented it this way because the original
  4988. // Win95 spec doesn't call for the recipient to free the message
  4989. // so we have to free it and we have no idea when it's safe
  4990. // with a PostMessage call.
  4991. //
  4992. HandleData->dbch_handle =
  4993. targetEntry->u.Target.FileHandle;
  4994. HandleData->dbch_hdevnotify =
  4995. (HDEVNOTIFY)((ULONG_PTR)targetEntry->ClientCtxPtr);
  4996. UnlockNotifyList(&notifyList->Lock);
  4997. bLocked = FALSE;
  4998. //
  4999. // Always send the native DEV_BROADCAST_HANDLE structure to
  5000. // windows. If any 64-bit/32-bit conversion needs to be
  5001. // done for this client, ntuser will do it for us.
  5002. //
  5003. pHandleData = HandleData;
  5004. if (targetEntry->SessionId == MAIN_SESSION ) {
  5005. ntStatus = DeviceEventWorker(targetEntry->Handle,
  5006. EventId,
  5007. (LPARAM)pHandleData,
  5008. TRUE,
  5009. &result);
  5010. } else {
  5011. if (fpWinStationSendWindowMessage) {
  5012. try {
  5013. if (fpWinStationSendWindowMessage(SERVERNAME_CURRENT,
  5014. targetEntry->SessionId,
  5015. DEFAULT_SEND_TIME_OUT,
  5016. HandleToUlong(targetEntry->Handle),
  5017. WM_DEVICECHANGE,
  5018. (WPARAM)EventId,
  5019. (LPARAM)pHandleData,
  5020. (LONG*)&result)) {
  5021. ntStatus = STATUS_SUCCESS;
  5022. } else {
  5023. ntStatus = STATUS_UNSUCCESSFUL;
  5024. }
  5025. } except (EXCEPTION_EXECUTE_HANDLER) {
  5026. KdPrintEx((DPFLTR_PNPMGR_ID,
  5027. DBGF_ERRORS,
  5028. "UMPNPMGR: Exception calling WinStationSendWindowMessage!\n"));
  5029. ASSERT(0);
  5030. ntStatus = STATUS_SUCCESS;
  5031. }
  5032. }
  5033. }
  5034. LockNotifyList(&notifyList->Lock);
  5035. bLocked = TRUE;
  5036. if (NT_SUCCESS(ntStatus)) {
  5037. //
  5038. // This call succeeded, if it's a query type call, check
  5039. // the result returned.
  5040. //
  5041. if ((Flags & BSF_QUERY) && (result == BROADCAST_QUERY_DENY)) {
  5042. KdPrintEx((DPFLTR_PNPMGR_ID,
  5043. DBGF_EVENT,
  5044. "UMPNPMGR: Window '%ws' vetoed TargetDeviceChangeEvent\n",
  5045. targetEntry->ClientName));
  5046. WindowVeto(targetEntry, VetoType, VetoName, VetoNameLength);
  5047. //
  5048. // Haven't told the services yet. Note that we
  5049. // always call this routine with the native
  5050. // DEV_BROADCAST_HANDLE structure, since it walks
  5051. // the entire list itself. It will do the
  5052. // conversion again, if necessary.
  5053. //
  5054. SendCancelNotification(targetEntry,
  5055. ServiceControl,
  5056. EventId,
  5057. BSF_QUERY,
  5058. (PDEV_BROADCAST_HDR)HandleData,
  5059. DeviceId);
  5060. goto Clean0;
  5061. }
  5062. } else if (ntStatus == STATUS_INVALID_HANDLE) {
  5063. //
  5064. // window handle no longer exists, cleanup this entry
  5065. //
  5066. KdPrintEx((DPFLTR_PNPMGR_ID,
  5067. DBGF_ERRORS | DBGF_WARNINGS,
  5068. "UMPNPMGR: Invalid window handle for '%ws' during TargetDeviceChangeEvent, removing entry.\n",
  5069. targetEntry->ClientName));
  5070. targetEntry->Freed |= (PNP_UNREG_FREE|PNP_UNREG_DEFER|PNP_UNREG_WIN);
  5071. DeleteNotifyEntry(targetEntry,FALSE);
  5072. } else if (ntStatus == STATUS_UNSUCCESSFUL) {
  5073. KdPrintEx((DPFLTR_PNPMGR_ID,
  5074. DBGF_ERRORS | DBGF_WARNINGS,
  5075. "UMPNPMGR: Window '%ws' timed out on TargetDeviceChangeEvent\n",
  5076. targetEntry->ClientName));
  5077. LogWarningEvent(WRN_TARGET_DEVICE_CHANGE_TIMED_OUT, 1, targetEntry->ClientName);
  5078. }
  5079. } else if ((pass == DEVICE_NOTIFY_SERVICE_HANDLE) &&
  5080. (GetPassFromEntry(targetEntry) == DEVICE_NOTIFY_SERVICE_HANDLE)) {
  5081. if (pServiceControlCallback) {
  5082. //
  5083. // Call the services handler routine...
  5084. //
  5085. HandleData->dbch_handle =
  5086. targetEntry->u.Target.FileHandle;
  5087. HandleData->dbch_hdevnotify =
  5088. (HDEVNOTIFY)((ULONG_PTR)targetEntry->ClientCtxPtr);
  5089. //
  5090. // Assume we're sending the native DEV_BROADCAST_HANDLE
  5091. // structure.
  5092. //
  5093. pHandleData = HandleData;
  5094. #ifdef _WIN64
  5095. //
  5096. // If the client is running on WOW64, send it the 32-bit
  5097. // DEV_BROADCAST_HANDLE structure we created instead.
  5098. //
  5099. if (targetEntry->Flags & DEVICE_NOTIFY_WOW64_CLIENT) {
  5100. HandleData32->dbch_handle =
  5101. (ULONG32)PtrToUlong(targetEntry->u.Target.FileHandle);
  5102. HandleData32->dbch_hdevnotify =
  5103. (ULONG32)PtrToUlong((HDEVNOTIFY)targetEntry->ClientCtxPtr);
  5104. pHandleData = HandleData32;
  5105. }
  5106. #endif // _WIN64
  5107. try {
  5108. UnlockNotifyList(&notifyList->Lock);
  5109. bLocked = FALSE;
  5110. err = NO_ERROR;
  5111. try {
  5112. (pServiceControlCallback)((SERVICE_STATUS_HANDLE)targetEntry->Handle,
  5113. ServiceControl,
  5114. EventId,
  5115. (LPARAM)pHandleData,
  5116. &err);
  5117. } except (EXCEPTION_EXECUTE_HANDLER) {
  5118. KdPrintEx((DPFLTR_PNPMGR_ID,
  5119. DBGF_ERRORS,
  5120. "UMPNPMGR: Exception calling Service Control Manager!\n"));
  5121. ASSERT(0);
  5122. err = NO_ERROR;
  5123. }
  5124. LockNotifyList(&notifyList->Lock);
  5125. bLocked = TRUE;
  5126. //
  5127. // convert Win32 error into window message-style
  5128. // return value
  5129. //
  5130. if (err == NO_ERROR) {
  5131. result = TRUE;
  5132. } else {
  5133. KdPrintEx((DPFLTR_PNPMGR_ID,
  5134. DBGF_EVENT,
  5135. "UMPNPMGR: Service %ws responded to TargetDeviceChangeEvent with status=0x%08lx\n",
  5136. targetEntry->ClientName,
  5137. err));
  5138. //
  5139. // This service specifically requested to receive this
  5140. // notification - it should know how to handle it.
  5141. //
  5142. ASSERT(err != ERROR_CALL_NOT_IMPLEMENTED);
  5143. //
  5144. // Log the error the service used to veto.
  5145. //
  5146. LogWarningEvent(WRN_TARGET_DEVICE_CHANGE_SERVICE_VETO,
  5147. 1,
  5148. targetEntry->ClientName);
  5149. result = BROADCAST_QUERY_DENY;
  5150. }
  5151. if ((Flags & BSF_QUERY) && (result == BROADCAST_QUERY_DENY)) {
  5152. serviceVetoedQuery = TRUE;
  5153. ServiceVeto(targetEntry, VetoType, VetoName, VetoNameLength );
  5154. //
  5155. // This service vetoed the query, tell everyone
  5156. // else it was cancelled. Note that we always
  5157. // call this routine with the native
  5158. // DEV_BROADCAST_HANDLE structure, since it
  5159. // walks the entire list itself. It will do the
  5160. // conversion again, if necessary.
  5161. //
  5162. SendCancelNotification(targetEntry,
  5163. ServiceControl,
  5164. EventId,
  5165. BSF_QUERY,
  5166. (PDEV_BROADCAST_HDR)HandleData,
  5167. DeviceId);
  5168. }
  5169. } except (EXCEPTION_EXECUTE_HANDLER) {
  5170. KdPrintEx((DPFLTR_PNPMGR_ID,
  5171. DBGF_ERRORS,
  5172. "UMPNPMGR: Exception calling Service Control Manager!\n"));
  5173. ASSERT(0);
  5174. //
  5175. // Reference the "serviceVetoedQuery" variable to
  5176. // ensure the compiler will respect statement
  5177. // ordering w.r.t. this variable. We want to make
  5178. // sure we know with certainty whether any service
  5179. // vetoed the query, even if subsequently sending
  5180. // the cancel caused an access violation.
  5181. //
  5182. serviceVetoedQuery = serviceVetoedQuery;
  5183. }
  5184. if (serviceVetoedQuery) {
  5185. goto Clean0;
  5186. }
  5187. }
  5188. } else if ((pass == DEVICE_NOTIFY_COMPLETION_HANDLE) &&
  5189. (GetPassFromEntry(targetEntry) == DEVICE_NOTIFY_COMPLETION_HANDLE)) {
  5190. //
  5191. // Complete the notification handle.
  5192. // NOTE: Notification completion handles not implemented.
  5193. //
  5194. NOTHING;
  5195. }
  5196. }
  5197. targetEntry = nextEntry;
  5198. } // while
  5199. } while ((pass = GetNextPass(pass, (Flags & BSF_QUERY))) != PASS_COMPLETE);
  5200. if (VetoNameLength != NULL) {
  5201. *VetoNameLength = 0;
  5202. }
  5203. Clean0:
  5204. if (bLocked) {
  5205. UnlockNotifyList(&notifyList->Lock);
  5206. }
  5207. #ifdef _WIN64
  5208. //
  5209. // Free the 32-bit DEV_BROADCAST_HANDLE structure, if we allocated one.
  5210. //
  5211. if (HandleData32 != NULL) {
  5212. HeapFree(ghPnPHeap, 0, HandleData32);
  5213. }
  5214. #endif // _WIN64
  5215. return (result != BROADCAST_QUERY_DENY);
  5216. } // NotifyTargetDeviceChange
  5217. ULONG
  5218. NotifyHardwareProfileChange(
  5219. IN DWORD ServiceControl,
  5220. IN DWORD EventId,
  5221. IN DWORD Flags,
  5222. OUT PPNP_VETO_TYPE VetoType OPTIONAL,
  5223. OUT LPWSTR VetoName OPTIONAL,
  5224. IN OUT PULONG VetoNameLength OPTIONAL
  5225. )
  5226. /*++
  5227. Routine Description:
  5228. This routine notifies registered services and all windows of hardware
  5229. profile change events.
  5230. Arguments:
  5231. ServiceControl - Specifies class of service event (power, device, hwprofile
  5232. change).
  5233. EventId - Specifies the DBT style event id for the device event.
  5234. (see sdk\inc\dbt.h for defined hardware profile change events)
  5235. Flags - Specifies BroadcastSystemMessage BSF_ flags.
  5236. VetoType - For query-type events, supplies the address of a variable to
  5237. receive, upon failure, the type of the component responsible
  5238. for vetoing the request.
  5239. VetoName - For query-type events, supplies the address of a variable to
  5240. receive, upon failure, the name of the component
  5241. responsible for vetoing the request.
  5242. VetoNameLength - For query-type events, supplies the address of a variable
  5243. specifying the size of the of buffer specified by the
  5244. VetoName parameter. Upon failure, this address will specify
  5245. the length of the string stored in that buffer by this
  5246. routine.
  5247. Return Value:
  5248. Returns FALSE in the case of a vetoed query event, TRUE otherwise.
  5249. --*/
  5250. {
  5251. DWORD pass;
  5252. DWORD recipients = BSM_ALLDESKTOPS | BSM_APPLICATIONS;
  5253. PPNP_NOTIFY_ENTRY entry = NULL, nextEntry = NULL;
  5254. PPNP_NOTIFY_LIST notifyList = NULL;
  5255. BOOL bLocked = FALSE;
  5256. LONG response;
  5257. ULONG successful;
  5258. LONG result;
  5259. DWORD err;
  5260. //
  5261. // If we're doing a query, then VetoType, VetoName, and VetoNameLength must
  5262. // all be specified.
  5263. //
  5264. ASSERT(!(Flags & BSF_QUERY) || (VetoType && VetoName && VetoNameLength));
  5265. if (!(Flags & BSF_QUERY) && (VetoNameLength != NULL)) {
  5266. //
  5267. // Not vetoable.
  5268. //
  5269. *VetoNameLength = 0;
  5270. }
  5271. notifyList = &ServiceList[CINDEX_HWPROFILE];
  5272. LockNotifyList(&notifyList->Lock);
  5273. bLocked = TRUE;
  5274. successful = TRUE;
  5275. pass = GetFirstPass(Flags & BSF_QUERY);
  5276. try {
  5277. while (pass != PASS_COMPLETE) {
  5278. if (pass == DEVICE_NOTIFY_WINDOW_HANDLE) {
  5279. //
  5280. // Notify the Windows
  5281. //
  5282. UnlockNotifyList (&notifyList->Lock);
  5283. bLocked = FALSE;
  5284. //
  5285. // ISSUE-2002/02/20-jamesca: BroadcastSystemMessage veto info?
  5286. //
  5287. // While we could *technically* use BroadcastSystemMessageEx
  5288. // to receive the hWnd of the vetoing window, and a handle to
  5289. // the Desktop that it is on, we don't actually know what
  5290. // WindowStation that Desktop is in!!! The BSM_ALLDESKTOPS
  5291. // flags actually causes the message to be sent to all
  5292. // Desktops on *all WindowStations* in this session -- which
  5293. // for a non-interactive service in session 0 (such as this),
  5294. // would include all Desktops in all WindowStations being used
  5295. // by all non-interactive services. In reality, a vetoing
  5296. // application would *most likely* be an interactive process
  5297. // on WinSta0, but there really is no guarantee that's the
  5298. // case. Even if we did know the complete WindowStation and
  5299. // Desktop location of the window, we would need to have a
  5300. // thread running on the same desktop just to access it -
  5301. // which would require either changing the windowstation for
  5302. // our entire process (a very very bad idea, since we share
  5303. // this process with the SCM and other services!!), or start a
  5304. // whole new process over there, and communicate with it. As
  5305. // you can see, this info was probably only intended to be
  5306. // useful for a caller already in the interactive
  5307. // WindowStation, to find out about vetoing windows also in
  5308. // the interactive WindowStation, and is therefore not really
  5309. // of much use to us -- so we may as well just go back to
  5310. // using BroadcastSystemMessage. *sigh*
  5311. //
  5312. result = BroadcastSystemMessage(Flags,
  5313. &recipients,
  5314. WM_DEVICECHANGE,
  5315. (WPARAM)EventId,
  5316. (LPARAM)NULL);
  5317. if ((result <= 0) && (Flags & BSF_QUERY)) {
  5318. WinBroadcastVeto(NULL, VetoType, VetoName, VetoNameLength);
  5319. successful = FALSE;
  5320. break;
  5321. }
  5322. if ((result > 0) || (!(Flags & BSF_QUERY))) {
  5323. if (fpWinStationBroadcastSystemMessage) {
  5324. try {
  5325. fpWinStationBroadcastSystemMessage(SERVERNAME_CURRENT,
  5326. TRUE,
  5327. 0,
  5328. DEFAULT_BROADCAST_TIME_OUT,
  5329. Flags,
  5330. &recipients,
  5331. WM_DEVICECHANGE,
  5332. (WPARAM)EventId,
  5333. (LPARAM)NULL,
  5334. &result);
  5335. } except (EXCEPTION_EXECUTE_HANDLER) {
  5336. KdPrintEx((DPFLTR_PNPMGR_ID,
  5337. DBGF_ERRORS,
  5338. "UMPNPMGR: Exception calling WinStationBroadcastSystemMessage!\n"));
  5339. ASSERT(0);
  5340. result = 1;
  5341. }
  5342. }
  5343. }
  5344. LockNotifyList (&notifyList->Lock);
  5345. bLocked = TRUE;
  5346. if ((result < 0) && (Flags & BSF_QUERY)) {
  5347. UnknownVeto(VetoType, VetoName, VetoNameLength);
  5348. successful = FALSE;
  5349. break;
  5350. } else if ((result == 0) && (Flags & BSF_QUERY)) {
  5351. WinBroadcastVeto(NULL, VetoType, VetoName, VetoNameLength);
  5352. successful = FALSE;
  5353. break;
  5354. }
  5355. } else if (pass == DEVICE_NOTIFY_SERVICE_HANDLE) {
  5356. //
  5357. // Notify the services
  5358. //
  5359. entry = GetFirstNotifyEntry (notifyList,Flags & BSF_QUERY);
  5360. while (entry) {
  5361. nextEntry = GetNextNotifyEntry(entry,Flags & BSF_QUERY);
  5362. if (entry->Unregistered) {
  5363. entry = nextEntry;
  5364. continue;
  5365. }
  5366. ASSERT(GetPassFromEntry(entry) == DEVICE_NOTIFY_SERVICE_HANDLE);
  5367. //
  5368. // This is a direct call, not a message via. USER
  5369. //
  5370. if (pServiceControlCallback) {
  5371. UnlockNotifyList (&notifyList->Lock);
  5372. bLocked = FALSE;
  5373. err = NO_ERROR;
  5374. try {
  5375. (pServiceControlCallback)((SERVICE_STATUS_HANDLE)entry->Handle,
  5376. ServiceControl,
  5377. EventId,
  5378. (LPARAM)NULL,
  5379. &err);
  5380. } except (EXCEPTION_EXECUTE_HANDLER) {
  5381. KdPrintEx((DPFLTR_PNPMGR_ID,
  5382. DBGF_ERRORS,
  5383. "UMPNPMGR: Exception calling Service Control Manager!\n"));
  5384. ASSERT(0);
  5385. err = NO_ERROR;
  5386. }
  5387. LockNotifyList (&notifyList->Lock);
  5388. bLocked = TRUE;
  5389. //
  5390. // convert Win32 error into window message-style return
  5391. // value.
  5392. //
  5393. if (err == NO_ERROR) {
  5394. result = TRUE;
  5395. } else {
  5396. KdPrintEx((DPFLTR_PNPMGR_ID,
  5397. DBGF_EVENT,
  5398. "UMPNPMGR: Service %ws responded to HardwareProfileChangeEvent with status=0x%08lx\n",
  5399. entry->ClientName,
  5400. err));
  5401. //
  5402. // This service specifically requested to receive this
  5403. // notification - it should know how to handle it.
  5404. //
  5405. ASSERT(err != ERROR_CALL_NOT_IMPLEMENTED);
  5406. //
  5407. // Log the error the service used to veto.
  5408. //
  5409. LogWarningEvent(WRN_HWPROFILE_CHANGE_SERVICE_VETO,
  5410. 1,
  5411. entry->ClientName);
  5412. result = BROADCAST_QUERY_DENY;
  5413. }
  5414. if ((Flags & BSF_QUERY) &&
  5415. (result == BROADCAST_QUERY_DENY)) {
  5416. ServiceVeto(entry,
  5417. VetoType,
  5418. VetoName,
  5419. VetoNameLength);
  5420. successful = FALSE;
  5421. break;
  5422. }
  5423. }
  5424. entry = nextEntry;
  5425. }
  5426. }
  5427. if (!successful) {
  5428. break;
  5429. }
  5430. pass = GetNextPass (pass,Flags & BSF_QUERY);
  5431. }
  5432. } except (EXCEPTION_EXECUTE_HANDLER) {
  5433. KdPrintEx((DPFLTR_PNPMGR_ID,
  5434. DBGF_ERRORS,
  5435. "UMPNPMGR: Exception in service callback in NotifyHardwareProfileChange\n"));
  5436. ASSERT(0);
  5437. if (Flags & BSF_QUERY) {
  5438. UnknownVeto(VetoType, VetoName, VetoNameLength);
  5439. successful = FALSE;
  5440. }
  5441. }
  5442. try {
  5443. if (!successful) {
  5444. ASSERT(Flags & BSF_QUERY);
  5445. //
  5446. // If a service vetoed the query, inform the services and windows,
  5447. // otherwise only the windows know what was coming.
  5448. //
  5449. if (pass == DEVICE_NOTIFY_SERVICE_HANDLE) {
  5450. SendCancelNotification(
  5451. entry,
  5452. ServiceControl,
  5453. EventId,
  5454. BSF_QUERY,
  5455. NULL,
  5456. NULL);
  5457. }
  5458. UnlockNotifyList (&notifyList->Lock);
  5459. bLocked = FALSE;
  5460. BroadcastSystemMessage(Flags & ~BSF_QUERY,
  5461. &recipients,
  5462. WM_DEVICECHANGE,
  5463. (WPARAM)MapQueryEventToCancelEvent(EventId),
  5464. (LPARAM)NULL);
  5465. if (fpWinStationBroadcastSystemMessage) {
  5466. try {
  5467. fpWinStationBroadcastSystemMessage(SERVERNAME_CURRENT,
  5468. TRUE,
  5469. 0,
  5470. DEFAULT_BROADCAST_TIME_OUT,
  5471. Flags & ~BSF_QUERY,
  5472. &recipients,
  5473. WM_DEVICECHANGE,
  5474. (WPARAM)MapQueryEventToCancelEvent(EventId),
  5475. (LPARAM)NULL,
  5476. &response);
  5477. } except (EXCEPTION_EXECUTE_HANDLER) {
  5478. KdPrintEx((DPFLTR_PNPMGR_ID,
  5479. DBGF_ERRORS,
  5480. "UMPNPMGR: Exception calling WinStationBroadcastSystemMessage\n"));
  5481. ASSERT(0);
  5482. }
  5483. }
  5484. LockNotifyList (&notifyList->Lock);
  5485. bLocked = TRUE;
  5486. }
  5487. } except (EXCEPTION_EXECUTE_HANDLER) {
  5488. KdPrintEx((DPFLTR_PNPMGR_ID,
  5489. DBGF_ERRORS,
  5490. "UMPNPMGR: Exception in service callback in NotifyHardwareProfileChange\n"));
  5491. ASSERT(0);
  5492. }
  5493. if (bLocked) {
  5494. UnlockNotifyList (&notifyList->Lock);
  5495. }
  5496. //
  5497. // if successful, we are not returning veto info.
  5498. //
  5499. if (successful && (VetoNameLength != NULL)) {
  5500. *VetoNameLength = 0;
  5501. }
  5502. return successful;
  5503. } // NotifyHardwareProfileChange
  5504. BOOL
  5505. SendCancelNotification(
  5506. IN PPNP_NOTIFY_ENTRY LastEntry,
  5507. IN DWORD ServiceControl,
  5508. IN DWORD EventId,
  5509. IN ULONG Flags,
  5510. IN PDEV_BROADCAST_HDR NotifyData OPTIONAL,
  5511. IN LPWSTR DeviceId OPTIONAL
  5512. )
  5513. /*++
  5514. Routine Description:
  5515. This routine sends a cancel notification to the entries in the range
  5516. specified. This routine assumes the appropriate list is already locked.
  5517. Arguments:
  5518. LastEntry - Specifies the last list entry that received the original
  5519. query notification, and was responsible for failing the
  5520. request. We will stop sending cancel notification events
  5521. when we get to this one.
  5522. ServiceControl - Specifies class of service event (power, device, hwprofile
  5523. change).
  5524. EventId - Specifies the DBT style event id for the device event.
  5525. (see sdk\inc\dbt.h for defined device events)
  5526. Flags - Specifies BroadcastSystemMessage BSF_ flags.
  5527. Note that BroadcastSystemMessage is not actually used for
  5528. target device events, but the specified BSF_ flags are used
  5529. to determine query and cancel event notification ordering.
  5530. NotifyData - Optionally, supplies a pointer to a PDEV_BROADCAST_Xxx
  5531. structure that is already filled out with most of the
  5532. pertinent data for this event.
  5533. This parameter may be NULL for "global" events that are not
  5534. associated with any device, such as power and hardware
  5535. profile change events.
  5536. DeviceId - Optionally, supplies the device instance id of the target
  5537. device for this event.
  5538. This parameter may be NULL for "global" events that are not
  5539. associated with any device, such as power and hardware
  5540. profile change events.
  5541. Return Value:
  5542. Returns TRUE / FALSE.
  5543. --*/
  5544. {
  5545. NTSTATUS ntStatus = STATUS_SUCCESS;
  5546. DWORD cancelEventId;
  5547. DWORD result, pass, lastPass;
  5548. PPNP_NOTIFY_ENTRY entry, headEntry;
  5549. PPNP_NOTIFY_LIST notifyList;
  5550. #ifdef _WIN64
  5551. DEV_BROADCAST_HANDLE32 UNALIGNED *HandleData32 = NULL;
  5552. ULONG ulHandleDataSize;
  5553. #endif // _WIN64
  5554. PVOID pNotifyData;
  5555. #ifdef _WIN64
  5556. if ((ARGUMENT_PRESENT(NotifyData)) &&
  5557. (NotifyData->dbch_devicetype == DBT_DEVTYP_HANDLE)) {
  5558. //
  5559. // If cancelling a DEV_BROADCAST_HANDLE type event, prepare a 32-bit
  5560. // notification structure, which we'll need to send to any WOW64 clients
  5561. // that are registered.
  5562. //
  5563. ulHandleDataSize = ((PDEV_BROADCAST_HANDLE)NotifyData)->dbch_size -
  5564. sizeof(DEV_BROADCAST_HANDLE64) +
  5565. sizeof(DEV_BROADCAST_HANDLE32);
  5566. ASSERT(ulHandleDataSize >= sizeof(DEV_BROADCAST_HANDLE32));
  5567. HandleData32 = HeapAlloc(ghPnPHeap, 0, ulHandleDataSize);
  5568. if (HandleData32 == NULL) {
  5569. return FALSE;
  5570. }
  5571. memset(HandleData32, 0, ulHandleDataSize);
  5572. HandleData32->dbch_size = ulHandleDataSize;
  5573. HandleData32->dbch_devicetype = DBT_DEVTYP_HANDLE;
  5574. HandleData32->dbch_nameoffset = ((PDEV_BROADCAST_HANDLE)NotifyData)->dbch_nameoffset;
  5575. memcpy(&HandleData32->dbch_eventguid,
  5576. &((PDEV_BROADCAST_HANDLE)NotifyData)->dbch_eventguid,
  5577. sizeof(GUID));
  5578. memcpy(&HandleData32->dbch_data,
  5579. &((PDEV_BROADCAST_HANDLE)NotifyData)->dbch_data,
  5580. (NotifyData->dbch_size - FIELD_OFFSET(DEV_BROADCAST_HANDLE, dbch_data)));
  5581. }
  5582. #endif // _WIN64
  5583. //
  5584. // Use the appropriate cancel device event id that corresponds to the
  5585. // original query device event id.
  5586. //
  5587. cancelEventId = MapQueryEventToCancelEvent(EventId);
  5588. //
  5589. // Get the corresponding notification list
  5590. //
  5591. notifyList = GetNotifyListForEntry(LastEntry);
  5592. ASSERT(notifyList);
  5593. if (notifyList == NULL) {
  5594. return FALSE;
  5595. }
  5596. //
  5597. // Get the pass we vetoed things on
  5598. //
  5599. lastPass = GetPassFromEntry(LastEntry);
  5600. //
  5601. // Get the opposite end of the list
  5602. //
  5603. headEntry = GetFirstNotifyEntry(notifyList, (Flags ^ BSF_QUERY));
  5604. //
  5605. // Walk the list of registered callers backwards(!) and notify anyone that registered
  5606. // an interest in this device instance. Start with the FirstEntry and stop
  5607. // just before the LastEntry (the LastEntry is the one that vetoed the
  5608. // request in the first place).
  5609. //
  5610. for(pass = lastPass;
  5611. pass != PASS_COMPLETE;
  5612. pass = GetNextPass(pass, (Flags ^ BSF_QUERY))) {
  5613. //
  5614. // If this is the pass the request was vetoed on, then start on the
  5615. // vetoer entry itself. Otherwise begin again at the appropriate end
  5616. // of the list.
  5617. //
  5618. for(entry = (pass == lastPass) ? LastEntry : headEntry;
  5619. entry;
  5620. entry = GetNextNotifyEntry(entry, (Flags ^ BSF_QUERY))) {
  5621. if (!NotifyEntryThisPass(entry, pass)) {
  5622. continue;
  5623. }
  5624. switch(pass) {
  5625. case DEVICE_NOTIFY_SERVICE_HANDLE:
  5626. if ((!ARGUMENT_PRESENT(DeviceId)) ||
  5627. (CompareString(LOCALE_INVARIANT, NORM_IGNORECASE,
  5628. DeviceId, -1,
  5629. entry->u.Target.DeviceId, -1) == CSTR_EQUAL)) {
  5630. if (pServiceControlCallback) {
  5631. //
  5632. // Assume we're sending the native structure.
  5633. //
  5634. pNotifyData = NotifyData;
  5635. if ((ARGUMENT_PRESENT(NotifyData)) &&
  5636. (NotifyData->dbch_devicetype == DBT_DEVTYP_HANDLE)) {
  5637. //
  5638. // If it's a DBT_DEVTYP_HANDLE notification, set
  5639. // the hdevnotify and file handle fields for the
  5640. // client we're notifying.
  5641. //
  5642. ((PDEV_BROADCAST_HANDLE)NotifyData)->dbch_handle =
  5643. entry->u.Target.FileHandle;
  5644. ((PDEV_BROADCAST_HANDLE)NotifyData)->dbch_hdevnotify =
  5645. (HDEVNOTIFY)((ULONG_PTR)entry->ClientCtxPtr);
  5646. #ifdef _WIN64
  5647. //
  5648. // If the client is running on WOW64, send it the 32-bit
  5649. // DEV_BROADCAST_HANDLE structure we created instead.
  5650. //
  5651. if (entry->Flags & DEVICE_NOTIFY_WOW64_CLIENT) {
  5652. HandleData32->dbch_handle =
  5653. (ULONG32)PtrToUlong(entry->u.Target.FileHandle);
  5654. HandleData32->dbch_hdevnotify =
  5655. (ULONG32)PtrToUlong((HDEVNOTIFY)entry->ClientCtxPtr);
  5656. pNotifyData = HandleData32;
  5657. }
  5658. #endif // _WIN64
  5659. }
  5660. //
  5661. // Call the services handler routine...
  5662. //
  5663. UnlockNotifyList(&notifyList->Lock);
  5664. result = NO_ERROR;
  5665. try {
  5666. (pServiceControlCallback)((SERVICE_STATUS_HANDLE)entry->Handle,
  5667. ServiceControl,
  5668. cancelEventId,
  5669. (LPARAM)pNotifyData,
  5670. &result);
  5671. } except (EXCEPTION_EXECUTE_HANDLER) {
  5672. KdPrintEx((DPFLTR_PNPMGR_ID,
  5673. DBGF_ERRORS,
  5674. "UMPNPMGR: Exception calling Service Control Manager!\n"));
  5675. result = NO_ERROR;
  5676. ASSERT(0);
  5677. }
  5678. LockNotifyList(&notifyList->Lock);
  5679. }
  5680. }
  5681. break;
  5682. case DEVICE_NOTIFY_WINDOW_HANDLE:
  5683. //
  5684. // Notify the windows. Note that events with NULL DeviceId's
  5685. // (for example hardware profile change events) are not
  5686. // registerable by windows. Luckily for them, we broadcast
  5687. // such info anyway.
  5688. //
  5689. if ((ARGUMENT_PRESENT(DeviceId)) &&
  5690. (CompareString(LOCALE_INVARIANT, NORM_IGNORECASE,
  5691. DeviceId, -1,
  5692. entry->u.Target.DeviceId, -1) == CSTR_EQUAL)) {
  5693. ASSERT(NotifyData);
  5694. //
  5695. // Always send the native DEV_BROADCAST_HANDLE structure to
  5696. // windows. If any 64-bit/32-bit conversion needs to be
  5697. // done for this client, ntuser will do it for us.
  5698. //
  5699. pNotifyData = NotifyData;
  5700. if ((ARGUMENT_PRESENT(NotifyData)) &&
  5701. (NotifyData->dbch_devicetype == DBT_DEVTYP_HANDLE)) {
  5702. //
  5703. // If it's a DBT_DEVTYP_HANDLE notification, set
  5704. // the hdevnotify and file handle fields for the
  5705. // client we're notifying.
  5706. //
  5707. ((PDEV_BROADCAST_HANDLE)NotifyData)->dbch_handle =
  5708. entry->u.Target.FileHandle;
  5709. ((PDEV_BROADCAST_HANDLE)NotifyData)->dbch_hdevnotify =
  5710. (HDEVNOTIFY)((ULONG_PTR)entry->ClientCtxPtr);
  5711. }
  5712. UnlockNotifyList(&notifyList->Lock);
  5713. if (entry->SessionId == MAIN_SESSION) {
  5714. ntStatus = DeviceEventWorker(entry->Handle,
  5715. cancelEventId,
  5716. (LPARAM)pNotifyData,
  5717. TRUE,
  5718. &result // ignore result
  5719. );
  5720. } else if (fpWinStationSendWindowMessage) {
  5721. try {
  5722. if (fpWinStationSendWindowMessage(SERVERNAME_CURRENT,
  5723. entry->SessionId,
  5724. DEFAULT_SEND_TIME_OUT,
  5725. HandleToUlong(entry->Handle),
  5726. WM_DEVICECHANGE,
  5727. (WPARAM)cancelEventId,
  5728. (LPARAM)pNotifyData,
  5729. (LONG*)&result)) {
  5730. ntStatus = STATUS_SUCCESS;
  5731. } else {
  5732. ntStatus = STATUS_UNSUCCESSFUL;
  5733. }
  5734. } except (EXCEPTION_EXECUTE_HANDLER) {
  5735. KdPrintEx((DPFLTR_PNPMGR_ID,
  5736. DBGF_ERRORS,
  5737. "UMPNPMGR: Exception calling WinStationSendWindowMessage!\n"));
  5738. ASSERT(0);
  5739. ntStatus = STATUS_SUCCESS;
  5740. }
  5741. }
  5742. LockNotifyList(&notifyList->Lock);
  5743. if (!NT_SUCCESS(ntStatus)) {
  5744. if (ntStatus == STATUS_INVALID_HANDLE) {
  5745. //
  5746. // window handle no longer exists, cleanup this entry
  5747. //
  5748. entry->Freed |= (PNP_UNREG_FREE|PNP_UNREG_DEFER|PNP_UNREG_WIN|PNP_UNREG_CANCEL);
  5749. DeleteNotifyEntry(entry,FALSE);
  5750. } else if (ntStatus == STATUS_UNSUCCESSFUL) {
  5751. KdPrintEx((DPFLTR_PNPMGR_ID,
  5752. DBGF_EVENT,
  5753. "UMPNPMGR: Window '%ws' timed out on cancel notification event\n",
  5754. entry->ClientName));
  5755. LogWarningEvent(WRN_CANCEL_NOTIFICATION_TIMED_OUT,
  5756. 1,
  5757. entry->ClientName);
  5758. }
  5759. }
  5760. }
  5761. break;
  5762. case DEVICE_NOTIFY_COMPLETION_HANDLE:
  5763. //
  5764. // NOTE: Completion handles not currently implemented.
  5765. //
  5766. NOTHING;
  5767. break;
  5768. }
  5769. }
  5770. }
  5771. #ifdef _WIN64
  5772. //
  5773. // Free the 32-bit DEV_BROADCAST_HANDLE structure, if we allocated one.
  5774. //
  5775. if (HandleData32 != NULL) {
  5776. HeapFree(ghPnPHeap, 0, HandleData32);
  5777. }
  5778. #endif // _WIN64
  5779. return TRUE;
  5780. } // SendCancelNotification
  5781. VOID
  5782. BroadcastCompatibleDeviceMsg(
  5783. IN DWORD EventId,
  5784. IN PDEV_BROADCAST_DEVICEINTERFACE ClassData,
  5785. IN PDWORD CurrentMask
  5786. )
  5787. /*++
  5788. Routine Description:
  5789. Deliver Win9x compatible event notification for the arrival and removal of
  5790. device interfaces to volume and port class devices.
  5791. Arguments:
  5792. EventId - Specifies the DBT style event id.
  5793. Currently, only DBT_DEVICEARRIVAL and DBT_DEVICEREMOVECOMPLETE
  5794. events are supported.
  5795. ClassData - Pointer to a PDEV_BROADCAST_DEVICEINTERFACE structure that is
  5796. already filled out with the pertinent data.
  5797. Currently, only volume and port class device interfaces are
  5798. supported.
  5799. (For volume class devices, the symbolic link
  5800. ClassData->dbcc_name is OPTIONAL - see Notes below.)
  5801. Return Value:
  5802. None.
  5803. Notes:
  5804. For volume class device broadcasts only, this routine may also be called
  5805. generically, with no symbolic link information provided. When no symbolic
  5806. link information to a volume device is supplied, the broadcast mask is
  5807. determined only from the current drive letter mappings and the global drive
  5808. letter mask (gAllDrivesMask) prior to this event. In this case, the global
  5809. drive letter mask is NOT updated here, and the caller should do so after
  5810. both the removal and arrival broadcasts in response to the name change are
  5811. performed. Currently, this type of call is only made from
  5812. BroadcastVolumeNameChange.
  5813. For volume class interface DBT_DEVICEREMOVECOMPLETE broadcasts, the drive
  5814. letter mask to be broadcast is always determined only by comparing drive
  5815. letters present prior to the remove of the interface with those present at
  5816. this time. This is done because the former mount points for this device are
  5817. no longer known when the interface removal event is received. Even so, it
  5818. is still necessary for the symbolic link corresponding to this interface to
  5819. be supplied to distinguish between the actual removal of the interface
  5820. (where the global drive letter mask is updated), the above case, where it is
  5821. not.
  5822. --*/
  5823. {
  5824. LONG status = ERROR_SUCCESS;
  5825. LONG result = 0;
  5826. DWORD recipients = BSM_APPLICATIONS | BSM_ALLDESKTOPS;
  5827. DWORD flags = BSF_IGNORECURRENTTASK | BSF_NOHANG;
  5828. HRESULT hr;
  5829. //
  5830. // Validate the input event data.
  5831. //
  5832. if ((ClassData->dbcc_devicetype != DBT_DEVTYP_DEVICEINTERFACE) ||
  5833. (ClassData->dbcc_size < sizeof(DEV_BROADCAST_DEVICEINTERFACE))) {
  5834. return;
  5835. }
  5836. if ((EventId != DBT_DEVICEARRIVAL) &&
  5837. (EventId != DBT_DEVICEREMOVECOMPLETE)) {
  5838. //
  5839. // If the requested Event is not DBT_DEVICEARRIVAL or
  5840. // DBT_DEVICEREMOVECOMPLETE, don't broadcast any messages.
  5841. //
  5842. return;
  5843. }
  5844. if (GuidEqual(&ClassData->dbcc_classguid, (LPGUID)&GUID_DEVINTERFACE_VOLUME)) {
  5845. //
  5846. // Volume class device interface events.
  5847. //
  5848. PDEV_BROADCAST_VOLUME pVolume;
  5849. DWORD broadcastmask = 0;
  5850. if (EventId == DBT_DEVICEARRIVAL) {
  5851. if (ClassData->dbcc_name[0] == L'\0') {
  5852. //
  5853. // If no symbolic link name was supplied, we were asked to
  5854. // broadcast volume device arrivals in response to a volume name
  5855. // change event. Broadcast any new drive letters found.
  5856. //
  5857. DWORD currentmask;
  5858. //
  5859. // If a current drive letter mask was provided, use it.
  5860. //
  5861. if (ARGUMENT_PRESENT(CurrentMask)) {
  5862. currentmask = *CurrentMask;
  5863. } else {
  5864. currentmask = GetAllVolumeMountPoints();
  5865. }
  5866. broadcastmask = (~gAllDrivesMask & currentmask);
  5867. } else {
  5868. //
  5869. // For volume class device interface arrival events, the volume
  5870. // device name is retrieved from the interface, and is compared to
  5871. // the volume names of all drive letter mountpoints in the system to
  5872. // determine the drive letter(s) corresponding to the arriving
  5873. // volume device interface.
  5874. //
  5875. LPWSTR devicePath, p;
  5876. WCHAR thisVolumeName[MAX_PATH];
  5877. WCHAR enumVolumeName[MAX_PATH];
  5878. WCHAR driveName[4];
  5879. ULONG length;
  5880. BOOL bResult;
  5881. //
  5882. // Allocate a temporary buffer for munging the symbolic link, with
  5883. // enough room for a trailing '\' char (should we need to add one),
  5884. // and the terminating NULL char.
  5885. //
  5886. length = lstrlen(ClassData->dbcc_name);
  5887. devicePath = HeapAlloc(ghPnPHeap, 0,
  5888. (length+1)*sizeof(WCHAR)+sizeof(UNICODE_NULL));
  5889. if (devicePath == NULL) {
  5890. status = ERROR_NOT_ENOUGH_MEMORY;
  5891. goto Clean0;
  5892. }
  5893. hr = StringCchCopy(devicePath,
  5894. length + 1,
  5895. ClassData->dbcc_name);
  5896. ASSERT(SUCCEEDED(hr));
  5897. //
  5898. // Search for the occurence of a refstring (if any) by looking for the
  5899. // next occurance of a '\' char, after the initial "\\?\".
  5900. //
  5901. p = wcschr(&(devicePath[4]), TEXT('\\'));
  5902. if (!p) {
  5903. //
  5904. // No refstring is present in the symbolic link; add a trailing
  5905. // '\' char (as required by GetVolumeNameForVolumeMountPoint).
  5906. //
  5907. p = devicePath + length;
  5908. *p = TEXT('\\');
  5909. }
  5910. //
  5911. // If there is no refstring present, we have added a trailing '\',
  5912. // and placed p at that position. If a refstring is present, p is
  5913. // at the position of the '\' char that separates the munged device
  5914. // interface name, and the refstring; since we don't need the
  5915. // refstring to reach the parent interface key, we can use the next
  5916. // char for NULL terminating the string in both cases.
  5917. //
  5918. p++;
  5919. *p = UNICODE_NULL;
  5920. //
  5921. // Get the Volume Name for this Mount Point
  5922. //
  5923. thisVolumeName[0] = TEXT('\0');
  5924. bResult = GetVolumeNameForVolumeMountPoint(devicePath,
  5925. thisVolumeName,
  5926. MAX_PATH);
  5927. HeapFree(ghPnPHeap, 0, devicePath);
  5928. if (!bResult || !thisVolumeName[0]) {
  5929. status = ERROR_BAD_PATHNAME;
  5930. goto Clean0;
  5931. }
  5932. //
  5933. // Initialize the drive name string
  5934. //
  5935. driveName[1] = TEXT(':');
  5936. driveName[2] = TEXT('\\');
  5937. driveName[3] = UNICODE_NULL;
  5938. //
  5939. // Find the drive letter mount point(s) for this volume device by
  5940. // enumerating all possible volume mount points and comparing each
  5941. // mounted volume name with the name of the volume corresponding to
  5942. // this device interface.
  5943. //
  5944. for (driveName[0] = TEXT('A'); driveName[0] <= TEXT('Z'); driveName[0]++) {
  5945. enumVolumeName[0] = UNICODE_NULL;
  5946. GetVolumeNameForVolumeMountPoint(driveName, enumVolumeName, MAX_PATH);
  5947. if (CompareString(
  5948. LOCALE_INVARIANT, NORM_IGNORECASE,
  5949. thisVolumeName, -1,
  5950. enumVolumeName, -1) == CSTR_EQUAL) {
  5951. //
  5952. // Add the corresponding bit for this drive letter to the mask
  5953. //
  5954. broadcastmask |= (1 << (driveName[0] - TEXT('A')));
  5955. }
  5956. }
  5957. //
  5958. // Update the global drive letter mask to include new drive
  5959. // letters only. Note that we don't set it to the current mask,
  5960. // because that may omit volumes that have been removed, but
  5961. // that we have not yet received removal notification for -
  5962. // which we would not notice as removed when the removal
  5963. // notification finally came.
  5964. //
  5965. gAllDrivesMask |= broadcastmask;
  5966. }
  5967. } else if (EventId == DBT_DEVICEREMOVECOMPLETE) {
  5968. //
  5969. // For volume class device interface removal events, the volume name
  5970. // (and hence, drive mountpoints) corresponding to this device
  5971. // interface has already been removed, and is no longer available.
  5972. // Instead, the bitmask of all drive letter mountpoints for current
  5973. // physical volumes is compared with that prior to the removal of
  5974. // this device. All missing drive mountpoints are assumed to have
  5975. // been associated with this volume device interface, and are
  5976. // subsequently broadcasted with this interface removal
  5977. // notification.
  5978. //
  5979. DWORD currentmask;
  5980. //
  5981. // Determine all current volume mount points, and broadcast any
  5982. // missing drive letters.
  5983. //
  5984. //
  5985. // If a current drive letter mask was provided, use it.
  5986. //
  5987. if (ARGUMENT_PRESENT(CurrentMask)) {
  5988. currentmask = *CurrentMask;
  5989. } else {
  5990. currentmask = GetAllVolumeMountPoints();
  5991. }
  5992. broadcastmask = (gAllDrivesMask & ~currentmask);
  5993. //
  5994. // Only update the global drive letter in response to the
  5995. // removal of a interface. For volume name changes, we update
  5996. // outside of this routine.
  5997. //
  5998. if (ClassData->dbcc_name[0] != L'\0') {
  5999. //
  6000. // Update the global drive letter mask to exclude removed drive
  6001. // letters only. Note that we don't set it to the current mask,
  6002. // because that may include volumes that have been added, but
  6003. // that we have not yet received arrival notification for -
  6004. // which we would not notice as added when the arrival
  6005. // notification finally came.
  6006. //
  6007. gAllDrivesMask &= ~broadcastmask;
  6008. }
  6009. }
  6010. //
  6011. // If there is nothing to broadcast, then we're done.
  6012. //
  6013. if (broadcastmask == 0) {
  6014. status = ERROR_SUCCESS;
  6015. goto Clean0;
  6016. }
  6017. //
  6018. // Fill out the volume broadcast structure.
  6019. //
  6020. pVolume =
  6021. (PDEV_BROADCAST_VOLUME)HeapAlloc(
  6022. ghPnPHeap, 0,
  6023. sizeof(DEV_BROADCAST_VOLUME));
  6024. if (pVolume == NULL) {
  6025. status = ERROR_NOT_ENOUGH_MEMORY;
  6026. goto Clean0;
  6027. }
  6028. pVolume->dbcv_size = sizeof(DEV_BROADCAST_VOLUME);
  6029. pVolume->dbcv_devicetype = DBT_DEVTYP_VOLUME;
  6030. pVolume->dbcv_flags = 0;
  6031. pVolume->dbcv_reserved = 0;
  6032. pVolume->dbcv_unitmask = broadcastmask;
  6033. //
  6034. // Broadcast the message to all components
  6035. //
  6036. result = BroadcastSystemMessage(flags,
  6037. &recipients,
  6038. WM_DEVICECHANGE,
  6039. EventId,
  6040. (LPARAM)pVolume);
  6041. if (fpWinStationBroadcastSystemMessage) {
  6042. try {
  6043. fpWinStationBroadcastSystemMessage(SERVERNAME_CURRENT,
  6044. TRUE,
  6045. 0,
  6046. DEFAULT_BROADCAST_TIME_OUT,
  6047. flags,
  6048. &recipients,
  6049. WM_DEVICECHANGE,
  6050. (WPARAM)EventId,
  6051. (LPARAM)pVolume,
  6052. &result);
  6053. } except (EXCEPTION_EXECUTE_HANDLER) {
  6054. KdPrintEx((DPFLTR_PNPMGR_ID,
  6055. DBGF_ERRORS,
  6056. "UMPNPMGR: Exception calling WinStationBroadcastSystemMessage!\n"));
  6057. ASSERT(0);
  6058. }
  6059. }
  6060. //
  6061. // Free the broadcast structure.
  6062. //
  6063. HeapFree(ghPnPHeap, 0, pVolume);
  6064. } else if ((GuidEqual(&ClassData->dbcc_classguid, (LPGUID)&GUID_DEVINTERFACE_PARALLEL)) ||
  6065. (GuidEqual(&ClassData->dbcc_classguid, (LPGUID)&GUID_DEVINTERFACE_COMPORT))) {
  6066. //
  6067. // COM and LPT port class device interface events.
  6068. //
  6069. PDEV_BROADCAST_PORT pPort;
  6070. LPWSTR p;
  6071. LPWSTR deviceInterfacePath = NULL;
  6072. LPWSTR deviceInterfaceName = NULL;
  6073. LPWSTR deviceInstance = NULL;
  6074. HKEY hKey;
  6075. WCHAR szTempString[MAX_PATH];
  6076. ULONG ulSize;
  6077. size_t DevicePathLen = 0, DeviceClassesLen = 0;
  6078. //
  6079. // Convert the interface class GUID to a string.
  6080. //
  6081. if (StringFromGuid((LPGUID)&ClassData->dbcc_classguid,
  6082. szTempString,
  6083. SIZECHARS(szTempString)) != NO_ERROR) {
  6084. status = ERROR_INVALID_PARAMETER;
  6085. goto Clean0;
  6086. }
  6087. //
  6088. // Build the complete path to the device interface key for this device.
  6089. //
  6090. hr = StringCchLength(ClassData->dbcc_name,
  6091. STRSAFE_MAX_CCH,
  6092. &DevicePathLen);
  6093. ASSERT(SUCCEEDED(hr));
  6094. ASSERT(DevicePathLen > 4);
  6095. if (DevicePathLen < 4) {
  6096. status = ERROR_INVALID_PARAMETER;
  6097. goto Clean0;
  6098. }
  6099. hr = StringCchLength(pszRegPathDeviceClasses,
  6100. MAX_CM_PATH,
  6101. &DeviceClassesLen);
  6102. ASSERT(SUCCEEDED(hr));
  6103. ASSERT(DeviceClassesLen > 0);
  6104. if ((FAILED(hr)) || (DeviceClassesLen == 0)) {
  6105. status = ERROR_INVALID_PARAMETER;
  6106. goto Clean0;
  6107. }
  6108. ulSize = (ULONG)DeviceClassesLen + 1 +
  6109. MAX_GUID_STRING_LEN + (ULONG)DevicePathLen + 1;
  6110. deviceInterfacePath =
  6111. (LPWSTR)HeapAlloc(
  6112. ghPnPHeap, 0,
  6113. (ulSize * sizeof(WCHAR)));
  6114. if (deviceInterfacePath == NULL) {
  6115. status = ERROR_NOT_ENOUGH_MEMORY;
  6116. goto Clean0;
  6117. }
  6118. //
  6119. // Copy the path to the "DeviceClasses" registry key
  6120. //
  6121. hr = StringCchCopy(deviceInterfacePath,
  6122. ulSize,
  6123. pszRegPathDeviceClasses);
  6124. if (SUCCEEDED(hr)) {
  6125. hr = StringCchCat(deviceInterfacePath,
  6126. ulSize,
  6127. L"\\");
  6128. }
  6129. //
  6130. // Append the interface class GUID to the registry path
  6131. //
  6132. if (SUCCEEDED(hr)) {
  6133. hr = StringCchCat(deviceInterfacePath,
  6134. ulSize,
  6135. szTempString);
  6136. }
  6137. if (SUCCEEDED(hr)) {
  6138. hr = StringCchCatEx(deviceInterfacePath,
  6139. ulSize,
  6140. L"\\",
  6141. &deviceInterfaceName,
  6142. NULL,
  6143. STRSAFE_NULL_ON_FAILURE);
  6144. }
  6145. //
  6146. // Append the symbolic link name to the registry path.
  6147. //
  6148. if (SUCCEEDED(hr)) {
  6149. hr = StringCchCat(deviceInterfacePath,
  6150. ulSize,
  6151. ClassData->dbcc_name);
  6152. }
  6153. ASSERT(SUCCEEDED(hr));
  6154. if (FAILED(hr)) {
  6155. status = ERROR_BAD_PATHNAME;
  6156. HeapFree(ghPnPHeap, 0, deviceInterfacePath);
  6157. goto Clean0;
  6158. }
  6159. ASSERT(deviceInterfaceName != NULL);
  6160. //
  6161. // Munge the symbolic link name to form the interface key name.
  6162. // (Note: The munging process is optimized here; we only have to munge
  6163. // the leading "\\?\" segment since the rest of the given symbolic link
  6164. // is already munged, with the exception of the refstring seperator char,
  6165. // if any, which we will handle below.)
  6166. //
  6167. deviceInterfaceName[0] = TEXT('#');
  6168. deviceInterfaceName[1] = TEXT('#');
  6169. ASSERT(deviceInterfaceName[2] == TEXT('?'));
  6170. deviceInterfaceName[3] = TEXT('#');
  6171. //
  6172. // Search for the begininng of the refstring (if any), by looking for
  6173. // the next occurance of a '\' char.
  6174. //
  6175. p = wcschr(&(deviceInterfaceName[4]), TEXT('\\'));
  6176. //
  6177. // If there is a RefString component to the device interface path,
  6178. // remove it from the path by replacing the path separator character
  6179. // with a NULL-terminating character.
  6180. //
  6181. if (p != NULL) {
  6182. *p = L'\0';
  6183. }
  6184. //
  6185. // Open the device interface key
  6186. //
  6187. status = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  6188. deviceInterfacePath,
  6189. 0,
  6190. KEY_READ,
  6191. &hKey);
  6192. HeapFree(ghPnPHeap, 0, deviceInterfacePath);
  6193. if (status != ERROR_SUCCESS) {
  6194. hKey = NULL;
  6195. goto Clean0;
  6196. }
  6197. //
  6198. // Allocate a string large enough to store the path from the Enum key,
  6199. // to the "\Device Parameters" subkey of this Device Instance's registry
  6200. // key.
  6201. //
  6202. ulSize = MAX_CM_PATH * sizeof(WCHAR);
  6203. deviceInstance =
  6204. (LPWSTR)HeapAlloc(
  6205. ghPnPHeap, 0, ulSize);
  6206. if (deviceInstance == NULL) {
  6207. status = ERROR_NOT_ENOUGH_MEMORY;
  6208. RegCloseKey(hKey);
  6209. hKey = NULL;
  6210. goto Clean0;
  6211. }
  6212. //
  6213. // Retrieve the device instance that owns this interface.
  6214. //
  6215. status = RegQueryValueEx(hKey,
  6216. pszRegValueDeviceInstance,
  6217. 0,
  6218. NULL,
  6219. (LPBYTE)deviceInstance,
  6220. &ulSize);
  6221. RegCloseKey(hKey);
  6222. hKey = NULL;
  6223. if (status != ERROR_SUCCESS) {
  6224. HeapFree(ghPnPHeap, 0, deviceInstance);
  6225. goto Clean0;
  6226. }
  6227. //
  6228. // Open the "Device Parameters" key under the HKLM\SYSTEM\CCS\Enum
  6229. // subkey for this DeviceInstance.
  6230. //
  6231. hr = StringCchCat(deviceInstance,
  6232. MAX_CM_PATH,
  6233. L"\\");
  6234. if (SUCCEEDED(hr)) {
  6235. hr = StringCchCat(deviceInstance,
  6236. MAX_CM_PATH,
  6237. pszRegKeyDeviceParam);
  6238. }
  6239. if (SUCCEEDED(hr)) {
  6240. status = RegOpenKeyEx(ghEnumKey,
  6241. deviceInstance,
  6242. 0,
  6243. KEY_READ,
  6244. &hKey);
  6245. } else {
  6246. status = ERROR_BAD_PATHNAME;
  6247. }
  6248. HeapFree(ghPnPHeap, 0, deviceInstance);
  6249. if (status != ERROR_SUCCESS) {
  6250. goto Clean0;
  6251. }
  6252. //
  6253. // Query the "PortName" value for the compatible name of this device.
  6254. //
  6255. ulSize = MAX_PATH*sizeof(WCHAR);
  6256. status = RegQueryValueEx(hKey,
  6257. pszRegValuePortName,
  6258. 0,
  6259. NULL,
  6260. (LPBYTE)szTempString,
  6261. &ulSize);
  6262. RegCloseKey(hKey);
  6263. if (status != ERROR_SUCCESS) {
  6264. goto Clean0;
  6265. }
  6266. //
  6267. // Fill out the port broadcast structure.
  6268. //
  6269. pPort =
  6270. (PDEV_BROADCAST_PORT)HeapAlloc(
  6271. ghPnPHeap, 0,
  6272. sizeof(DEV_BROADCAST_PORT) + ulSize);
  6273. if (pPort == NULL) {
  6274. status = ERROR_NOT_ENOUGH_MEMORY;
  6275. goto Clean0;
  6276. }
  6277. pPort->dbcp_size = sizeof(DEV_BROADCAST_PORT) + ulSize;
  6278. pPort->dbcp_devicetype = DBT_DEVTYP_PORT;
  6279. pPort->dbcp_reserved = 0;
  6280. hr = StringCbCopy(pPort->dbcp_name,
  6281. ulSize + sizeof(WCHAR),
  6282. szTempString);
  6283. ASSERT(SUCCEEDED(hr));
  6284. if (FAILED(hr)) {
  6285. HeapFree(ghPnPHeap, 0, pPort);
  6286. status = ERROR_BAD_PATHNAME;
  6287. goto Clean0;
  6288. }
  6289. //
  6290. // Broadcast the message to all components
  6291. //
  6292. result = BroadcastSystemMessage(flags,
  6293. &recipients,
  6294. WM_DEVICECHANGE,
  6295. EventId,
  6296. (LPARAM)pPort);
  6297. if (fpWinStationBroadcastSystemMessage) {
  6298. try {
  6299. fpWinStationBroadcastSystemMessage(SERVERNAME_CURRENT,
  6300. TRUE,
  6301. 0,
  6302. DEFAULT_BROADCAST_TIME_OUT,
  6303. flags,
  6304. &recipients,
  6305. WM_DEVICECHANGE,
  6306. (WPARAM)EventId,
  6307. (LPARAM)pPort,
  6308. &result);
  6309. } except (EXCEPTION_EXECUTE_HANDLER) {
  6310. KdPrintEx((DPFLTR_PNPMGR_ID,
  6311. DBGF_ERRORS,
  6312. "UMPNPMGR: Exception calling WinStationBroadcastSystemMessage!\n"));
  6313. ASSERT(0);
  6314. }
  6315. }
  6316. //
  6317. // Free the broadcast structure.
  6318. //
  6319. HeapFree(ghPnPHeap, 0, pPort);
  6320. }
  6321. Clean0:
  6322. return;
  6323. } // BroadcastCompatibleDeviceMsg
  6324. VOID
  6325. BroadcastVolumeNameChange(
  6326. VOID
  6327. )
  6328. /*++
  6329. Routine Description:
  6330. Perform Win9x compatible volume removal and arrival messages, to be called
  6331. in reponse to a volume name change event.
  6332. Arguments:
  6333. None.
  6334. Return Value:
  6335. None.
  6336. Notes:
  6337. The drive mask to be broadcast will be determined by comparing the current
  6338. drive letter mask with that prior to the event. The global drive letter
  6339. mask is also updated here, after all removal and arrival notifications have
  6340. been sent.
  6341. --*/
  6342. {
  6343. DEV_BROADCAST_DEVICEINTERFACE volumeNotify;
  6344. DWORD currentmask = 0;
  6345. //
  6346. // Fill out a DEV_BROADCAST_DEVICEINTERFACE structure.
  6347. //
  6348. ZeroMemory(&volumeNotify, sizeof(DEV_BROADCAST_DEVICEINTERFACE));
  6349. volumeNotify.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
  6350. volumeNotify.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
  6351. volumeNotify.dbcc_reserved = 0;
  6352. memcpy(&volumeNotify.dbcc_classguid, &GUID_DEVINTERFACE_VOLUME, sizeof(GUID));
  6353. //
  6354. // A null symbolic link name for dbcc_name designates that
  6355. // BroadcastCompatibleDeviceMsg is to determine the drive mask to
  6356. // broadcast by checking differences between the last broadcast drive
  6357. // mask (gAllDrivesMask), and the current drive mask.
  6358. //
  6359. // When broadcasting in response to a volume name change, we must wait until
  6360. // both removal and arrival messages have been sent before we can update the
  6361. // global drive letter mask. A null symbolic link name specifies that
  6362. // BroadcastCompatibleDeviceMsg should not update the global mask; this will
  6363. // be done here, after all broadcasts are complete.
  6364. //
  6365. volumeNotify.dbcc_name[0] = L'\0';
  6366. //
  6367. // Retrieve the current drive letter mask.
  6368. //
  6369. currentmask = GetAllVolumeMountPoints();
  6370. //
  6371. // Broadcast volume removal notification for any drive letter moint points
  6372. // no longer in use, followed by volume arrival notification for new
  6373. //
  6374. BroadcastCompatibleDeviceMsg(DBT_DEVICEREMOVECOMPLETE, &volumeNotify, &currentmask);
  6375. BroadcastCompatibleDeviceMsg(DBT_DEVICEARRIVAL, &volumeNotify, &currentmask);
  6376. //
  6377. // Now that both removal and arrival messages have been sent, update the
  6378. // global drive letter mask to reflect what we just broadcast.
  6379. //
  6380. gAllDrivesMask = currentmask;
  6381. return;
  6382. } // BroadcastVolumeNameChange
  6383. DWORD
  6384. GetAllVolumeMountPoints(
  6385. VOID
  6386. )
  6387. /*++
  6388. Routine Description:
  6389. Queries all drive letter mountpoints ('A'-'Z') and returns a bitmask
  6390. representing all such mount points currently in use by physical volume
  6391. devices.
  6392. Arguments:
  6393. None.
  6394. Return Value:
  6395. Returns a bit mask representing drive letter mount points ('A'-'Z') in use
  6396. by physical volume devices.
  6397. Note:
  6398. The returned bit mask includes only mount points for physical volume class
  6399. devices. Network mounted drives are not included.
  6400. --*/
  6401. {
  6402. WCHAR driveName[4];
  6403. WCHAR volumeName[MAX_PATH];
  6404. DWORD driveLetterMask=0;
  6405. //
  6406. // Initialize drive name and mask
  6407. //
  6408. driveName[1] = TEXT(':');
  6409. driveName[2] = TEXT('\\');
  6410. driveName[3] = UNICODE_NULL;
  6411. //
  6412. // Compare the name of this volume with those of all mounted volumes in the system
  6413. //
  6414. for (driveName[0] = TEXT('A'); driveName[0] <= TEXT('Z'); driveName[0]++) {
  6415. volumeName[0] = UNICODE_NULL;
  6416. if (!GetVolumeNameForVolumeMountPoint(driveName,
  6417. volumeName,
  6418. MAX_PATH)) {
  6419. continue;
  6420. }
  6421. if (volumeName[0] != UNICODE_NULL) {
  6422. //
  6423. // Add the corresponding bit for this drive letter to the mask
  6424. //
  6425. driveLetterMask |= (1 << (driveName[0] - TEXT('A')));
  6426. }
  6427. }
  6428. return driveLetterMask;
  6429. } // GetAllVolumeMountPoints
  6430. ULONG
  6431. NotifyPower(
  6432. IN DWORD ServiceControl,
  6433. IN DWORD EventId,
  6434. IN DWORD EventData,
  6435. IN DWORD Flags,
  6436. OUT PPNP_VETO_TYPE VetoType OPTIONAL,
  6437. OUT LPWSTR VetoName OPTIONAL,
  6438. IN OUT PULONG VetoNameLength OPTIONAL
  6439. )
  6440. /*++
  6441. Routine Description:
  6442. This routine notifies services of system-wide power events.
  6443. Arguments:
  6444. ServiceControl - Specifies class of service event (power, device, hwprofile
  6445. change).
  6446. EventId - Specifies the PBT style event id for the power event.
  6447. (see sdk\inc\pbt.h for defined power events)
  6448. EventData - Specifies additional data for the event.
  6449. Flags - Specifies BroadcastSystemMessage BSF_ flags.
  6450. VetoType - For query-type events, supplies the address of a variable to
  6451. receive, upon failure, the type of the component responsible
  6452. for vetoing the request.
  6453. VetoName - For query-type events, supplies the address of a variable to
  6454. receive, upon failure, the name of the component
  6455. responsible for vetoing the request.
  6456. VetoNameLength - For query-type events, supplies the address of a variable
  6457. specifying the size of the of buffer specified by the
  6458. VetoName parameter. Upon failure, this address will specify
  6459. the length of the string stored in that buffer by this
  6460. routine.
  6461. Return Value:
  6462. Returns FALSE in the case of a vetoed query event, TRUE otherwise.
  6463. Notes:
  6464. This routine currently only notifies services of power events. Notification
  6465. to windows is handled directly by USER.
  6466. Power events are placed in the plug and play event queue via a private call
  6467. from USER, for the explicit purpose of notifying services of system-wide
  6468. power events, done here.
  6469. --*/
  6470. {
  6471. NTSTATUS status=STATUS_SUCCESS;
  6472. PPNP_NOTIFY_ENTRY entry, nextEntry;
  6473. PPNP_NOTIFY_LIST notifyList = NULL;
  6474. BOOL bLocked = FALSE;
  6475. DWORD err;
  6476. LONG result;
  6477. //
  6478. // NOTE: Services are not currently sent EventData for power events. The
  6479. // SCM currently ASSERTs that this will always be zero.
  6480. //
  6481. // The SDK states that WM_POWERBROADCAST "RESUME" type messages may contain
  6482. // the PBTF_APMRESUMEFROMFAILURE flag in the LPARAM field, and that "QUERY"
  6483. // type messages may contain a single bit in the LPARAM field specifying
  6484. // whether user interaction is allowed.
  6485. //
  6486. // Although these don't currently seem to be used much (even for window
  6487. // messages, as stated), shouldn't EventData also be valid for service power
  6488. // event notification?
  6489. //
  6490. UNREFERENCED_PARAMETER(EventData);
  6491. //
  6492. // If we're doing a query, then VetoType, VetoName, and VetoNameLength must
  6493. // all be specified.
  6494. //
  6495. ASSERT(!(Flags & BSF_QUERY) || (VetoType && VetoName && VetoNameLength));
  6496. if (!(Flags & BSF_QUERY) && (VetoNameLength != NULL)) {
  6497. //
  6498. // Not vetoable.
  6499. //
  6500. *VetoNameLength = 0;
  6501. }
  6502. notifyList = &ServiceList[CINDEX_POWEREVENT];
  6503. LockNotifyList (&notifyList->Lock);
  6504. bLocked = TRUE;
  6505. //
  6506. //Services only. User sends out messages to apps
  6507. //
  6508. try {
  6509. //
  6510. //Notify the services
  6511. //
  6512. entry = GetFirstNotifyEntry(notifyList,0);
  6513. if (!entry) {
  6514. //
  6515. // can't veto if no one registered.
  6516. //
  6517. if (VetoNameLength != NULL) {
  6518. *VetoNameLength = 0;
  6519. }
  6520. }
  6521. while (entry) {
  6522. nextEntry = GetNextNotifyEntry(entry,0);
  6523. if (entry->Unregistered) {
  6524. entry = nextEntry;
  6525. continue;
  6526. }
  6527. //
  6528. // This is a direct call, not a message via. USER
  6529. //
  6530. if (pServiceControlCallback) {
  6531. UnlockNotifyList (&notifyList->Lock);
  6532. bLocked = FALSE;
  6533. err = NO_ERROR;
  6534. try {
  6535. (pServiceControlCallback)((SERVICE_STATUS_HANDLE)entry->Handle,
  6536. ServiceControl,
  6537. EventId,
  6538. (LPARAM)NULL, // Currently, no EventData allowed for services
  6539. &err);
  6540. } except (EXCEPTION_EXECUTE_HANDLER) {
  6541. KdPrintEx((DPFLTR_PNPMGR_ID,
  6542. DBGF_ERRORS,
  6543. "UMPNPMGR: Exception calling Service Control Manager!\n"));
  6544. err = NO_ERROR;
  6545. ASSERT(0);
  6546. }
  6547. LockNotifyList (&notifyList->Lock);
  6548. bLocked = TRUE;
  6549. //
  6550. // convert Win32 error into window message-style return
  6551. // value.
  6552. //
  6553. if (err == NO_ERROR) {
  6554. result = TRUE;
  6555. } else {
  6556. KdPrintEx((DPFLTR_PNPMGR_ID,
  6557. DBGF_EVENT,
  6558. "UMPNPMGR: Service %ws responded to PowerEvent, with status=0x%08lx\n",
  6559. entry->ClientName,
  6560. err));
  6561. //
  6562. // This service specifically requested to receive this
  6563. // notification - it should know how to handle it.
  6564. //
  6565. ASSERT(err != ERROR_CALL_NOT_IMPLEMENTED);
  6566. //
  6567. // Log the error the service used to veto.
  6568. //
  6569. LogWarningEvent(WRN_POWER_EVENT_SERVICE_VETO,
  6570. 1,
  6571. entry->ClientName);
  6572. result = BROADCAST_QUERY_DENY;
  6573. }
  6574. //
  6575. // Check if one of the QUERY messages was denied
  6576. //
  6577. if ((Flags & BSF_QUERY) &&
  6578. (result == BROADCAST_QUERY_DENY)) {
  6579. ServiceVeto(entry, VetoType, VetoName, VetoNameLength );
  6580. //
  6581. // This service vetoed the query, tell everyone else
  6582. // it was cancelled.
  6583. //
  6584. SendCancelNotification(entry,
  6585. ServiceControl,
  6586. EventId,
  6587. 0,
  6588. NULL,
  6589. NULL);
  6590. status = STATUS_UNSUCCESSFUL;
  6591. break;
  6592. }
  6593. }
  6594. entry = nextEntry;
  6595. }
  6596. } except (EXCEPTION_EXECUTE_HANDLER){
  6597. KdPrintEx((DPFLTR_PNPMGR_ID,
  6598. DBGF_ERRORS,
  6599. "UMPNPMGR: Exception delivering Power Notification to Service Control Manager\n"));
  6600. ASSERT(0);
  6601. }
  6602. if (bLocked) {
  6603. UnlockNotifyList (&notifyList->Lock);
  6604. }
  6605. //
  6606. // if successful, we are not returning veto info.
  6607. //
  6608. if (NT_SUCCESS(status) && (VetoNameLength != NULL)) {
  6609. *VetoNameLength = 0;
  6610. }
  6611. return (NT_SUCCESS(status));
  6612. } // NotifyPower
  6613. CONFIGRET
  6614. RegisterServiceNotification(
  6615. IN SERVICE_STATUS_HANDLE hService,
  6616. IN LPWSTR pszService,
  6617. IN DWORD scmControls,
  6618. IN BOOL bServiceStopped
  6619. )
  6620. /*++
  6621. Routine Description:
  6622. This routine is called directly and privately by the service controller.
  6623. It allows the SCM to register or unregister services for events sent by this
  6624. service.
  6625. Arguments:
  6626. hService - Specifies the service handle.
  6627. pszService - Specifies the name of the service.
  6628. scmControls - Specifies the messages that SCM wants to listen to.
  6629. bServiceStopped - Specifies whether the service is stopped.
  6630. Return Value:
  6631. Return CR_SUCCESS if the function succeeds, otherwise it returns one
  6632. of the CR_* errors.
  6633. Notes:
  6634. This routine is called anytime a service changes the state of the
  6635. SERVICE_ACCEPT_POWEREVENT or SERVICE_ACCEPT_HARDWAREPROFILECHANGE flags in
  6636. its list of accepted controls.
  6637. This routine is also called by the SCM whenever any service has stopped, to
  6638. make sure that the specified service status handle is no longer registered
  6639. to receive SERVICE_CONTROL_DEVICEEVENT events.
  6640. Although it is the responsibility of the service to unregister for any
  6641. device event notifications that it has registered to receive before it is
  6642. stopped, its service status handle may be reused by the service controller,
  6643. so we must clean up any remaining device event registrations so that other
  6644. services will not receive them instead.
  6645. This is necessary for shared process services, since RPC rundown on the
  6646. notification handle will not occur until the service's process exits, which
  6647. may be long after the service has stopped.
  6648. --*/
  6649. {
  6650. ULONG cBits, i=0;
  6651. CONFIGRET Status = CR_SUCCESS;
  6652. PPNP_NOTIFY_ENTRY entry = NULL, curentry, nextentry;
  6653. PLOCKINFO LockHeld = NULL;
  6654. //
  6655. // Filter out the accepted controls we care about.
  6656. //
  6657. cBits = MapSCMControlsToControlBit(scmControls);
  6658. //
  6659. // If we were called because the service was stopped, make sure that we
  6660. // always unregister for all notifications.
  6661. //
  6662. if (bServiceStopped) {
  6663. ASSERT(cBits == 0);
  6664. cBits = 0;
  6665. }
  6666. try {
  6667. EnterCriticalSection(&RegistrationCS);
  6668. //
  6669. // Add or remove an entry in the array for each control bits.
  6670. //
  6671. for (i = 0;i< SERVICE_NUM_CONTROLS;i++) {
  6672. if (LockNotifyList(&ServiceList[i].Lock)) {
  6673. LockHeld = &ServiceList[i].Lock;
  6674. } else {
  6675. //
  6676. // Couldn't acquire the lock. Just move on to the next control
  6677. // bit.
  6678. //
  6679. continue;
  6680. }
  6681. //
  6682. // Check to see if an entry for this service handle already exists
  6683. // in our list.
  6684. //
  6685. for (curentry = GetFirstNotifyEntry(&ServiceList[i],0);
  6686. curentry;
  6687. curentry = GetNextNotifyEntry(curentry,0)) {
  6688. if (curentry->Handle == (HANDLE)hService) {
  6689. break;
  6690. }
  6691. }
  6692. //
  6693. // At this point, if curentry is non-NULL, then the service
  6694. // handle is already in our list, otherwise, it is not.
  6695. //
  6696. if (cBits & (1 << i)) {
  6697. //
  6698. // If entry isn't already in the list, then add it.
  6699. //
  6700. if (!curentry) {
  6701. entry = HeapAlloc(ghPnPHeap, 0, sizeof(PNP_NOTIFY_ENTRY));
  6702. if (NULL == entry) {
  6703. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  6704. Status = CR_OUT_OF_MEMORY;
  6705. UnlockNotifyList(LockHeld);
  6706. LockHeld = NULL;
  6707. goto Clean0;
  6708. }
  6709. RtlZeroMemory (entry,sizeof (PNP_NOTIFY_ENTRY));
  6710. entry->Handle = (HANDLE)hService;
  6711. entry->Signature = SERVICE_ENTRY_SIGNATURE;
  6712. entry->Freed = 0;
  6713. entry->Flags = DEVICE_NOTIFY_SERVICE_HANDLE;
  6714. entry->ClientName = NULL;
  6715. if (ARGUMENT_PRESENT(pszService)) {
  6716. HRESULT hr;
  6717. size_t ServiceNameLen = 0;
  6718. hr = StringCchLength(pszService,
  6719. MAX_SERVICE_NAME_LEN,
  6720. &ServiceNameLen);
  6721. if (FAILED(hr)) {
  6722. ServiceNameLen = MAX_SERVICE_NAME_LEN - 1;
  6723. }
  6724. entry->ClientName =
  6725. (LPWSTR)HeapAlloc(
  6726. ghPnPHeap, 0,
  6727. (ServiceNameLen+1)*sizeof(WCHAR));
  6728. if (entry->ClientName == NULL) {
  6729. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  6730. Status = CR_OUT_OF_MEMORY;
  6731. HeapFree(ghPnPHeap,0,entry);
  6732. UnlockNotifyList(LockHeld);
  6733. LockHeld = NULL;
  6734. goto Clean0;
  6735. }
  6736. //
  6737. // Copy to the allocated buffer, truncating if necessary.
  6738. //
  6739. hr = StringCchCopy(entry->ClientName,
  6740. ServiceNameLen + 1,
  6741. pszService);
  6742. ASSERT(SUCCEEDED(hr));
  6743. }
  6744. entry->u.Service.scmControls = scmControls;
  6745. MarkEntryWithList(entry,i);
  6746. AddNotifyEntry(&ServiceList[i], entry);
  6747. //
  6748. // Now reset entry pointer to NULL so we won't try to free
  6749. // it if we encounter an exception
  6750. //
  6751. entry = NULL;
  6752. }
  6753. } else {
  6754. //
  6755. // If entry is in the list, then remove it.
  6756. //
  6757. if (curentry) {
  6758. curentry->Freed |= (PNP_UNREG_FREE|PNP_UNREG_DEFER|PNP_UNREG_SERVICE);
  6759. DeleteNotifyEntry(curentry,TRUE);
  6760. }
  6761. }
  6762. UnlockNotifyList(LockHeld);
  6763. LockHeld = NULL;
  6764. }
  6765. //
  6766. // If the service is being stopped, unregister all outstanding device
  6767. // event registrations.
  6768. //
  6769. if (bServiceStopped) {
  6770. //
  6771. // If a notification is currently in progress, check to see if there
  6772. // are any entries for this service in the deferred RegisterList or
  6773. // UnregisterList.
  6774. //
  6775. if (gNotificationInProg != 0) {
  6776. if (RegisterList) {
  6777. PPNP_DEFERRED_LIST currReg, prevReg;
  6778. currReg = RegisterList;
  6779. prevReg = NULL;
  6780. while (currReg) {
  6781. ASSERT(currReg->Entry->Unregistered);
  6782. if (currReg->Entry->Handle == (HANDLE)hService) {
  6783. if (prevReg) {
  6784. prevReg->Next = currReg->Next;
  6785. } else {
  6786. RegisterList = currReg->Next;
  6787. }
  6788. HeapFree(ghPnPHeap, 0, currReg);
  6789. if (prevReg) {
  6790. currReg = prevReg->Next;
  6791. } else {
  6792. currReg = RegisterList;
  6793. }
  6794. } else {
  6795. prevReg = currReg;
  6796. currReg = currReg->Next;
  6797. }
  6798. }
  6799. }
  6800. if (UnregisterList) {
  6801. PPNP_DEFERRED_LIST currUnreg, prevUnreg;
  6802. currUnreg = UnregisterList;
  6803. prevUnreg = NULL;
  6804. while (currUnreg) {
  6805. ASSERT(currUnreg->Entry->Unregistered);
  6806. if (currUnreg->Entry->Handle == (HANDLE)hService) {
  6807. if (prevUnreg) {
  6808. prevUnreg->Next = currUnreg->Next;
  6809. } else {
  6810. UnregisterList = currUnreg->Next;
  6811. }
  6812. HeapFree(ghPnPHeap, 0, currUnreg);
  6813. if (prevUnreg) {
  6814. currUnreg = prevUnreg->Next;
  6815. } else {
  6816. currUnreg = UnregisterList;
  6817. }
  6818. } else {
  6819. prevUnreg = currUnreg;
  6820. currUnreg = currUnreg->Next;
  6821. }
  6822. }
  6823. }
  6824. }
  6825. //
  6826. // Check for any target device notification entries for this
  6827. // service.
  6828. //
  6829. for (i = 0; i < TARGET_HASH_BUCKETS; i++) {
  6830. if (LockNotifyList(&TargetList[i].Lock)) {
  6831. LockHeld = &TargetList[i].Lock;
  6832. } else {
  6833. //
  6834. // Couldn't acquire the lock. Just move on to the next list.
  6835. //
  6836. continue;
  6837. }
  6838. //
  6839. // Check to see if an entry for this service handle exists in
  6840. // this list.
  6841. //
  6842. curentry = GetFirstNotifyEntry(&TargetList[i],0);
  6843. while(curentry) {
  6844. nextentry = GetNextNotifyEntry(curentry,0);
  6845. if (curentry->Unregistered) {
  6846. curentry = nextentry;
  6847. continue;
  6848. }
  6849. if (curentry->Handle == (HANDLE)hService) {
  6850. //
  6851. // Remove the entry from the notification list.
  6852. //
  6853. curentry->Unregistered = TRUE;
  6854. curentry->Freed |= (PNP_UNREG_FREE|PNP_UNREG_TARGET);
  6855. DeleteNotifyEntry(curentry,FALSE);
  6856. //
  6857. // Only log a warning if the PlugPlay service has not
  6858. // already stopped. Otherwise, the client may actually
  6859. // have tried to unregister after we were shut down.
  6860. //
  6861. if (CurrentServiceState != SERVICE_STOPPED &&
  6862. CurrentServiceState != SERVICE_STOP_PENDING) {
  6863. KdPrintEx((DPFLTR_PNPMGR_ID,
  6864. DBGF_WARNINGS | DBGF_EVENT,
  6865. "UMPNPMGR: Service '%ws' "
  6866. "may have stopped without unregistering "
  6867. "for TargetDeviceChange notification.\n",
  6868. curentry->ClientName));
  6869. LogWarningEvent(WRN_STOPPED_SERVICE_REGISTERED,
  6870. 1,
  6871. curentry->ClientName);
  6872. }
  6873. }
  6874. curentry = nextentry;
  6875. }
  6876. UnlockNotifyList(LockHeld);
  6877. LockHeld = NULL;
  6878. }
  6879. //
  6880. // Check for any device interface notification entries for this
  6881. // service.
  6882. //
  6883. for (i = 0; i < CLASS_GUID_HASH_BUCKETS; i++) {
  6884. if (LockNotifyList(&ClassList[i].Lock)) {
  6885. LockHeld = &ClassList[i].Lock;
  6886. } else {
  6887. //
  6888. // Couldn't acquire the lock. Just move on to the next list.
  6889. //
  6890. continue;
  6891. }
  6892. //
  6893. // Check to see if an entry for this service handle exists in
  6894. // this list.
  6895. //
  6896. curentry = GetFirstNotifyEntry(&ClassList[i],0);
  6897. while(curentry) {
  6898. nextentry = GetNextNotifyEntry(curentry,0);
  6899. if (curentry->Unregistered) {
  6900. curentry = nextentry;
  6901. continue;
  6902. }
  6903. if (curentry->Handle == (HANDLE)hService) {
  6904. //
  6905. // Remove the entry from the notification list.
  6906. //
  6907. curentry->Unregistered = TRUE;
  6908. curentry->Freed |= (PNP_UNREG_FREE|PNP_UNREG_CLASS);
  6909. DeleteNotifyEntry(curentry,FALSE);
  6910. //
  6911. // Only log a warning if the PlugPlay service has not
  6912. // already stopped. Otherwise, the client may actually
  6913. // have tried to unregister after we were shut down.
  6914. //
  6915. if (CurrentServiceState != SERVICE_STOPPED &&
  6916. CurrentServiceState != SERVICE_STOP_PENDING) {
  6917. KdPrintEx((DPFLTR_PNPMGR_ID,
  6918. DBGF_WARNINGS | DBGF_EVENT,
  6919. "UMPNPMGR: Service '%ws' "
  6920. "may have stopped without unregistering "
  6921. "for DeviceInterfaceChange notification.\n",
  6922. curentry->ClientName));
  6923. LogWarningEvent(WRN_STOPPED_SERVICE_REGISTERED,
  6924. 1,
  6925. curentry->ClientName);
  6926. }
  6927. }
  6928. curentry = nextentry;
  6929. }
  6930. UnlockNotifyList(LockHeld);
  6931. LockHeld = NULL;
  6932. }
  6933. }
  6934. Clean0:
  6935. LeaveCriticalSection(&RegistrationCS);
  6936. } except (EXCEPTION_EXECUTE_HANDLER){
  6937. KdPrintEx((DPFLTR_PNPMGR_ID,
  6938. DBGF_ERRORS,
  6939. "UMPNPMGR: Exception in RegisterServiceNotification!!\n"));
  6940. ASSERT(0);
  6941. SetLastError(ERROR_EXCEPTION_IN_SERVICE);
  6942. Status = CR_FAILURE;
  6943. if (LockHeld) {
  6944. UnlockNotifyList(LockHeld);
  6945. }
  6946. LeaveCriticalSection(&RegistrationCS);
  6947. if (entry) {
  6948. if (entry->ClientName) {
  6949. HeapFree(ghPnPHeap, 0, entry->ClientName);
  6950. }
  6951. HeapFree(ghPnPHeap, 0, entry);
  6952. }
  6953. }
  6954. return Status;
  6955. } // RegisterServiceNotification
  6956. CONFIGRET
  6957. RegisterScmCallback(
  6958. IN PSCMCALLBACK_ROUTINE pScCallback,
  6959. IN PSCMAUTHENTICATION_CALLBACK pScAuthCallback
  6960. )
  6961. /*++
  6962. Routine Description:
  6963. This routine is called directly and privately by the service controller. It
  6964. allows the SCM to dynamically provide this service with callback routines.
  6965. Arguments:
  6966. pScCallback - Specifies the entrypoint for the routine that should be used
  6967. to have the service controller send special controls to a
  6968. service (which ControlService would block), on behalf of
  6969. the user-mode plug and play manager.
  6970. pScAuthCallback - Specifies the entrypoint for the routine that should be
  6971. used to retrieve the service status for a service.
  6972. Return Value:
  6973. Returns CR_SUCCESS.
  6974. --*/
  6975. {
  6976. ASSERT(pScCallback);
  6977. ASSERT(pScAuthCallback);
  6978. pServiceControlCallback = pScCallback;
  6979. pSCMAuthenticate = pScAuthCallback;
  6980. return CR_SUCCESS;
  6981. } // RegisterScmCallback
  6982. CONFIGRET
  6983. UnRegisterScmCallback(
  6984. VOID
  6985. )
  6986. /*++
  6987. Routine Description:
  6988. This routine is called directly and privately by the service controller. It
  6989. allows the SCM to unregister the callback routines previously registered by
  6990. RegisterScmCallback.
  6991. Arguments:
  6992. None.
  6993. Return Value:
  6994. Returns CR_SUCCESS.
  6995. --*/
  6996. {
  6997. pServiceControlCallback = NULL;
  6998. pSCMAuthenticate = NULL;
  6999. return CR_SUCCESS;
  7000. } // UnRegisterScmCallback
  7001. ULONG
  7002. MapSCMControlsToControlBit(
  7003. IN ULONG scmControls
  7004. )
  7005. /*++
  7006. Routine Description:
  7007. Returns a bitmask of control bits specifying ServiceList lists to which a
  7008. service should be added or removed from, based on the controls currently
  7009. accepted by the service.
  7010. Arguments:
  7011. scmControls - Specifies the service controls currently accepted by a
  7012. service.
  7013. Return Value:
  7014. Returns a bitmask of control bits corresponding to entries in the
  7015. ServiceList array of lists to which a service should be added or removed
  7016. from, based on the controls currently accepted by the service.
  7017. Notes:
  7018. Services are added or removed from a ServiceList notification list by adding
  7019. or removing the corresponding SERVICE_ACCEPT_* control from its list of
  7020. accepted controls when calling SetServiceStatus(). The service control
  7021. manager calls RegisterServiceNotification() as appropriate to register or
  7022. unregister the service to receive that control. Currently, only
  7023. SERVICE_ACCEPT_HARDWAREPROFILECHANGE and SERVICE_ACCEPT_POWEREVENT are
  7024. supported.
  7025. A service registers to receive the SERVICE_CONTROL_DEVICEEVENT control by
  7026. calling RegisterDeviceNotification, and is stored in the appropriate
  7027. TargetList or ClassList entry.
  7028. --*/
  7029. {
  7030. ULONG retBits=0;
  7031. if (scmControls & SERVICE_ACCEPT_HARDWAREPROFILECHANGE) {
  7032. retBits |= CBIT_HWPROFILE;
  7033. }
  7034. if (scmControls & SERVICE_ACCEPT_POWEREVENT) {
  7035. retBits |= CBIT_POWEREVENT;
  7036. }
  7037. return retBits;
  7038. } // MapSCMControlsToControlBit
  7039. DWORD
  7040. GetFirstPass(
  7041. IN BOOL bQuery
  7042. )
  7043. /*++
  7044. Routine Description:
  7045. This routine retrieves the first class of handles to notify. The subsequent
  7046. class of handles to notify should be retrieved by calling GetNextPass(...);
  7047. Arguments:
  7048. bQuery - If TRUE, starts with window handles, otherwise service handles.
  7049. Return Value:
  7050. Returns the first class of handles to notify.
  7051. Notes:
  7052. See GetNextPass() for the notification pass progression.
  7053. --*/
  7054. {
  7055. //
  7056. // Since services are generally less likely to veto device event queries, we
  7057. // first make sure that all windows succeed the query before notifying any
  7058. // services. For non-query events, services should be the first to know.
  7059. //
  7060. return (bQuery) ? DEVICE_NOTIFY_WINDOW_HANDLE : DEVICE_NOTIFY_SERVICE_HANDLE;
  7061. }
  7062. DWORD
  7063. GetNextPass(
  7064. IN DWORD curPass,
  7065. IN BOOL bQuery
  7066. )
  7067. /*++
  7068. Routine Description:
  7069. This routine retrieves the next class of handles to notify. If there is no
  7070. subsequent class of handles to notify, PASS_COMPLETE is returned.
  7071. Arguments:
  7072. curPass Current pass.
  7073. bQuery If TRUE, proceed from window handles to completion handles to
  7074. service handles. Otherwise process in reverse.
  7075. Return Value:
  7076. Returns the subsequent pass.
  7077. Notes:
  7078. For query events, the notification pass progression is:
  7079. DEVICE_NOTIFY_WINDOW_HANDLE,
  7080. DEVICE_NOTIFY_COMPLETION_HANDLE,
  7081. DEVICE_NOTIFY_SERVICE_HANDLE,
  7082. PASS_COMPLETE
  7083. For non-query events, the notification pass progression is:
  7084. DEVICE_NOTIFY_SERVICE_HANDLE,
  7085. DEVICE_NOTIFY_COMPLETION_HANDLE,
  7086. DEVICE_NOTIFY_WINDOW_HANDLE,
  7087. PASS_COMPLETE
  7088. --*/
  7089. {
  7090. if (bQuery) {
  7091. if (curPass == DEVICE_NOTIFY_WINDOW_HANDLE ) {
  7092. curPass = DEVICE_NOTIFY_COMPLETION_HANDLE;
  7093. } else if (curPass == DEVICE_NOTIFY_COMPLETION_HANDLE) {
  7094. curPass = DEVICE_NOTIFY_SERVICE_HANDLE;
  7095. } else {
  7096. curPass = PASS_COMPLETE;
  7097. }
  7098. } else {
  7099. if (curPass == DEVICE_NOTIFY_SERVICE_HANDLE ) {
  7100. curPass = DEVICE_NOTIFY_COMPLETION_HANDLE;
  7101. } else if (curPass == DEVICE_NOTIFY_COMPLETION_HANDLE) {
  7102. curPass = DEVICE_NOTIFY_WINDOW_HANDLE;
  7103. } else {
  7104. curPass = PASS_COMPLETE;
  7105. }
  7106. }
  7107. return curPass;
  7108. }
  7109. BOOL
  7110. NotifyEntryThisPass(
  7111. IN PPNP_NOTIFY_ENTRY Entry,
  7112. IN DWORD Pass
  7113. )
  7114. {
  7115. ASSERT(Pass != PASS_COMPLETE);
  7116. return ((!(Entry->Unregistered)) && (GetPassFromEntry(Entry) == Pass));
  7117. }
  7118. DWORD
  7119. GetPassFromEntry(
  7120. IN PPNP_NOTIFY_ENTRY Entry
  7121. )
  7122. {
  7123. return (Entry->Flags & DEVICE_NOTIFY_HANDLE_MASK);
  7124. }
  7125. BOOL
  7126. EventIdFromEventGuid(
  7127. IN CONST GUID *EventGuid,
  7128. OUT LPDWORD EventId,
  7129. OUT LPDWORD Flags,
  7130. OUT LPDWORD ServiceControl
  7131. )
  7132. /*++
  7133. Routine Description:
  7134. This thread routine converts an event guid into the corresponding event id
  7135. that user-mode code expects (used in BroadcastSystemMessage).
  7136. Arguments:
  7137. EventGuid Specifies an event guid.
  7138. EventId Returns the id form (from dbt.h) of the guid in EventGuid.
  7139. Flags Returns the flags that should be used when broadcasting this
  7140. event.
  7141. NOTE: device ARRIVAL and event CANCEL are considered "Queries"
  7142. since the bottom level drivers need to be told first.
  7143. Return Value:
  7144. Currently returns TRUE/FALSE.
  7145. Notes:
  7146. Most users of this function call it mainly to retrieve the EventId. Those
  7147. functions typically examine the returned flags only to check the BSF_QUERY
  7148. flag (ie, they don't call BroadcastSystemMessage). Depending on whether
  7149. BSF_QUERY is set, the notification lists will be walked forwards or
  7150. backwards.
  7151. We should really return something generic such as:
  7152. [MSG_POST, MSG_QUERY, MSG_SEND] | [MSG_FORWARDS, MSG_BACKWARDS]
  7153. Then we should implement a BsmFlagsFromMsgFlags function.
  7154. --*/
  7155. {
  7156. //
  7157. // BSF_IGNORECURRENTTASK - Sent messages do not appear in the sending
  7158. // processes message queue.
  7159. //
  7160. // BSF_QUERY - If any recipient vetoes the message by returning
  7161. // the appropriate value, the broadcast is failed
  7162. // (ie, BroadcastSystemMessage returns 0).
  7163. //
  7164. // BSF_NOHANG - Non-posted messages are automatically failed if
  7165. // the window has not processed any available
  7166. // messages within a system defined time (as of
  7167. // 04/20/1999 this is 5 seconds).
  7168. // (SendMessageTimeout: SMTO_ABORTIFHUNG)
  7169. //
  7170. // BSF_FORCEIFHUNG - Failures due to timeouts or hangs are instead
  7171. // treated as successes.
  7172. //
  7173. // BSF_NOTIMEOUTIFNOTHUNG - If a window has not responded to the passed in
  7174. // notification, but is actively processing
  7175. // subsequent messages, then it is assumed to be
  7176. // interacting with the user, in which case the
  7177. // timeout is on hold.
  7178. // (SendMessageTimeout: SMTO_NOTIMEOUTIFNOTHUNG)
  7179. //
  7180. // BSF_POSTMESSAGE - Message is posted, results ignored. Note that
  7181. // a notification with private data in the lParam
  7182. // *cannot* be posted - the OS does not make a
  7183. // private copy, but rather treats the broadcast
  7184. // as if it were a SendMessage if you try.
  7185. //
  7186. // BSF_ALLOWSFW - Windows that receive the broadcast are allowed
  7187. // to become foreground windows.
  7188. //
  7189. // Also, DBT messages >= 0x8000 have lParams pointing to blocks of data that
  7190. // need to be marshalled around. As user doesn't support "snapshotting" the
  7191. // data for posts, we can't pass in BSF_POSTMESSAGE.
  7192. //
  7193. *Flags = BSF_IGNORECURRENTTASK;
  7194. //
  7195. // Standard (well-known) event guids.
  7196. //
  7197. if (GuidEqual(EventGuid, (LPGUID)&GUID_HWPROFILE_QUERY_CHANGE)) {
  7198. *Flags |= BSF_QUERY | BSF_ALLOWSFW |
  7199. BSF_FORCEIFHUNG | BSF_NOHANG;
  7200. *EventId = DBT_QUERYCHANGECONFIG;
  7201. *ServiceControl = SERVICE_CONTROL_HARDWAREPROFILECHANGE;
  7202. } else if (GuidEqual(EventGuid, (LPGUID)&GUID_HWPROFILE_CHANGE_CANCELLED)) {
  7203. *Flags |= BSF_POSTMESSAGE;
  7204. *EventId = DBT_CONFIGCHANGECANCELED;
  7205. *ServiceControl = SERVICE_CONTROL_HARDWAREPROFILECHANGE;
  7206. } else if (GuidEqual(EventGuid, (LPGUID)&GUID_HWPROFILE_CHANGE_COMPLETE)) {
  7207. *Flags |= BSF_POSTMESSAGE;
  7208. *EventId = DBT_CONFIGCHANGED;
  7209. *ServiceControl = SERVICE_CONTROL_HARDWAREPROFILECHANGE;
  7210. } else if (GuidEqual(EventGuid, (LPGUID)&GUID_DEVICE_INTERFACE_ARRIVAL)) {
  7211. *Flags |= BSF_NOHANG;
  7212. *EventId = DBT_DEVICEARRIVAL;
  7213. *ServiceControl = SERVICE_CONTROL_DEVICEEVENT;
  7214. } else if (GuidEqual(EventGuid, (LPGUID)&GUID_DEVICE_INTERFACE_REMOVAL)) {
  7215. //
  7216. // NOTE - BSF_QUERY is set so that we run the list backwards. No actual
  7217. // broadcasts are done on this Id.
  7218. //
  7219. *Flags |= BSF_NOHANG | BSF_QUERY;
  7220. *EventId = DBT_DEVICEREMOVECOMPLETE;
  7221. *ServiceControl = SERVICE_CONTROL_DEVICEEVENT;
  7222. } else if (GuidEqual(EventGuid, (LPGUID)&GUID_TARGET_DEVICE_QUERY_REMOVE)) {
  7223. *Flags |= BSF_QUERY | BSF_ALLOWSFW |
  7224. BSF_FORCEIFHUNG | BSF_NOHANG;
  7225. *EventId = DBT_DEVICEQUERYREMOVE;
  7226. *ServiceControl = SERVICE_CONTROL_DEVICEEVENT;
  7227. } else if (GuidEqual(EventGuid, (LPGUID)&GUID_TARGET_DEVICE_REMOVE_CANCELLED)) {
  7228. *Flags |= BSF_NOHANG;
  7229. *EventId = DBT_DEVICEQUERYREMOVEFAILED;
  7230. *ServiceControl = SERVICE_CONTROL_DEVICEEVENT;
  7231. } else if (GuidEqual(EventGuid, (LPGUID)&GUID_DEVICE_REMOVE_PENDING)) {
  7232. //
  7233. // NOTE - BSF_QUERY is set so that we run the list backwards. No actual
  7234. // broadcasts are done on this Id.
  7235. //
  7236. *Flags |= BSF_NOHANG | BSF_QUERY;
  7237. *EventId = DBT_DEVICEREMOVEPENDING;
  7238. *ServiceControl = SERVICE_CONTROL_DEVICEEVENT;
  7239. } else if (GuidEqual(EventGuid, (LPGUID)&GUID_TARGET_DEVICE_REMOVE_COMPLETE)) {
  7240. //
  7241. // NOTE - BSF_QUERY is set so that we run the list backwards. No actual
  7242. // broadcasts are done on this Id.
  7243. //
  7244. *Flags |= BSF_NOHANG | BSF_QUERY;
  7245. *EventId = DBT_DEVICEREMOVECOMPLETE;
  7246. *ServiceControl = SERVICE_CONTROL_DEVICEEVENT;
  7247. } else if (GuidEqual(EventGuid, (LPGUID)&GUID_DEVICE_ARRIVAL)) {
  7248. *Flags |= BSF_NOHANG;
  7249. *EventId = DBT_DEVICEARRIVAL;
  7250. *ServiceControl = SERVICE_CONTROL_DEVICEEVENT;
  7251. } else if (GuidEqual(EventGuid, (LPGUID)&GUID_DEVICE_ENUMERATED)) {
  7252. *Flags = 0;
  7253. *EventId = DBT_DEVICEARRIVAL;
  7254. *ServiceControl = 0;
  7255. //
  7256. // Private event guids (kernel-mode pnp to user-mode pnp communication).
  7257. // Setting EventId to zero causes ProcessDeviceEvent to swallow these
  7258. // TargetDeviceChangeEvent events.
  7259. //
  7260. } else if (GuidEqual(EventGuid, (LPGUID)&GUID_DEVICE_SAFE_REMOVAL) ||
  7261. GuidEqual(EventGuid, (LPGUID)&GUID_DEVICE_EJECT_VETOED) ||
  7262. GuidEqual(EventGuid, (LPGUID)&GUID_DEVICE_REMOVAL_VETOED) ||
  7263. GuidEqual(EventGuid, (LPGUID)&GUID_DEVICE_WARM_EJECT_VETOED) ||
  7264. GuidEqual(EventGuid, (LPGUID)&GUID_DEVICE_STANDBY_VETOED) ||
  7265. GuidEqual(EventGuid, (LPGUID)&GUID_DEVICE_HIBERNATE_VETOED) ||
  7266. GuidEqual(EventGuid, (LPGUID)&GUID_DEVICE_KERNEL_INITIATED_EJECT) ||
  7267. GuidEqual(EventGuid, (LPGUID)&GUID_DEVICE_SURPRISE_REMOVAL) ||
  7268. GuidEqual(EventGuid, (LPGUID)&GUID_DRIVER_BLOCKED) ||
  7269. GuidEqual(EventGuid, (LPGUID)&GUID_DEVICE_INVALID_ID)) {
  7270. *Flags = 0;
  7271. *EventId = 0;
  7272. *ServiceControl = 0;
  7273. } else if (GuidEqual(EventGuid, (LPGUID)&GUID_PNP_CUSTOM_NOTIFICATION)) {
  7274. //
  7275. // Custom events cannot be failed (ie they aren't queries)
  7276. //
  7277. *EventId = DBT_CUSTOMEVENT;
  7278. *Flags |= BSF_NOHANG;
  7279. *ServiceControl = SERVICE_CONTROL_DEVICEEVENT;
  7280. } else if (GuidEqual(EventGuid, (LPGUID)&GUID_PNP_POWER_NOTIFICATION)) {
  7281. //
  7282. // These are treated as custom too.
  7283. //
  7284. *EventId = DBT_CUSTOMEVENT;
  7285. *Flags |= BSF_NOHANG;
  7286. *ServiceControl = SERVICE_CONTROL_POWEREVENT;
  7287. } else {
  7288. //
  7289. // Anything that makes it here is a bug.
  7290. //
  7291. ASSERT(GuidEqual(EventGuid, (LPGUID)&GUID_PNP_CUSTOM_NOTIFICATION));
  7292. *EventId = 0;
  7293. *Flags = 0;
  7294. *ServiceControl = 0;
  7295. }
  7296. return TRUE;
  7297. } // EventIdFromEventGuid
  7298. ULONG
  7299. SendHotplugNotification(
  7300. IN CONST GUID *EventGuid,
  7301. IN PPNP_VETO_TYPE VetoType OPTIONAL,
  7302. IN LPWSTR MultiSzList,
  7303. IN OUT PULONG SessionId,
  7304. IN ULONG Flags
  7305. )
  7306. /*++
  7307. Routine Description:
  7308. This routine kicks off a hotplug.dll process (if someone is logged in).
  7309. We use a named pipe to comunicate with the user mode process and have it
  7310. display the requested UI.
  7311. Arguments:
  7312. EventGuid - Specifies an event GUID.
  7313. VetoType - For events requiring a vetoer, supplies the address of a
  7314. variable containing the type of the component responsible for
  7315. vetoing the request.
  7316. MultiSzList - Supplies the MultiSz list to be sent to hotplu.dll. This is
  7317. usually a device ID, possibly followed by a list of vetoers
  7318. (which may or may not be device ID's).
  7319. SessionId - Supplies the address of a variable containing the SessionId on
  7320. which the hotplug dialog is to be displayed. If successful,
  7321. the SessionId will contain the id of the session in which the
  7322. device install client process was launched. Otherwise, will
  7323. contain an invalid session id, INVALID_SESSION (0xFFFFFFFF).
  7324. Flags - Specifies flags describing the behavior of the hotplug dialog.
  7325. The following flags are currently defined:
  7326. HOTPLUG_DISPLAY_ON_CONSOLE - if specified, the value in the SessionId
  7327. variable will be ignored, and the hotplug dialog will always be
  7328. displayed on the current active console session.
  7329. Return Value:
  7330. Currently returns TRUE/FALSE.
  7331. Return Value:
  7332. If the process was successfully created, the return value is TRUE. This
  7333. routine doesn't wait until the process terminates.
  7334. If we couldn't create the process (e.g., because no user was logged in),
  7335. the return value is FALSE.
  7336. --*/
  7337. {
  7338. BOOL bStatus;
  7339. STARTUPINFO StartupInfo;
  7340. PROCESS_INFORMATION ProcessInfo;
  7341. WCHAR szCmdLine[MAX_PATH];
  7342. WCHAR szHotPlugDllEntryPoint[80];
  7343. HANDLE hHotPlugPipe = NULL;
  7344. HANDLE hHotPlugEvent = NULL;
  7345. HANDLE hFinishEvents[2] = { NULL, NULL };
  7346. HANDLE hTemp, hUserToken = NULL;
  7347. RPC_STATUS rpcStatus = RPC_S_OK;
  7348. GUID newGuid;
  7349. WCHAR szGuidString[MAX_GUID_STRING_LEN];
  7350. WCHAR szHotPlugPipeName[MAX_PATH];
  7351. WCHAR szHotPlugEventName[MAX_PATH];
  7352. ULONG ulHotPlugEventNameSize;
  7353. ULONG ulMultiSzListSize;
  7354. ULONG ulSize, ulSessionId = INVALID_SESSION;
  7355. WIN32_FIND_DATA findData;
  7356. LPWSTR pszName = NULL;
  7357. PVOID lpEnvironment = NULL;
  7358. OVERLAPPED overlapped;
  7359. DWORD dwError, dwWait, dwBytes;
  7360. HRESULT hr;
  7361. size_t Len = 0;
  7362. //
  7363. // Check if we should skip client side UI.
  7364. //
  7365. if (gbSuppressUI) {
  7366. KdPrintEx((DPFLTR_PNPMGR_ID,
  7367. DBGF_WARNINGS,
  7368. "UMPNPMGR: SendHotplugNotification: "
  7369. "UI has been suppressed, exiting.\n"));
  7370. LogWarningEvent(WRN_HOTPLUG_UI_SUPPRESSED, 1, MultiSzList);
  7371. return FALSE;
  7372. }
  7373. //
  7374. // Initialize process, startup and overlapped structures, since we
  7375. // depend on them being NULL during cleanup here on out.
  7376. //
  7377. ZeroMemory(&ProcessInfo, sizeof(ProcessInfo));
  7378. ZeroMemory(&StartupInfo, sizeof(StartupInfo));
  7379. ZeroMemory(&overlapped, sizeof(overlapped));
  7380. //
  7381. // Assume failure
  7382. //
  7383. bStatus = FALSE;
  7384. try {
  7385. //
  7386. // Determine the session to use, based on the supplied flags.
  7387. //
  7388. if (Flags & HOTPLUG_DISPLAY_ON_CONSOLE) {
  7389. ulSessionId = GetActiveConsoleSessionId();
  7390. } else {
  7391. ASSERT(*SessionId != INVALID_SESSION);
  7392. ulSessionId = *SessionId;
  7393. }
  7394. //
  7395. // Before doing anything, check that hotplug.dll is actually present on
  7396. // the system.
  7397. //
  7398. szCmdLine[0] = L'\0';
  7399. ulSize = GetSystemDirectory(szCmdLine, MAX_PATH);
  7400. if ((ulSize == 0) || ((ulSize + 2 + ARRAY_SIZE(HOTPLUG_DLL)) > MAX_PATH)) {
  7401. return FALSE;
  7402. }
  7403. hr = StringCchCat(szCmdLine,
  7404. SIZECHARS(szCmdLine),
  7405. L"\\");
  7406. if (SUCCEEDED(hr)) {
  7407. hr = StringCchCat(szCmdLine,
  7408. SIZECHARS(szCmdLine),
  7409. HOTPLUG_DLL);
  7410. }
  7411. if (FAILED(hr)) {
  7412. return FALSE;
  7413. }
  7414. hTemp = FindFirstFile(szCmdLine, &findData);
  7415. if(hTemp != INVALID_HANDLE_VALUE) {
  7416. FindClose(hTemp);
  7417. } else {
  7418. KdPrintEx((DPFLTR_PNPMGR_ID,
  7419. DBGF_ERRORS | DBGF_WARNINGS | DBGF_EVENT,
  7420. "UMPNPMGR: SendHotplugNotification: %ws not found, error = %d, exiting\n",
  7421. szCmdLine,
  7422. GetLastError()));
  7423. LogWarningEvent(WRN_HOTPLUG_NOT_PRESENT, 1, szCmdLine);
  7424. return FALSE;
  7425. }
  7426. //
  7427. // Get the user access token for the active console session user.
  7428. //
  7429. if (!GetSessionUserToken(ulSessionId, &hUserToken)) {
  7430. return FALSE;
  7431. }
  7432. //
  7433. // Create a named pipe and event for communication and synchronization
  7434. // with HotPlug. The event and named pipe must be global so that
  7435. // UMPNPMGR can interact with a HotPlug client in a different session,
  7436. // but it must still be unique for that session. Add a generated GUID
  7437. // so the names are not entirely well-known.
  7438. //
  7439. rpcStatus = UuidCreate(&newGuid);
  7440. if ((rpcStatus != RPC_S_OK) &&
  7441. (rpcStatus != RPC_S_UUID_LOCAL_ONLY)) {
  7442. goto clean0;
  7443. }
  7444. if (StringFromGuid((LPGUID)&newGuid,
  7445. szGuidString,
  7446. MAX_GUID_STRING_LEN) != NO_ERROR) {
  7447. goto clean0;
  7448. }
  7449. if (FAILED(StringCchPrintf(
  7450. szHotPlugPipeName,
  7451. SIZECHARS(szHotPlugPipeName),
  7452. L"%ws_%d.%ws",
  7453. PNP_HOTPLUG_PIPE,
  7454. ulSessionId,
  7455. szGuidString))) {
  7456. goto clean0;
  7457. }
  7458. if (FAILED(StringCchPrintf(
  7459. szHotPlugEventName,
  7460. SIZECHARS(szHotPlugEventName),
  7461. L"Global\\%ws_%d.%ws",
  7462. PNP_HOTPLUG_EVENT,
  7463. ulSessionId,
  7464. szGuidString))) {
  7465. goto clean0;
  7466. }
  7467. if (FAILED(StringCchLength(
  7468. szHotPlugEventName,
  7469. SIZECHARS(szHotPlugEventName),
  7470. &Len))) {
  7471. goto clean0;
  7472. }
  7473. ulHotPlugEventNameSize = (ULONG)((Len + 1) * sizeof(WCHAR));
  7474. //
  7475. // Get the length of the multi-sz list. This is usually a device ID
  7476. // possibly followed by a list of vetoers which may or may not be device
  7477. // Id's
  7478. //
  7479. ulMultiSzListSize = 0;
  7480. for (pszName = MultiSzList;
  7481. *pszName;
  7482. pszName += lstrlen(pszName) + 1) {
  7483. ulMultiSzListSize += (lstrlen(pszName) + 1) * sizeof(WCHAR);
  7484. }
  7485. ulMultiSzListSize += sizeof(WCHAR);
  7486. //
  7487. // The approximate size of the named pipe output buffer should be large
  7488. // enough to hold the greater of either:
  7489. // - The name and size of the named event string, OR
  7490. // - The type, size and contents of the multi-sz list.
  7491. //
  7492. ulSize = max(sizeof(ulHotPlugEventNameSize) +
  7493. ulHotPlugEventNameSize,
  7494. sizeof(PNP_VETO_TYPE) +
  7495. sizeof(ulMultiSzListSize) +
  7496. ulMultiSzListSize);
  7497. //
  7498. // Open up a named pipe to communicate with hotplug.dll.
  7499. //
  7500. if (CreateUserReadNamedPipe(
  7501. hUserToken,
  7502. szHotPlugPipeName,
  7503. ulSize,
  7504. &hHotPlugPipe) != NO_ERROR) {
  7505. ASSERT(hHotPlugPipe == NULL);
  7506. goto clean0;
  7507. }
  7508. //
  7509. // Create an event that a user-client can synchronize with and set, and
  7510. // that we will block on after we send all the device IDs to
  7511. // hotplug.dll.
  7512. //
  7513. if (CreateUserSynchEvent(
  7514. hUserToken,
  7515. szHotPlugEventName,
  7516. &hHotPlugEvent) != NO_ERROR) {
  7517. ASSERT(hHotPlugEvent == NULL);
  7518. goto clean0;
  7519. }
  7520. if (GuidEqual(EventGuid, (LPGUID)&GUID_DEVICE_EJECT_VETOED)) {
  7521. //
  7522. // GUID_DEVICE_EJECT_VETOED : HotPlugEjectVetoed
  7523. // Expects veto information.
  7524. //
  7525. hr = StringCchCopyEx(szHotPlugDllEntryPoint,
  7526. SIZECHARS(szHotPlugDllEntryPoint),
  7527. TEXT("HotPlugEjectVetoed"),
  7528. NULL, NULL,
  7529. STRSAFE_NULL_ON_FAILURE);
  7530. ASSERT(SUCCEEDED(hr));
  7531. ASSERT(VetoType);
  7532. } else if (GuidEqual(EventGuid, (LPGUID)&GUID_DEVICE_REMOVAL_VETOED)) {
  7533. //
  7534. // GUID_DEVICE_REMOVAL_VETOED : HotPlugRemovalVetoed
  7535. // Expects veto information.
  7536. //
  7537. hr = StringCchCopyEx(szHotPlugDllEntryPoint,
  7538. SIZECHARS(szHotPlugDllEntryPoint),
  7539. TEXT("HotPlugRemovalVetoed"),
  7540. NULL, NULL,
  7541. STRSAFE_NULL_ON_FAILURE);
  7542. ASSERT(SUCCEEDED(hr));
  7543. ASSERT(VetoType);
  7544. } else if (GuidEqual(EventGuid, (LPGUID)&GUID_DEVICE_STANDBY_VETOED)) {
  7545. //
  7546. // GUID_DEVICE_STANDBY_VETOED : HotPlugStandbyVetoed
  7547. // Expects veto information.
  7548. //
  7549. hr = StringCchCopyEx(szHotPlugDllEntryPoint,
  7550. SIZECHARS(szHotPlugDllEntryPoint),
  7551. TEXT("HotPlugStandbyVetoed"),
  7552. NULL, NULL,
  7553. STRSAFE_NULL_ON_FAILURE);
  7554. ASSERT(SUCCEEDED(hr));
  7555. ASSERT(VetoType);
  7556. } else if (GuidEqual(EventGuid, (LPGUID)&GUID_DEVICE_HIBERNATE_VETOED)) {
  7557. //
  7558. // GUID_DEVICE_HIBERNATE_VETOED : HotPlugHibernateVetoed
  7559. // Expects veto information.
  7560. //
  7561. hr = StringCchCopyEx(szHotPlugDllEntryPoint,
  7562. SIZECHARS(szHotPlugDllEntryPoint),
  7563. TEXT("HotPlugHibernateVetoed"),
  7564. NULL, NULL,
  7565. STRSAFE_NULL_ON_FAILURE);
  7566. ASSERT(SUCCEEDED(hr));
  7567. ASSERT(VetoType);
  7568. } else if (GuidEqual(EventGuid, (LPGUID)&GUID_DEVICE_WARM_EJECT_VETOED)) {
  7569. //
  7570. // GUID_DEVICE_WARM_EJECT_VETOED : HotPlugWarmEjectVetoed
  7571. // Expects veto information.
  7572. //
  7573. hr = StringCchCopyEx(szHotPlugDllEntryPoint,
  7574. SIZECHARS(szHotPlugDllEntryPoint),
  7575. TEXT("HotPlugWarmEjectVetoed"),
  7576. NULL, NULL,
  7577. STRSAFE_NULL_ON_FAILURE);
  7578. ASSERT(SUCCEEDED(hr));
  7579. ASSERT(VetoType);
  7580. } else if (GuidEqual(EventGuid, (LPGUID)&GUID_DEVICE_SAFE_REMOVAL)) {
  7581. //
  7582. // GUID_DEVICE_SAFE_REMOVAL : HotPlugSafeRemovalNotification
  7583. // No veto information.
  7584. //
  7585. hr = StringCchCopyEx(szHotPlugDllEntryPoint,
  7586. SIZECHARS(szHotPlugDllEntryPoint),
  7587. TEXT("HotPlugSafeRemovalNotification"),
  7588. NULL, NULL,
  7589. STRSAFE_NULL_ON_FAILURE);
  7590. ASSERT(SUCCEEDED(hr));
  7591. ASSERT(VetoType == NULL);
  7592. } else if (GuidEqual(EventGuid, (LPGUID)&GUID_DEVICE_SURPRISE_REMOVAL)) {
  7593. //
  7594. // GUID_DEVICE_SURPRISE_REMOVAL : HotPlugSurpriseWarn
  7595. // No veto information.
  7596. //
  7597. hr = StringCchCopyEx(szHotPlugDllEntryPoint,
  7598. SIZECHARS(szHotPlugDllEntryPoint),
  7599. TEXT("HotPlugSurpriseWarn"),
  7600. NULL, NULL,
  7601. STRSAFE_NULL_ON_FAILURE);
  7602. ASSERT(SUCCEEDED(hr));
  7603. ASSERT(VetoType == NULL);
  7604. } else if (GuidEqual(EventGuid, (LPGUID)&GUID_DRIVER_BLOCKED)) {
  7605. //
  7606. // GUID_DRIVER_BLOCKED : HotPlugDriverBlocked
  7607. // No veto information.
  7608. //
  7609. hr = StringCchCopyEx(szHotPlugDllEntryPoint,
  7610. SIZECHARS(szHotPlugDllEntryPoint),
  7611. TEXT("HotPlugDriverBlocked"),
  7612. NULL, NULL,
  7613. STRSAFE_NULL_ON_FAILURE);
  7614. ASSERT(SUCCEEDED(hr));
  7615. ASSERT(VetoType == NULL);
  7616. } else if (GuidEqual(EventGuid, (LPGUID)&GUID_DEVICE_INVALID_ID)) {
  7617. //
  7618. // GUID_DEVICE_INVALID_ID : HotPlugChildWithInvalidId
  7619. // No veto information.
  7620. //
  7621. hr = StringCchCopyEx(szHotPlugDllEntryPoint,
  7622. SIZECHARS(szHotPlugDllEntryPoint),
  7623. TEXT("HotPlugChildWithInvalidId"),
  7624. NULL, NULL,
  7625. STRSAFE_NULL_ON_FAILURE);
  7626. ASSERT(SUCCEEDED(hr));
  7627. ASSERT(VetoType == NULL);
  7628. } else {
  7629. //
  7630. // Unknown device event.
  7631. //
  7632. KdPrintEx((DPFLTR_PNPMGR_ID,
  7633. DBGF_ERRORS | DBGF_EVENT,
  7634. "UMPNPMGR: SendHotplugNotification: "
  7635. "Unknown device event!\n"));
  7636. ASSERT(0);
  7637. goto clean0;
  7638. }
  7639. //
  7640. // Attempt to create the user's environment block. If for some reason we
  7641. // can't, we'll just have to create the process without it.
  7642. //
  7643. if (!CreateEnvironmentBlock(&lpEnvironment,
  7644. hUserToken,
  7645. FALSE)) {
  7646. KdPrintEx((DPFLTR_PNPMGR_ID,
  7647. DBGF_ERRORS | DBGF_EVENT,
  7648. "UMPNPMGR: SendHotplugNotification: "
  7649. "Failed to allocate environment block, error = %d!\n",
  7650. GetLastError()));
  7651. lpEnvironment = NULL;
  7652. }
  7653. //
  7654. // Launch hotplug.dll using rundll32.exe, passing it the pipe name.
  7655. // "rundll32.exe hotplug.dll,<hotplug-entry-point> <hotplug-pipe-name>"
  7656. //
  7657. if (FAILED(StringCchPrintf(
  7658. szCmdLine,
  7659. SIZECHARS(szCmdLine),
  7660. TEXT("%ws %ws,%ws %ws"),
  7661. RUNDLL32_EXE, HOTPLUG_DLL,
  7662. szHotPlugDllEntryPoint,
  7663. szHotPlugPipeName))) {
  7664. goto clean0;
  7665. }
  7666. StartupInfo.cb = sizeof(StartupInfo);
  7667. StartupInfo.wShowWindow = SW_SHOW;
  7668. StartupInfo.lpDesktop = DEFAULT_INTERACTIVE_DESKTOP; // WinSta0\Default
  7669. //
  7670. // CreateProcessAsUser will create the process in the session
  7671. // specified by the by user-token.
  7672. //
  7673. if (!CreateProcessAsUser(hUserToken, // hToken
  7674. NULL, // lpApplicationName
  7675. szCmdLine, // lpCommandLine
  7676. NULL, // lpProcessAttributes
  7677. NULL, // lpThreadAttributes
  7678. FALSE, // bInheritHandles
  7679. CREATE_UNICODE_ENVIRONMENT |
  7680. DETACHED_PROCESS, // dwCreationFlags
  7681. lpEnvironment, // lpEnvironment
  7682. NULL, // lpDirectory
  7683. &StartupInfo, // lpStartupInfo
  7684. &ProcessInfo // lpProcessInfo
  7685. )) {
  7686. KdPrintEx((DPFLTR_PNPMGR_ID,
  7687. DBGF_EVENT | DBGF_ERRORS,
  7688. "UMPNPMGR: SendHotplugNotification: "
  7689. "Create rundll32 process failed, error = %d\n",
  7690. GetLastError()));
  7691. goto clean0;
  7692. }
  7693. ASSERT(ProcessInfo.hProcess);
  7694. ASSERT(ProcessInfo.hThread);
  7695. //
  7696. // Create an event for use with overlapped I/O - no security, manual
  7697. // reset, not signalled, no name.
  7698. //
  7699. overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  7700. if (overlapped.hEvent == NULL) {
  7701. goto clean0;
  7702. }
  7703. //
  7704. // Connect to the newly created named pipe. If hotplug is not already
  7705. // connected to the named pipe, then ConnectNamedPipe() will fail with
  7706. // ERROR_IO_PENDING, and we will wait on the overlapped event. If
  7707. // newdev is already connected, it will fail with ERROR_PIPE_CONNECTED.
  7708. // Note however that neither of these is an error condition.
  7709. //
  7710. if (!ConnectNamedPipe(hHotPlugPipe, &overlapped)) {
  7711. //
  7712. // Overlapped ConnectNamedPipe should always return FALSE on
  7713. // success. Check the last error to see what really happened.
  7714. //
  7715. dwError = GetLastError();
  7716. if (dwError == ERROR_IO_PENDING) {
  7717. //
  7718. // I/O is pending, wait up to one minute for the client to
  7719. // connect, also wait on the process in case it terminates
  7720. // unexpectedly.
  7721. //
  7722. hFinishEvents[0] = overlapped.hEvent;
  7723. hFinishEvents[1] = ProcessInfo.hProcess;
  7724. dwWait = WaitForMultipleObjects(2, hFinishEvents,
  7725. FALSE,
  7726. PNP_PIPE_TIMEOUT); // 60 seconds
  7727. if (dwWait == WAIT_OBJECT_0) {
  7728. //
  7729. // The overlapped I/O operation completed. Check the status
  7730. // of the operation.
  7731. //
  7732. if (!GetOverlappedResult(hHotPlugPipe,
  7733. &overlapped,
  7734. &dwBytes,
  7735. FALSE)) {
  7736. goto clean0;
  7737. }
  7738. } else {
  7739. //
  7740. // Either the connection timed out, or the client process
  7741. // exited. Cancel pending I/O against the pipe, and quit.
  7742. //
  7743. KdPrintEx((DPFLTR_PNPMGR_ID,
  7744. DBGF_INSTALL | DBGF_ERRORS,
  7745. "UMPNPMGR: SendHotPlugNotification: "
  7746. "Connect timed out, or client process exited!\n"));
  7747. CancelIo(hHotPlugPipe);
  7748. goto clean0;
  7749. }
  7750. } else if (dwError != ERROR_PIPE_CONNECTED) {
  7751. //
  7752. // If the last error indicates anything other than pending I/O,
  7753. // or that The client is already connected to named pipe, fail.
  7754. //
  7755. goto clean0;
  7756. }
  7757. } else {
  7758. //
  7759. // ConnectNamedPipe should not return anything but FALSE in
  7760. // overlapped mode.
  7761. //
  7762. goto clean0;
  7763. }
  7764. //
  7765. // The client is now connected to the named pipe.
  7766. // Close the overlapped event.
  7767. //
  7768. CloseHandle(overlapped.hEvent);
  7769. overlapped.hEvent = NULL;
  7770. //
  7771. // The first data in the pipe will be the length of the name of the
  7772. // event that will be used to sync up umpnpmgr.dll and hotplug.dll.
  7773. //
  7774. if (!WriteFile(hHotPlugPipe,
  7775. &ulHotPlugEventNameSize,
  7776. sizeof(ulHotPlugEventNameSize),
  7777. &ulSize,
  7778. NULL)) {
  7779. LogErrorEvent(ERR_WRITING_SURPRISE_REMOVE_PIPE, GetLastError(), 0);
  7780. goto clean0;
  7781. }
  7782. //
  7783. // The next data in the pipe will be the name of the event that will
  7784. // be used to sync up umpnpmgr.dll and hotplug.dll.
  7785. //
  7786. if (!WriteFile(hHotPlugPipe,
  7787. (LPCVOID)szHotPlugEventName,
  7788. ulHotPlugEventNameSize,
  7789. &ulSize,
  7790. NULL)) {
  7791. LogErrorEvent(ERR_WRITING_SURPRISE_REMOVE_PIPE, GetLastError(), 0);
  7792. goto clean0;
  7793. }
  7794. if (ARGUMENT_PRESENT(VetoType)) {
  7795. //
  7796. // For the notification types expecting veto information,
  7797. // send the Veto type to the client.
  7798. //
  7799. if (!WriteFile(hHotPlugPipe,
  7800. (LPCVOID)VetoType,
  7801. sizeof(PNP_VETO_TYPE),
  7802. &ulSize,
  7803. NULL)) {
  7804. LogErrorEvent(ERR_WRITING_SURPRISE_REMOVE_PIPE, GetLastError(), 0);
  7805. goto clean0;
  7806. }
  7807. }
  7808. //
  7809. // Send the string length to the client
  7810. //
  7811. if (!WriteFile(hHotPlugPipe,
  7812. (LPCVOID)&ulMultiSzListSize,
  7813. sizeof(ulMultiSzListSize),
  7814. &ulSize,
  7815. NULL)) {
  7816. LogErrorEvent(ERR_WRITING_SURPRISE_REMOVE_PIPE, GetLastError(), 0);
  7817. goto clean0;
  7818. }
  7819. //
  7820. // Now send over the entire string
  7821. //
  7822. if (!WriteFile(hHotPlugPipe,
  7823. MultiSzList,
  7824. ulMultiSzListSize,
  7825. &ulSize,
  7826. NULL)) {
  7827. LogErrorEvent(ERR_WRITING_SURPRISE_REMOVE_PIPE, GetLastError(), 0);
  7828. goto clean0;
  7829. }
  7830. //
  7831. // When we are done writing, we need to close the pipe handles so that
  7832. // the client will get a ReadFile error and know that we are finished.
  7833. //
  7834. if (hHotPlugPipe) {
  7835. CloseHandle(hHotPlugPipe);
  7836. hHotPlugPipe = NULL;
  7837. }
  7838. //
  7839. // Wait for hotplug.dll to respond by setting the event before before
  7840. // returning. Also wait on the process as well, to catch the case where
  7841. // the process crashes (or goes away) without signaling the device
  7842. // install event.
  7843. //
  7844. hFinishEvents[0] = hHotPlugEvent;
  7845. hFinishEvents[1] = ProcessInfo.hProcess;
  7846. WaitForMultipleObjects(2, hFinishEvents, FALSE, INFINITE);
  7847. bStatus = TRUE;
  7848. clean0:
  7849. NOTHING;
  7850. } except (EXCEPTION_EXECUTE_HANDLER) {
  7851. KdPrintEx((DPFLTR_PNPMGR_ID,
  7852. DBGF_ERRORS,
  7853. "UMPNPMGR: Exception in SendHotPlugNotification!!\n"));
  7854. ASSERT(0);
  7855. bStatus = FALSE;
  7856. //
  7857. // Reference the following variables so the compiler will respect
  7858. // statement ordering w.r.t. their assignment.
  7859. //
  7860. lpEnvironment = lpEnvironment;
  7861. ProcessInfo.hThread = ProcessInfo.hThread;
  7862. ProcessInfo.hProcess = ProcessInfo.hProcess;
  7863. hUserToken = hUserToken;
  7864. hHotPlugPipe = hHotPlugPipe;
  7865. hHotPlugEvent = hHotPlugEvent;
  7866. }
  7867. if (lpEnvironment) {
  7868. DestroyEnvironmentBlock(lpEnvironment);
  7869. }
  7870. if (ProcessInfo.hThread) {
  7871. CloseHandle(ProcessInfo.hThread);
  7872. }
  7873. if (ProcessInfo.hProcess) {
  7874. CloseHandle(ProcessInfo.hProcess);
  7875. }
  7876. if (hUserToken) {
  7877. CloseHandle(hUserToken);
  7878. }
  7879. if (overlapped.hEvent) {
  7880. CloseHandle(overlapped.hEvent);
  7881. }
  7882. if (hHotPlugPipe) {
  7883. CloseHandle(hHotPlugPipe);
  7884. }
  7885. if (hHotPlugEvent) {
  7886. CloseHandle(hHotPlugEvent);
  7887. }
  7888. if (!bStatus) {
  7889. *SessionId = INVALID_SESSION;
  7890. } else {
  7891. *SessionId = ulSessionId;
  7892. }
  7893. return bStatus;
  7894. } // SendHotplugNotification
  7895. ULONG
  7896. CheckEjectPermissions(
  7897. IN LPWSTR DeviceId,
  7898. OUT PPNP_VETO_TYPE VetoType OPTIONAL,
  7899. OUT LPWSTR VetoName OPTIONAL,
  7900. IN OUT PULONG VetoNameLength OPTIONAL
  7901. )
  7902. /*++
  7903. Routine Description:
  7904. Checks that the user has eject permissions for the specified device.
  7905. Arguments:
  7906. DeviceId - Specifies the device instance id of the device for which
  7907. eject permissions are to be checked.
  7908. VetoType - Supplies the address of a variable to receive, upon
  7909. failure, the type of the component responsible for vetoing
  7910. the request.
  7911. VetoName - Supplies the address of a variable to receive, upon
  7912. failure, the name of the component responsible for vetoing
  7913. the request.
  7914. VetoNameLength - Supplies the address of a variable specifying the size of
  7915. the of buffer specified by the VetoName parameter. Upon
  7916. failure, this address will specify the length of the string
  7917. stored in that buffer by this routine.
  7918. Return Value:
  7919. FALSE if the eject should be blocked, TRUE otherwise.
  7920. Note:
  7921. This routine is called while processing a kernel-initiated ejection event.
  7922. On this side of the event, we are NOT in the context of the user who
  7923. initiated the ejection, but since only the active console user was allowed
  7924. to initiate the request that triggered this event, we use the access token
  7925. of the active console user for the check on this side also. (should the
  7926. active console user change between the request and this event, this would
  7927. check that the user that the current active console user has eject
  7928. permissions; this is still a valid thing to do since it is the console user
  7929. who will receive the ejected hardware)
  7930. --*/
  7931. {
  7932. BOOL bResult, bDockDevice;
  7933. ULONG ulPropertyData, ulDataSize, ulDataType;
  7934. ULONG ulTransferLen, ulConsoleSessionId;
  7935. HANDLE hUserToken = NULL;
  7936. //
  7937. // Is this a dock?
  7938. //
  7939. bDockDevice = FALSE;
  7940. ulDataSize = ulTransferLen = sizeof(ULONG);
  7941. if (CR_SUCCESS == PNP_GetDeviceRegProp(NULL,
  7942. DeviceId,
  7943. CM_DRP_CAPABILITIES,
  7944. &ulDataType,
  7945. (LPBYTE)&ulPropertyData,
  7946. &ulTransferLen,
  7947. &ulDataSize,
  7948. 0)) {
  7949. if (ulPropertyData & CM_DEVCAP_DOCKDEVICE) {
  7950. //
  7951. // Undocking (ie ejecting a dock) uses a special privilege.
  7952. //
  7953. bDockDevice = TRUE;
  7954. }
  7955. } else {
  7956. KdPrintEx((DPFLTR_PNPMGR_ID,
  7957. DBGF_ERRORS,
  7958. "UMPNPMGR: PNP_GetDeviceRegProp failed, error = %d\n",
  7959. GetLastError()));
  7960. return FALSE;
  7961. }
  7962. ulConsoleSessionId = GetActiveConsoleSessionId();
  7963. if ((IsSessionLocked(ulConsoleSessionId)) ||
  7964. (!GetSessionUserToken(ulConsoleSessionId, &hUserToken))) {
  7965. //
  7966. // If the console session is locked or no user is logged in, supply no
  7967. // user token, and verify strictly against the policy permissions
  7968. // required to eject the dock or device, absent a user.
  7969. //
  7970. hUserToken = NULL;
  7971. }
  7972. bResult = VerifyKernelInitiatedEjectPermissions(hUserToken, bDockDevice);
  7973. if (bResult == FALSE) {
  7974. if (ARGUMENT_PRESENT(VetoType)) {
  7975. *VetoType = PNP_VetoInsufficientRights;
  7976. }
  7977. if (ARGUMENT_PRESENT(VetoNameLength)) {
  7978. //
  7979. // VetoNameLength is in characters.
  7980. //
  7981. if (ARGUMENT_PRESENT(VetoName) && *VetoNameLength) {
  7982. *VetoName = UNICODE_NULL;
  7983. }
  7984. *VetoNameLength = 0;
  7985. }
  7986. }
  7987. if (hUserToken) {
  7988. CloseHandle(hUserToken);
  7989. }
  7990. return bResult;
  7991. } // CheckEjectPermissions
  7992. //---------------------------------------------------------------------------
  7993. // Private Utility Routines
  7994. //---------------------------------------------------------------------------
  7995. VOID
  7996. LogErrorEvent(
  7997. DWORD dwEventID,
  7998. DWORD dwError,
  7999. WORD nStrings,
  8000. ...
  8001. )
  8002. {
  8003. HANDLE hEventLog;
  8004. LPWSTR *paStrings;
  8005. va_list pArg;
  8006. DWORD index;
  8007. hEventLog = RegisterEventSource(NULL, TEXT("PlugPlayManager"));
  8008. if (hEventLog == NULL) {
  8009. return;
  8010. }
  8011. if (nStrings) {
  8012. paStrings = HeapAlloc(ghPnPHeap, 0, nStrings * sizeof(LPWSTR));
  8013. if (paStrings != NULL) {
  8014. va_start(pArg, nStrings);
  8015. for (index = 0; index < nStrings; index++) {
  8016. paStrings[index] = va_arg(pArg, LPWSTR);
  8017. }
  8018. va_end(pArg);
  8019. ReportEvent( hEventLog,
  8020. EVENTLOG_ERROR_TYPE,
  8021. 0, // wCategory
  8022. dwEventID, // dwEventID
  8023. NULL, // lpUserSID
  8024. nStrings, // wNumStrings
  8025. sizeof(dwError), // dwDataSize
  8026. paStrings, // lpStrings
  8027. &dwError); // lpRawData
  8028. HeapFree(ghPnPHeap, 0, paStrings);
  8029. }
  8030. } else {
  8031. ReportEvent( hEventLog,
  8032. EVENTLOG_ERROR_TYPE,
  8033. 0, // wCategory
  8034. dwEventID, // dwEventID
  8035. NULL, // lpUserSID
  8036. 0, // wNumStrings
  8037. sizeof(dwError), // dwDataSize
  8038. NULL, // lpStrings
  8039. &dwError); // lpRawData
  8040. }
  8041. DeregisterEventSource(hEventLog);
  8042. }
  8043. VOID
  8044. LogWarningEvent(
  8045. DWORD dwEventID,
  8046. WORD nStrings,
  8047. ...
  8048. )
  8049. {
  8050. HANDLE hEventLog;
  8051. LPWSTR *paStrings;
  8052. va_list pArg;
  8053. DWORD index;
  8054. hEventLog = RegisterEventSource(NULL, TEXT("PlugPlayManager"));
  8055. if (hEventLog == NULL) {
  8056. return;
  8057. }
  8058. paStrings = HeapAlloc(ghPnPHeap, 0, nStrings * sizeof(LPWSTR));
  8059. if (paStrings != NULL) {
  8060. va_start(pArg, nStrings);
  8061. for (index = 0; index < nStrings; index++) {
  8062. paStrings[index] = va_arg(pArg, LPWSTR);
  8063. }
  8064. va_end(pArg);
  8065. ReportEvent( hEventLog,
  8066. EVENTLOG_WARNING_TYPE,
  8067. 0, // wCategory
  8068. dwEventID, // dwEventID
  8069. NULL, // lpUserSID
  8070. nStrings, // wNumStrings
  8071. 0, // dwDataSize
  8072. paStrings, // lpStrings
  8073. NULL); // lpRawData
  8074. HeapFree(ghPnPHeap, 0, paStrings);
  8075. }
  8076. DeregisterEventSource(hEventLog);
  8077. }
  8078. BOOL
  8079. LockNotifyList(
  8080. IN LOCKINFO *Lock
  8081. )
  8082. {
  8083. return LockPrivateResource(Lock);
  8084. }
  8085. VOID
  8086. UnlockNotifyList(
  8087. IN LOCKINFO *Lock
  8088. )
  8089. {
  8090. UnlockPrivateResource(Lock);
  8091. }
  8092. PPNP_NOTIFY_LIST
  8093. GetNotifyListForEntry(
  8094. IN PPNP_NOTIFY_ENTRY Entry
  8095. )
  8096. /*++
  8097. Routine Description:
  8098. This routine retrives the notification list that the given entry is in,
  8099. based on the list entry signature. If this entry has been removed from a
  8100. notification list (via DeleteNotifyEntry), NULL is returned.
  8101. Arguments:
  8102. Entry - Specifies a notification entry for the coresponding notification
  8103. list is to be found.
  8104. Return Value:
  8105. Returns the notification list this entry is a member of, or NULL if the
  8106. entry is not in any notification list.
  8107. --*/
  8108. {
  8109. PPNP_NOTIFY_LIST notifyList;
  8110. if (!Entry) {
  8111. return NULL;
  8112. }
  8113. //
  8114. // Retrieve the list pointer from the entry signature.
  8115. // The signature contains two pieces of data.
  8116. //
  8117. // It is a ULONG, with byte 0 being a list index and
  8118. // bytes 1,2,3 being the signature
  8119. // We mask and compare the top 3 bytes to find which list
  8120. // then return the address of the list to lock based on the
  8121. // index in the bottom byte.
  8122. //
  8123. switch (Entry->Signature & LIST_ENTRY_SIGNATURE_MASK) {
  8124. case TARGET_ENTRY_SIGNATURE:
  8125. notifyList = &TargetList[Entry->Signature & LIST_ENTRY_INDEX_MASK];
  8126. break;
  8127. case CLASS_ENTRY_SIGNATURE:
  8128. notifyList = &ClassList[Entry->Signature & LIST_ENTRY_INDEX_MASK];
  8129. break;
  8130. case SERVICE_ENTRY_SIGNATURE:
  8131. notifyList = &ServiceList[Entry->Signature & LIST_ENTRY_INDEX_MASK];
  8132. break;
  8133. case 0:
  8134. //
  8135. // If the entry Signature is 0, this entry has been removed from it's
  8136. // notification list.
  8137. //
  8138. notifyList = NULL;
  8139. break;
  8140. default:
  8141. //
  8142. // Should never get here!
  8143. //
  8144. ASSERT (FALSE);
  8145. notifyList = NULL;
  8146. break;
  8147. }
  8148. return notifyList;
  8149. } // GetNotifyListForEntry
  8150. BOOL
  8151. DeleteNotifyEntry(
  8152. IN PPNP_NOTIFY_ENTRY Entry,
  8153. IN BOOLEAN RpcNotified
  8154. )
  8155. /*++
  8156. Routine Description:
  8157. This routine removes an entry from a notification list and frees the
  8158. memory for that entry.
  8159. Arguments:
  8160. Entry - Specifies an entry in one of the notification lists that is
  8161. to be deleted.
  8162. Return Value:
  8163. Returns TRUE or FALSE.
  8164. --*/
  8165. {
  8166. PPNP_NOTIFY_ENTRY previousEntry = Entry->Previous;
  8167. if (!(Entry->Freed & DEFER_NOTIFY_FREE)) {
  8168. if (previousEntry == NULL) {
  8169. return FALSE;
  8170. }
  8171. //
  8172. // hook up the forward and backwards pointers
  8173. //
  8174. previousEntry->Next = Entry->Next;
  8175. if (Entry->Next) {
  8176. ((PPNP_NOTIFY_ENTRY)(Entry->Next))->Previous = previousEntry;
  8177. }
  8178. //
  8179. // Clear the entry signature now that it is no longer part of any list.
  8180. //
  8181. Entry->Signature = 0;
  8182. }
  8183. if (RpcNotified || (Entry->Freed & DEFER_NOTIFY_FREE)) {
  8184. if (Entry->ClientName) {
  8185. HeapFree (ghPnPHeap,0,Entry->ClientName);
  8186. Entry->ClientName = NULL;
  8187. }
  8188. HeapFree(ghPnPHeap, 0, Entry);
  8189. }else {
  8190. //
  8191. //Let the entry dangle until the RPC rundown
  8192. //
  8193. Entry->Freed |= DEFER_NOTIFY_FREE;
  8194. }
  8195. return TRUE;
  8196. } // DeleteNotifyEntry;
  8197. VOID
  8198. AddNotifyEntry(
  8199. IN PPNP_NOTIFY_LIST NotifyList,
  8200. IN PPNP_NOTIFY_ENTRY NewEntry
  8201. )
  8202. /*++
  8203. Routine Description:
  8204. This routine inserts an entry at the tail of a notification list.
  8205. Arguments:
  8206. Entry - Specifies an entry to be added to a notification list
  8207. Return Value:
  8208. None.
  8209. --*/
  8210. {
  8211. PPNP_NOTIFY_ENTRY previousEntry = NULL, currentEntry = NULL;
  8212. //
  8213. // Skip to the last entry in this list.
  8214. //
  8215. previousEntry = (PPNP_NOTIFY_ENTRY)NotifyList;
  8216. currentEntry = previousEntry->Next;
  8217. while (currentEntry) {
  8218. previousEntry = currentEntry;
  8219. currentEntry = currentEntry->Next;
  8220. }
  8221. //
  8222. // Attach this entry to the end of the list.
  8223. //
  8224. previousEntry->Next = NewEntry;
  8225. NewEntry->Previous = previousEntry;
  8226. NewEntry->Next = NULL;
  8227. return;
  8228. } // AddNotifyEntry;
  8229. PPNP_NOTIFY_ENTRY
  8230. GetNextNotifyEntry(
  8231. IN PPNP_NOTIFY_ENTRY Entry,
  8232. IN DWORD Flags
  8233. )
  8234. /*++
  8235. Routine Description:
  8236. Returns the next entry in the notification list for the entry specified, in
  8237. the direction specified by the Flags.
  8238. Arguments:
  8239. Entry - Specified a notification list entry.
  8240. Flags - Specifies BSF_* flags indicating the direction the list is to be
  8241. traversed. If BSF_QUERY is specified, the previous list entry is
  8242. returned, otherwise returns the next entry forward in the list.
  8243. Return Value:
  8244. Returns the next entry in the notification list, or NULL if no such entry
  8245. exists.
  8246. --*/
  8247. {
  8248. PPNP_NOTIFY_ENTRY nextEntry = NULL;
  8249. if (Entry == NULL) {
  8250. return Entry;
  8251. }
  8252. //
  8253. // Determine if this is a QUERY (or a resume). In which case
  8254. // we go back -> front.
  8255. //
  8256. if (Flags & BSF_QUERY) {
  8257. nextEntry = Entry->Previous;
  8258. //
  8259. // If the previous entry is the list head, there is no next entry.
  8260. //
  8261. if ((nextEntry == NULL) ||
  8262. (nextEntry->Previous == NULL)) {
  8263. return NULL;
  8264. }
  8265. } else {
  8266. nextEntry = Entry->Next;
  8267. }
  8268. return nextEntry;
  8269. }
  8270. PPNP_NOTIFY_ENTRY
  8271. GetFirstNotifyEntry(
  8272. IN PPNP_NOTIFY_LIST List,
  8273. IN DWORD Flags
  8274. )
  8275. /*++
  8276. Routine Description:
  8277. Returns the first entry in the specified notification list, starting from
  8278. the direction specified by the Flags.
  8279. Arguments:
  8280. List - Specified a notification list.
  8281. Flags - Specifies BSF_* flags indicating the end of the list from which the
  8282. first entry is to be retrieved. If BSF_QUERY is specified, the last
  8283. list entry is returned, otherwise returns the first entry in the
  8284. list.
  8285. Return Value:
  8286. Returns the first entry in the notification list, or NULL if no such entry
  8287. exists.
  8288. --*/
  8289. {
  8290. PPNP_NOTIFY_ENTRY previousEntry = NULL, currentEntry = NULL, firstEntry = NULL;
  8291. //
  8292. // Determine if this is a QUERY (or a resume). In which case
  8293. // we go back -> front.
  8294. //
  8295. if (Flags & BSF_QUERY) {
  8296. //
  8297. // Skip to the last entry in this list.
  8298. //
  8299. previousEntry = (PPNP_NOTIFY_ENTRY)List;
  8300. currentEntry = previousEntry->Next;
  8301. while (currentEntry) {
  8302. previousEntry = currentEntry;
  8303. currentEntry = currentEntry->Next;
  8304. }
  8305. if (!previousEntry->Previous) {
  8306. //
  8307. // If the list is empty, there is no first entry.
  8308. //
  8309. firstEntry = NULL;
  8310. } else {
  8311. firstEntry = previousEntry;
  8312. }
  8313. } else {
  8314. firstEntry = (PPNP_NOTIFY_ENTRY)List->Next;
  8315. }
  8316. return firstEntry;
  8317. }
  8318. ULONG
  8319. HashString(
  8320. IN LPWSTR String,
  8321. IN ULONG Buckets
  8322. )
  8323. /*++
  8324. Routine Description:
  8325. This routine performs a quick and dirty hash of a unicode string.
  8326. Arguments:
  8327. String - Null-terminated unicode string to perform hash on.
  8328. Buckets - Number of hashing buckets.
  8329. Return Value:
  8330. Returns a hash value between 0 and Buckets.
  8331. --*/
  8332. {
  8333. LPWSTR p = String;
  8334. ULONG hash = 0;
  8335. while (*p) {
  8336. hash ^= *p;
  8337. p++;
  8338. }
  8339. hash = hash % Buckets;
  8340. return hash;
  8341. } // HashString
  8342. DWORD
  8343. MapQueryEventToCancelEvent(
  8344. IN DWORD QueryEventId
  8345. )
  8346. /*++
  8347. Routine Description:
  8348. This routine maps a query device event id (such as query remove) to the
  8349. corresponding cancel device event id (such as cancel remove). The event
  8350. ids are based on DBT_Xxx values from DBT.H.
  8351. Arguments:
  8352. QueryEventId - A DBT_Xxx query type device event id.
  8353. Return Value:
  8354. Returns the corresponding cancel device event id or -1 if it fails.
  8355. --*/
  8356. {
  8357. DWORD cancelEventId;
  8358. switch (QueryEventId) {
  8359. case DBT_QUERYCHANGECONFIG:
  8360. cancelEventId = DBT_CONFIGCHANGECANCELED;
  8361. break;
  8362. case DBT_DEVICEQUERYREMOVE:
  8363. cancelEventId = DBT_DEVICEQUERYREMOVEFAILED;
  8364. break;
  8365. case PBT_APMQUERYSUSPEND:
  8366. cancelEventId = PBT_APMQUERYSUSPENDFAILED;
  8367. break;
  8368. case PBT_APMQUERYSTANDBY:
  8369. cancelEventId = PBT_APMQUERYSTANDBYFAILED;
  8370. default:
  8371. cancelEventId = (DWORD)-1;
  8372. break;
  8373. }
  8374. return cancelEventId;
  8375. } // MapQueryEventToCancelEvent
  8376. VOID
  8377. FixUpDeviceId(
  8378. IN OUT LPWSTR DeviceId
  8379. )
  8380. /*++
  8381. Routine Description:
  8382. This routine copies a device id, fixing it up as it does the copy.
  8383. 'Fixing up' means that the string is made upper-case, and that the
  8384. following character ranges are turned into underscores (_):
  8385. c <= 0x20 (' ')
  8386. c > 0x7F
  8387. c == 0x2C (',')
  8388. (NOTE: This algorithm is also implemented in the Config Manager APIs,
  8389. and must be kept in sync with that routine. To maintain device identifier
  8390. compatibility, these routines must work the same as Win95.)
  8391. Arguments:
  8392. Return Value:
  8393. None.
  8394. --*/
  8395. {
  8396. PWCHAR p;
  8397. CharUpper(DeviceId);
  8398. p = DeviceId;
  8399. while (*p) {
  8400. if ((*p <= TEXT(' ')) || (*p > (WCHAR)0x7F) || (*p == TEXT(','))) {
  8401. *p = TEXT('_');
  8402. }
  8403. p++;
  8404. }
  8405. } // FixUpDeviceId
  8406. BOOL
  8407. GetWindowsExeFileName(
  8408. IN HWND hWnd,
  8409. OUT LPWSTR lpszFileName,
  8410. IN OUT PULONG pulFileNameLength
  8411. )
  8412. /*++
  8413. Routine Description:
  8414. This routine retrieves the module file name for the process that the
  8415. specified window belongs to.
  8416. Arguments:
  8417. hWnd - Supplies the handle to the window whose process module
  8418. file name is to be retrieved.
  8419. lpszFileName - Supplies the address of a variable to receive, upon
  8420. success, the module file name of the window's process.
  8421. pulFileNameLength - Supplies the address of a variable specifying the size of
  8422. the of buffer specified by the lpszFileName parameter.
  8423. Upon success, this address will specify the length of
  8424. the string stored in that buffer by this routine.
  8425. Return Value:
  8426. Returns TRUE.
  8427. Notes:
  8428. Not implemented. Currently returns a NULL string for the file name.
  8429. --*/
  8430. {
  8431. UNREFERENCED_PARAMETER(hWnd);
  8432. if ((!ARGUMENT_PRESENT(lpszFileName)) ||
  8433. (!ARGUMENT_PRESENT(pulFileNameLength))) {
  8434. return FALSE;
  8435. }
  8436. if (*pulFileNameLength > 0) {
  8437. *pulFileNameLength = 0;
  8438. lpszFileName[0] = UNICODE_NULL;
  8439. }
  8440. return TRUE;
  8441. } // GetWindowsExeFileName
  8442. BOOL
  8443. InitializeHydraInterface(
  8444. VOID
  8445. )
  8446. /*++
  8447. Routine Description:
  8448. This routine loads the terminal services support libraries and locates
  8449. required function entrypoints.
  8450. Arguments:
  8451. None.
  8452. Return Value:
  8453. Returns TRUE if the terminal services support libraries were successfully
  8454. loaded, and entrypoints located.
  8455. --*/
  8456. {
  8457. BOOL Status = FALSE;
  8458. //
  8459. // Load the base library that contains the user message dispatch routines
  8460. // for Terminal Services.
  8461. //
  8462. ghWinStaLib = LoadLibrary(WINSTA_DLL);
  8463. if (!ghWinStaLib) {
  8464. return FALSE;
  8465. }
  8466. fpWinStationSendWindowMessage =
  8467. (FP_WINSTASENDWINDOWMESSAGE)GetProcAddress(
  8468. ghWinStaLib,
  8469. "WinStationSendWindowMessage");
  8470. fpWinStationBroadcastSystemMessage =
  8471. (FP_WINSTABROADCASTSYSTEMMESSAGE)GetProcAddress(
  8472. ghWinStaLib,
  8473. "WinStationBroadcastSystemMessage");
  8474. fpWinStationQueryInformationW =
  8475. (FP_WINSTAQUERYINFORMATIONW)GetProcAddress(
  8476. ghWinStaLib,
  8477. "WinStationQueryInformationW");
  8478. if (!fpWinStationSendWindowMessage ||
  8479. !fpWinStationBroadcastSystemMessage ||
  8480. !fpWinStationQueryInformationW) {
  8481. goto Clean0;
  8482. }
  8483. //
  8484. // Load the library that contains Terminal Services support routines.
  8485. //
  8486. ghWtsApi32Lib = LoadLibrary(WTSAPI32_DLL);
  8487. if (!ghWtsApi32Lib) {
  8488. goto Clean0;
  8489. }
  8490. fpWTSQuerySessionInformation =
  8491. (FP_WTSQUERYSESSIONINFORMATION)GetProcAddress(
  8492. ghWtsApi32Lib,
  8493. "WTSQuerySessionInformationW");
  8494. fpWTSFreeMemory =
  8495. (FP_WTSFREEMEMORY)GetProcAddress(
  8496. ghWtsApi32Lib,
  8497. "WTSFreeMemory");
  8498. if (!fpWTSQuerySessionInformation ||
  8499. !fpWTSFreeMemory) {
  8500. goto Clean0;
  8501. }
  8502. Status = TRUE;
  8503. Clean0:
  8504. ASSERT(Status == TRUE);
  8505. if (!Status) {
  8506. //
  8507. // Something failed. Unload all libraries.
  8508. //
  8509. fpWinStationSendWindowMessage = NULL;
  8510. fpWinStationBroadcastSystemMessage = NULL;
  8511. fpWinStationQueryInformationW = NULL;
  8512. if (ghWinStaLib) {
  8513. FreeLibrary(ghWinStaLib);
  8514. ghWinStaLib = NULL;
  8515. }
  8516. fpWTSQuerySessionInformation = NULL;
  8517. fpWTSFreeMemory = NULL;
  8518. if (ghWtsApi32Lib) {
  8519. FreeLibrary(ghWtsApi32Lib);
  8520. ghWtsApi32Lib = NULL;
  8521. }
  8522. }
  8523. return Status;
  8524. } // InitializeHydraInterface
  8525. BOOL
  8526. GetClientName(
  8527. IN PPNP_NOTIFY_ENTRY entry,
  8528. OUT LPWSTR lpszClientName,
  8529. IN OUT PULONG pulClientNameLength
  8530. )
  8531. /*++
  8532. Routine Description:
  8533. This routine retrieves the client name for the specified notification list
  8534. entry.
  8535. Arguments:
  8536. entry - Specifies a notification list entry.
  8537. lpszClientName - Supplies the address of a variable to receive, the
  8538. client name of the window's process.
  8539. pulClientNameLength - Supplies the address of a variable specifying the size of
  8540. the of buffer specified by the lpszFileName parameter.
  8541. Upon return, this address will specify the length of
  8542. the string stored in that buffer by this routine.
  8543. Return Value:
  8544. Returns TRUE.
  8545. --*/
  8546. {
  8547. size_t BufferLen = 0, ClientNameLen = 0;
  8548. //
  8549. // Validate parameters.
  8550. //
  8551. if ((!ARGUMENT_PRESENT(lpszClientName)) ||
  8552. (!ARGUMENT_PRESENT(pulClientNameLength)) ||
  8553. (*pulClientNameLength == 0)) {
  8554. return FALSE;
  8555. }
  8556. //
  8557. // Copy as much of the client name that will fit into the specified buffer,
  8558. // (including the NULL terminating char).
  8559. //
  8560. BufferLen = *pulClientNameLength;
  8561. *pulClientNameLength = 0;
  8562. *lpszClientName = L'\0';
  8563. if ((!ARGUMENT_PRESENT(entry)) ||
  8564. (entry->ClientName == NULL)) {
  8565. return FALSE;
  8566. }
  8567. ASSERT(BufferLen > 0);
  8568. //
  8569. // Copy client name to specified buffer, allow truncation.
  8570. //
  8571. if (FAILED(StringCchCopyEx(
  8572. lpszClientName,
  8573. BufferLen,
  8574. entry->ClientName,
  8575. NULL, NULL,
  8576. STRSAFE_IGNORE_NULLS))) {
  8577. //
  8578. // Failure from truncation can be handled safely.
  8579. //
  8580. NOTHING;
  8581. }
  8582. //
  8583. // Count the number of characters copied to the buffer.
  8584. //
  8585. if (FAILED(StringCchLength(
  8586. lpszClientName,
  8587. BufferLen,
  8588. &ClientNameLen))) {
  8589. *lpszClientName = L'\0';
  8590. return FALSE;
  8591. }
  8592. //
  8593. // The size returned does not include the terminating NULL.
  8594. //
  8595. ASSERT(ClientNameLen < MAX_SERVICE_NAME_LEN);
  8596. *pulClientNameLength = (ULONG)ClientNameLen;
  8597. return TRUE;
  8598. } // GetClientName
  8599. void __RPC_USER
  8600. PNP_NOTIFICATION_CONTEXT_rundown(
  8601. PPNP_NOTIFICATION_CONTEXT hEntry
  8602. )
  8603. /*++
  8604. Routine Description:
  8605. Rundown routine for RPC. This will get called if a client/server pipe
  8606. breaks without unregistering a notification. If a notification is in
  8607. progress when rundown is called, the entry is kept in a deferred list, and
  8608. this routines is explicitly called again for the deferred entry, after
  8609. notification is complete.
  8610. This routine frees the memory associated with the notification entry that is
  8611. no longer needed.
  8612. Arguments:
  8613. hEntry - Specifies a notification entry for which RPC has requested rundown.
  8614. Return Value:
  8615. None.
  8616. --*/
  8617. {
  8618. PPNP_NOTIFY_LIST notifyList = NULL;
  8619. PPNP_NOTIFY_ENTRY node;
  8620. PPNP_DEFERRED_LIST rundownNode;
  8621. BOOLEAN bLocked = FALSE;
  8622. KdPrintEx((DPFLTR_PNPMGR_ID,
  8623. DBGF_WARNINGS | DBGF_EVENT,
  8624. "UMPNPMGR: Cleaning up broken pipe\n"));
  8625. try {
  8626. EnterCriticalSection(&RegistrationCS);
  8627. node = (PPNP_NOTIFY_ENTRY) hEntry;
  8628. if (gNotificationInProg != 0) {
  8629. //
  8630. // Before freeing the entry, we need to make sure that it's not sitting
  8631. // around in the deferred RegisterList or UnregisterList.
  8632. //
  8633. if (RegisterList != NULL) {
  8634. //
  8635. // Check to see if this entry is in the deferred RegisterList.
  8636. //
  8637. PPNP_DEFERRED_LIST currReg,prevReg;
  8638. currReg = RegisterList;
  8639. prevReg = NULL;
  8640. while (currReg) {
  8641. ASSERT(currReg->Entry->Unregistered);
  8642. if (currReg->Entry == node) {
  8643. //
  8644. // Remove this entry from the deferred RegisterList.
  8645. //
  8646. if (prevReg) {
  8647. prevReg->Next = currReg->Next;
  8648. } else {
  8649. RegisterList = currReg->Next;
  8650. }
  8651. HeapFree(ghPnPHeap, 0, currReg);
  8652. if (prevReg) {
  8653. currReg = prevReg->Next;
  8654. } else {
  8655. currReg = RegisterList;
  8656. }
  8657. } else {
  8658. prevReg = currReg;
  8659. currReg = currReg->Next;
  8660. }
  8661. }
  8662. }
  8663. if (UnregisterList != NULL) {
  8664. //
  8665. // Check to see if this entry is in the deferred UnregisterList.
  8666. //
  8667. PPNP_DEFERRED_LIST currUnreg,prevUnreg;
  8668. currUnreg = UnregisterList;
  8669. prevUnreg = NULL;
  8670. while (currUnreg) {
  8671. ASSERT(currUnreg->Entry->Unregistered);
  8672. if (currUnreg->Entry == node) {
  8673. //
  8674. // Remove this entry from the deferred UnregisterList.
  8675. //
  8676. if (prevUnreg) {
  8677. prevUnreg->Next = currUnreg->Next;
  8678. } else {
  8679. UnregisterList = currUnreg->Next;
  8680. }
  8681. HeapFree(ghPnPHeap, 0, currUnreg);
  8682. if (prevUnreg) {
  8683. currUnreg = prevUnreg->Next;
  8684. } else {
  8685. currUnreg = UnregisterList;
  8686. }
  8687. } else {
  8688. prevUnreg = currUnreg;
  8689. currUnreg = currUnreg->Next;
  8690. }
  8691. }
  8692. }
  8693. //
  8694. // If the entry to be rundown is part of a notification list, make
  8695. // sure it does not get notified.
  8696. //
  8697. notifyList = GetNotifyListForEntry(node);
  8698. if (notifyList) {
  8699. LockNotifyList(&notifyList->Lock);
  8700. bLocked = TRUE;
  8701. node->Unregistered = TRUE;
  8702. UnlockNotifyList(&notifyList->Lock);
  8703. bLocked = FALSE;
  8704. }
  8705. //
  8706. // Delay rundown of this entry until after the notification in
  8707. // progress is complete.
  8708. //
  8709. rundownNode = (PPNP_DEFERRED_LIST)
  8710. HeapAlloc(ghPnPHeap,
  8711. 0,
  8712. sizeof (PNP_DEFERRED_LIST));
  8713. if (!rundownNode) {
  8714. KdPrintEx((DPFLTR_PNPMGR_ID,
  8715. DBGF_ERRORS | DBGF_WARNINGS,
  8716. "UMPNPMGR: Error allocating deferred list entry during RPC rundown!\n"));
  8717. goto Clean0;
  8718. }
  8719. rundownNode->hBinding = 0;
  8720. rundownNode->Entry = node;
  8721. rundownNode->Next = RundownList;
  8722. RundownList = rundownNode;
  8723. } else {
  8724. if (!(node->Freed & DEFER_NOTIFY_FREE)) {
  8725. //
  8726. // This entry is still in a notification list.
  8727. //
  8728. notifyList = GetNotifyListForEntry(node);
  8729. ASSERT(notifyList);
  8730. if (notifyList) {
  8731. //
  8732. // Lock the notification list and delete this entry.
  8733. //
  8734. LockNotifyList (&notifyList->Lock);
  8735. bLocked = TRUE;
  8736. }
  8737. node->Freed |= (PNP_UNREG_FREE|PNP_UNREG_RUNDOWN);
  8738. DeleteNotifyEntry (node,TRUE);
  8739. if (notifyList) {
  8740. UnlockNotifyList (&notifyList->Lock);
  8741. bLocked = FALSE;
  8742. }
  8743. } else {
  8744. //
  8745. // This node has been removed from the list, and should just be deleted
  8746. //
  8747. DeleteNotifyEntry (node,TRUE);
  8748. }
  8749. }
  8750. Clean0:
  8751. LeaveCriticalSection(&RegistrationCS);
  8752. } except (EXCEPTION_EXECUTE_HANDLER) {
  8753. KdPrintEx((DPFLTR_PNPMGR_ID,
  8754. DBGF_ERRORS | DBGF_WARNINGS,
  8755. "UMPNPMGR: Exception during PNP_NOTIFICATION_CONTEXT_rundown!\n"));
  8756. ASSERT(0);
  8757. if (bLocked) {
  8758. UnlockNotifyList (&notifyList->Lock);
  8759. }
  8760. LeaveCriticalSection(&RegistrationCS);
  8761. }
  8762. return;
  8763. } // PNP_NOTIFICATION_CONTEXT_rundown
  8764. DWORD
  8765. LoadDeviceInstaller(
  8766. VOID
  8767. )
  8768. /*++
  8769. Routine Description:
  8770. This routine loads setupapi.dll and retrieves the necessary device install
  8771. entrypoints. It also creates two named events used to communicate with the
  8772. client-side UI in the case where there's a user logged in.
  8773. If setupapi.dll is already loaded, it simply returns success.
  8774. Arguments:
  8775. None
  8776. Return Value:
  8777. If successful, NO_ERROR is returned. Otherwise, a Win32 error code is
  8778. returned indicating the cause of failure.
  8779. --*/
  8780. {
  8781. DWORD Err = NO_ERROR;
  8782. DWORD SetupGlobalFlags;
  8783. if (ghDeviceInstallerLib) {
  8784. return NO_ERROR;
  8785. }
  8786. ghDeviceInstallerLib =
  8787. LoadLibrary(SETUPAPI_DLL);
  8788. if (!ghDeviceInstallerLib) {
  8789. return GetLastError();
  8790. }
  8791. try {
  8792. //
  8793. // Locate the SETUPAPI entrypoints required to perform device
  8794. // installation.
  8795. //
  8796. fpCreateDeviceInfoList =
  8797. (FP_CREATEDEVICEINFOLIST)GetProcAddress(
  8798. ghDeviceInstallerLib,
  8799. "SetupDiCreateDeviceInfoList");
  8800. if (!fpCreateDeviceInfoList) {
  8801. goto HitFailure;
  8802. }
  8803. fpOpenDeviceInfo =
  8804. (FP_OPENDEVICEINFO)GetProcAddress(
  8805. ghDeviceInstallerLib,
  8806. "SetupDiOpenDeviceInfoW");
  8807. if (!fpOpenDeviceInfo) {
  8808. goto HitFailure;
  8809. }
  8810. fpBuildDriverInfoList =
  8811. (FP_BUILDDRIVERINFOLIST)GetProcAddress(
  8812. ghDeviceInstallerLib,
  8813. "SetupDiBuildDriverInfoList");
  8814. if (!fpBuildDriverInfoList) {
  8815. goto HitFailure;
  8816. }
  8817. fpDestroyDeviceInfoList =
  8818. (FP_DESTROYDEVICEINFOLIST)GetProcAddress(
  8819. ghDeviceInstallerLib,
  8820. "SetupDiDestroyDeviceInfoList");
  8821. if (!fpDestroyDeviceInfoList) {
  8822. goto HitFailure;
  8823. }
  8824. fpCallClassInstaller =
  8825. (FP_CALLCLASSINSTALLER)GetProcAddress(
  8826. ghDeviceInstallerLib,
  8827. "SetupDiCallClassInstaller");
  8828. if (!fpCallClassInstaller) {
  8829. goto HitFailure;
  8830. }
  8831. fpInstallClass =
  8832. (FP_INSTALLCLASS)GetProcAddress(
  8833. ghDeviceInstallerLib,
  8834. "SetupDiInstallClassW");
  8835. if (!fpInstallClass) {
  8836. goto HitFailure;
  8837. }
  8838. fpGetSelectedDriver =
  8839. (FP_GETSELECTEDDRIVER)GetProcAddress(
  8840. ghDeviceInstallerLib,
  8841. "SetupDiGetSelectedDriverW");
  8842. if (!fpGetSelectedDriver) {
  8843. goto HitFailure;
  8844. }
  8845. fpGetDriverInfoDetail =
  8846. (FP_GETDRIVERINFODETAIL)GetProcAddress(
  8847. ghDeviceInstallerLib,
  8848. "SetupDiGetDriverInfoDetailW");
  8849. if (!fpGetDriverInfoDetail) {
  8850. goto HitFailure;
  8851. }
  8852. fpGetDeviceInstallParams =
  8853. (FP_GETDEVICEINSTALLPARAMS)GetProcAddress(
  8854. ghDeviceInstallerLib,
  8855. "SetupDiGetDeviceInstallParamsW");
  8856. if (!fpGetDeviceInstallParams) {
  8857. goto HitFailure;
  8858. }
  8859. fpSetDeviceInstallParams =
  8860. (FP_SETDEVICEINSTALLPARAMS)GetProcAddress(
  8861. ghDeviceInstallerLib,
  8862. "SetupDiSetDeviceInstallParamsW");
  8863. if (!fpSetDeviceInstallParams) {
  8864. goto HitFailure;
  8865. }
  8866. fpGetDriverInstallParams =
  8867. (FP_GETDRIVERINSTALLPARAMS)GetProcAddress(
  8868. ghDeviceInstallerLib,
  8869. "SetupDiGetDriverInstallParamsW");
  8870. if (!fpGetDriverInstallParams) {
  8871. goto HitFailure;
  8872. }
  8873. fpSetClassInstallParams =
  8874. (FP_SETCLASSINSTALLPARAMS)GetProcAddress(
  8875. ghDeviceInstallerLib,
  8876. "SetupDiSetClassInstallParamsW");
  8877. if (!fpSetClassInstallParams) {
  8878. goto HitFailure;
  8879. }
  8880. fpGetClassInstallParams =
  8881. (FP_GETCLASSINSTALLPARAMS)GetProcAddress(
  8882. ghDeviceInstallerLib,
  8883. "SetupDiGetClassInstallParamsW");
  8884. if (!fpGetClassInstallParams) {
  8885. goto HitFailure;
  8886. }
  8887. fpOpenInfFile =
  8888. (FP_OPENINFFILE)GetProcAddress(
  8889. ghDeviceInstallerLib,
  8890. "SetupOpenInfFileW");
  8891. if (!fpOpenInfFile) {
  8892. goto HitFailure;
  8893. }
  8894. fpCloseInfFile =
  8895. (FP_CLOSEINFFILE)GetProcAddress(
  8896. ghDeviceInstallerLib,
  8897. "SetupCloseInfFile");
  8898. if (!fpCloseInfFile) {
  8899. goto HitFailure;
  8900. }
  8901. fpFindFirstLine =
  8902. (FP_FINDFIRSTLINE)GetProcAddress(
  8903. ghDeviceInstallerLib,
  8904. "SetupFindFirstLineW");
  8905. if (!fpFindFirstLine) {
  8906. goto HitFailure;
  8907. }
  8908. fpFindNextMatchLine =
  8909. (FP_FINDNEXTMATCHLINE)GetProcAddress(
  8910. ghDeviceInstallerLib,
  8911. "SetupFindNextMatchLineW");
  8912. if (!fpFindNextMatchLine) {
  8913. goto HitFailure;
  8914. }
  8915. fpGetStringField =
  8916. (FP_GETSTRINGFIELD)GetProcAddress(
  8917. ghDeviceInstallerLib,
  8918. "SetupGetStringFieldW");
  8919. if (!fpGetStringField) {
  8920. goto HitFailure;
  8921. }
  8922. fpSetGlobalFlags =
  8923. (FP_SETGLOBALFLAGS)GetProcAddress(
  8924. ghDeviceInstallerLib,
  8925. "pSetupSetGlobalFlags");
  8926. if (!fpSetGlobalFlags) {
  8927. goto HitFailure;
  8928. }
  8929. fpGetGlobalFlags =
  8930. (FP_GETGLOBALFLAGS)GetProcAddress(
  8931. ghDeviceInstallerLib,
  8932. "pSetupGetGlobalFlags");
  8933. if (!fpGetGlobalFlags) {
  8934. goto HitFailure;
  8935. }
  8936. fpAccessRunOnceNodeList =
  8937. (FP_ACCESSRUNONCENODELIST)GetProcAddress(
  8938. ghDeviceInstallerLib,
  8939. "pSetupAccessRunOnceNodeList");
  8940. if (!fpAccessRunOnceNodeList) {
  8941. goto HitFailure;
  8942. }
  8943. fpDestroyRunOnceNodeList =
  8944. (FP_DESTROYRUNONCENODELIST)GetProcAddress(
  8945. ghDeviceInstallerLib,
  8946. "pSetupDestroyRunOnceNodeList");
  8947. if (!fpDestroyRunOnceNodeList) {
  8948. goto HitFailure;
  8949. }
  8950. //
  8951. // Now configure setupapi for server-side installation
  8952. //
  8953. SetupGlobalFlags = fpGetGlobalFlags();
  8954. //
  8955. // We want to run non-interactive and do RunOnce entries server-side
  8956. //
  8957. SetupGlobalFlags |= (PSPGF_NONINTERACTIVE | PSPGF_SERVER_SIDE_RUNONCE);
  8958. //
  8959. // Make sure we _aren't_ skipping backup--it is essential that we be
  8960. // able to completely back-out of an installation half-way through if
  8961. // we encounter a failure (e.g., an unsigned file).
  8962. //
  8963. SetupGlobalFlags &= ~PSPGF_NO_BACKUP;
  8964. fpSetGlobalFlags(SetupGlobalFlags);
  8965. //
  8966. // If we get to here, we succeeded.
  8967. //
  8968. goto clean0;
  8969. HitFailure:
  8970. //
  8971. // Failed to retrieve some entrypoint.
  8972. //
  8973. Err = GetLastError();
  8974. clean0:
  8975. NOTHING;
  8976. } except(EXCEPTION_EXECUTE_HANDLER) {
  8977. KdPrintEx((DPFLTR_PNPMGR_ID,
  8978. DBGF_ERRORS | DBGF_WARNINGS,
  8979. "UMPNPMGR: Exception during LoadDeviceInstaller!\n"));
  8980. ASSERT(0);
  8981. Err = ERROR_INVALID_DATA;
  8982. }
  8983. if(Err != NO_ERROR) {
  8984. KdPrintEx((DPFLTR_PNPMGR_ID,
  8985. DBGF_INSTALL | DBGF_ERRORS,
  8986. "UMPNPMGR: failed to load device installer, error = %d\n",
  8987. Err));
  8988. FreeLibrary(ghDeviceInstallerLib);
  8989. ghDeviceInstallerLib = NULL;
  8990. } else {
  8991. KdPrintEx((DPFLTR_PNPMGR_ID,
  8992. DBGF_INSTALL,
  8993. "UMPNPMGR: Loaded device installer\n",
  8994. Err));
  8995. }
  8996. return Err;
  8997. } // LoadDeviceInstaller
  8998. VOID
  8999. UnloadDeviceInstaller(
  9000. VOID
  9001. )
  9002. /*++
  9003. Routine Description:
  9004. This unloads setupapi.dll if it's presently loaded.
  9005. Arguments:
  9006. None.
  9007. Return Value:
  9008. None.
  9009. --*/
  9010. {
  9011. PINSTALL_CLIENT_ENTRY pDeviceInstallClient, pNextDeviceInstallClient;
  9012. //
  9013. // Unload setupapi.dll.
  9014. //
  9015. if(ghDeviceInstallerLib) {
  9016. FreeLibrary(ghDeviceInstallerLib);
  9017. ghDeviceInstallerLib = NULL;
  9018. KdPrintEx((DPFLTR_PNPMGR_ID,
  9019. DBGF_INSTALL,
  9020. "UMPNPMGR: Unloaded device installer\n"));
  9021. }
  9022. //
  9023. // Close any device install clients that exist.
  9024. //
  9025. LockNotifyList(&InstallClientList.Lock);
  9026. pDeviceInstallClient = InstallClientList.Next;
  9027. while (pDeviceInstallClient) {
  9028. ASSERT(pDeviceInstallClient->RefCount == 1);
  9029. pNextDeviceInstallClient = pDeviceInstallClient->Next;
  9030. DereferenceDeviceInstallClient(pDeviceInstallClient);
  9031. pDeviceInstallClient = pNextDeviceInstallClient;
  9032. }
  9033. UnlockNotifyList(&InstallClientList.Lock);
  9034. return;
  9035. } // UnloadDeviceInstaller
  9036. DWORD
  9037. InstallDeviceServerSide(
  9038. IN LPWSTR pszDeviceId,
  9039. IN OUT PBOOL RebootRequired,
  9040. IN OUT PBOOL DeviceHasProblem,
  9041. IN OUT PULONG SessionId,
  9042. IN ULONG Flags
  9043. )
  9044. /*++
  9045. Routine Description:
  9046. This routine attempts to install the specified device in the context of
  9047. umpnpmgr (i.e., on the server-side of the ConfigMgr interface).
  9048. Arguments:
  9049. pszDeviceId - device instance ID of the devnode to be installed.
  9050. RebootRequired - Supplies the address of a boolean variable that will be
  9051. set to TRUE if the (successful) installation of this device requires a
  9052. reboot. Note, the existing value of this variable is preserved if
  9053. either (a) the installation fails or (b) no reboot was required.
  9054. DeviceHasProblem - Supplies the address of a boolean variable that will be
  9055. set to TRUE if the device has a CM_PROB_Xxx code after the drivers
  9056. were installed. Note, this value is only set if the installation
  9057. succeedes.
  9058. SessionId - Supplies the address of a variable containing the SessionId on
  9059. which the device install client is to be displayed. If successful, the
  9060. SessionId will contain the id of the session in which the device install
  9061. client UI process was launched. Otherwise, will contain an invalid
  9062. session id INVALID_SESSION, (0xFFFFFFFF).
  9063. Flags - Specifies flags describing the behavior of the device install client.
  9064. The following flags are currently defined:
  9065. DEVICE_INSTALL_DISPLAY_ON_CONSOLE - if specified, the value in the
  9066. SessionId variable will be ignored, and the device installclient will
  9067. always be displayed on the current active console session.
  9068. Return Value:
  9069. If the device installation was successful, the return value is NO_ERROR.
  9070. Otherwise, the return value is a Win32 error code indicating the cause of
  9071. failure.
  9072. --*/
  9073. {
  9074. DWORD Err;
  9075. HDEVINFO DeviceInfoSet;
  9076. SP_DEVINFO_DATA DeviceInfoData;
  9077. LPWSTR pszClassGuid;
  9078. WCHAR szBuffer[MAX_PATH];
  9079. HKEY hKey;
  9080. SP_DEVINSTALL_PARAMS DeviceInstallParams;
  9081. BOOL b, bDoClientUI = FALSE;
  9082. LPWSTR p;
  9083. SP_DRVINFO_DATA DriverInfoData;
  9084. ULONG ulType;
  9085. ULONG ulSize;
  9086. DWORD Capabilities;
  9087. SP_NEWDEVICEWIZARD_DATA NewDevWizData;
  9088. BOOL RemoveNewDevDescValue = FALSE;
  9089. PSP_DRVINFO_DETAIL_DATA pDriverInfoDetailData = NULL;
  9090. DWORD DriverInfoDetailDataSize;
  9091. HINF hInf;
  9092. INFCONTEXT InfContext;
  9093. DWORD i, dwWait;
  9094. HANDLE hFinishEvents[3] = { NULL, NULL, NULL };
  9095. PINSTALL_CLIENT_ENTRY pDeviceInstallClient = NULL;
  9096. ULONG ulSessionId = INVALID_SESSION;
  9097. ULONG ulTransferLen;
  9098. ULONG ulStatus, ulProblem;
  9099. HRESULT hr;
  9100. //
  9101. // Now create a container set for our device information element.
  9102. //
  9103. DeviceInfoSet = fpCreateDeviceInfoList(NULL, NULL);
  9104. if(DeviceInfoSet == INVALID_HANDLE_VALUE) {
  9105. return GetLastError();
  9106. }
  9107. DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
  9108. if(!fpOpenDeviceInfo(DeviceInfoSet, pszDeviceId, NULL, 0, &DeviceInfoData)) {
  9109. goto clean1;
  9110. }
  9111. //
  9112. // OK, it looks like we're going to be able to attempt a server-side
  9113. // install. Next up is the (potentially time-consuming) driver search.
  9114. // Before we start that, we want to fire up some UI on the client side (if
  9115. // somebody is logged in) letting them know we've found their hardware and
  9116. // are working on installing it.
  9117. //
  9118. // NOTE: We don't fire up client-side UI if the device has the SilentInstall
  9119. // capability.
  9120. //
  9121. ulSize = ulTransferLen = sizeof(Capabilities);
  9122. if ((CR_SUCCESS != PNP_GetDeviceRegProp(NULL,
  9123. pszDeviceId,
  9124. CM_DRP_CAPABILITIES,
  9125. &ulType,
  9126. (LPBYTE)&Capabilities,
  9127. &ulTransferLen,
  9128. &ulSize,
  9129. 0))
  9130. || !(Capabilities & CM_DEVCAP_SILENTINSTALL)) {
  9131. //
  9132. // Either we couldn't retrieve the capabilities property (shouldn't
  9133. // happen, or we did retrieve it but the silent-install bit wasn't set.
  9134. //
  9135. bDoClientUI = TRUE;
  9136. //
  9137. // If we're not going to determine the session to use for UI, use the
  9138. // SessionId supplied by the caller.
  9139. //
  9140. if ((Flags & DEVICE_INSTALL_DISPLAY_ON_CONSOLE) == 0) {
  9141. ASSERT(*SessionId != INVALID_SESSION);
  9142. ulSessionId = *SessionId;
  9143. }
  9144. //
  9145. // Go ahead and fire up the client-side UI.
  9146. //
  9147. DoDeviceInstallClient(pszDeviceId,
  9148. &ulSessionId,
  9149. Flags | DEVICE_INSTALL_UI_ONLY | DEVICE_INSTALL_PLAY_SOUND,
  9150. &pDeviceInstallClient);
  9151. }
  9152. //
  9153. // Do a default driver search for this device.
  9154. //
  9155. if(!fpBuildDriverInfoList(DeviceInfoSet, &DeviceInfoData, SPDIT_COMPATDRIVER)) {
  9156. goto clean1;
  9157. }
  9158. //
  9159. // Select the best driver from the list we just built.
  9160. //
  9161. if(!fpCallClassInstaller(DIF_SELECTBESTCOMPATDRV, DeviceInfoSet, &DeviceInfoData)) {
  9162. goto clean1;
  9163. }
  9164. DriverInfoData.cbSize = sizeof(SP_DRVINFO_DATA);
  9165. b = fpGetSelectedDriver(DeviceInfoSet, &DeviceInfoData, &DriverInfoData);
  9166. ASSERT(b); // the above call shouldn't fail
  9167. if(!b) {
  9168. goto clean1;
  9169. }
  9170. //
  9171. // NOTE: the multi-port serial class has some buggy co-installers that
  9172. // always popup UI, without using the finish-install wizard page mechanism,
  9173. // and without regard to the DI_QUIETINSTALL flag. Until they clean up
  9174. // their act, we must disallow server-side installation of those devices
  9175. // as well.
  9176. //
  9177. if(GuidEqual(&GUID_DEVCLASS_MULTIPORTSERIAL, &(DeviceInfoData.ClassGuid))) {
  9178. Err = ERROR_DI_DONT_INSTALL;
  9179. goto clean0;
  9180. }
  9181. //
  9182. // Kludge to allow INFs to force client-side (i.e., interactive)
  9183. // installation for certain devices. They do this by referencing a
  9184. // hardware or compatible ID in an "InteractiveInstall" entry in the INF's
  9185. // [ControlFlags] section. The format of one of these lines is:
  9186. //
  9187. // InteractiveInstall = <ID1> [, <ID2>... ]
  9188. //
  9189. // and there may be any number of these lines.
  9190. //
  9191. //
  9192. // First, retrieve the driver info detail data (this contains the hardware
  9193. // ID and any compatible IDs specified by this INF driver entry).
  9194. //
  9195. b = fpGetDriverInfoDetail(DeviceInfoSet,
  9196. &DeviceInfoData,
  9197. &DriverInfoData,
  9198. NULL,
  9199. 0,
  9200. &DriverInfoDetailDataSize
  9201. );
  9202. Err = GetLastError();
  9203. //
  9204. // The above call to get driver info detail data should never succeed
  9205. // because the buffer will alwyas be too small (we're just interested in
  9206. // sizing the buffer at this point).
  9207. //
  9208. ASSERT(!b && (Err == ERROR_INSUFFICIENT_BUFFER));
  9209. if(b || (Err != ERROR_INSUFFICIENT_BUFFER)) {
  9210. Err = ERROR_INVALID_DATA;
  9211. goto clean0;
  9212. }
  9213. //
  9214. // Now that we know how big of a buffer we need to hold the driver info
  9215. // details, allocate the buffer and retrieve the information.
  9216. //
  9217. pDriverInfoDetailData = HeapAlloc(ghPnPHeap, 0, DriverInfoDetailDataSize);
  9218. if(!pDriverInfoDetailData) {
  9219. Err = ERROR_NOT_ENOUGH_MEMORY;
  9220. goto clean0;
  9221. }
  9222. pDriverInfoDetailData->cbSize = sizeof(SP_DRVINFO_DETAIL_DATA);
  9223. if(!fpGetDriverInfoDetail(DeviceInfoSet,
  9224. &DeviceInfoData,
  9225. &DriverInfoData,
  9226. pDriverInfoDetailData,
  9227. DriverInfoDetailDataSize,
  9228. NULL)) {
  9229. Err = GetLastError();
  9230. ASSERT(FALSE); // we should never fail this call.
  9231. goto clean0;
  9232. }
  9233. //
  9234. // OK, we have all the hardware and compatible IDs for this driver node.
  9235. // Now we need to open up the INF and see if any of them are referenced in
  9236. // an "InteractiveInstall" control flag entry.
  9237. //
  9238. hInf = fpOpenInfFile(pDriverInfoDetailData->InfFileName,
  9239. NULL,
  9240. INF_STYLE_WIN4,
  9241. NULL
  9242. );
  9243. if(hInf == INVALID_HANDLE_VALUE) {
  9244. //
  9245. // For some reason, we couldn't open the INF!
  9246. //
  9247. goto clean1;
  9248. }
  9249. b = FALSE;
  9250. //
  9251. // Look at each InteractiveInstall line in the INF's [ControlFlags]
  9252. // section...
  9253. //
  9254. if(fpFindFirstLine(hInf, pszControlFlags, pszInteractiveInstall, &InfContext)) {
  9255. do {
  9256. //
  9257. // and within each line, examine each value...
  9258. //
  9259. for(i = 1;
  9260. fpGetStringField(&InfContext, i, szBuffer, sizeof(szBuffer) / sizeof(WCHAR), NULL);
  9261. i++) {
  9262. //
  9263. // Check to see if this ID matches up with one of the driver
  9264. // node's hardware or compatible IDs.
  9265. //
  9266. for(p = pDriverInfoDetailData->HardwareID; *p; p += (lstrlen(p) + 1)) {
  9267. if (CompareString(
  9268. LOCALE_INVARIANT, NORM_IGNORECASE,
  9269. p, -1,
  9270. szBuffer, -1) == CSTR_EQUAL) {
  9271. //
  9272. // We found a match! We must defer the installation to
  9273. // the client-side.
  9274. //
  9275. b = TRUE;
  9276. goto InteractiveInstallSearchDone;
  9277. }
  9278. }
  9279. }
  9280. } while(fpFindNextMatchLine(&InfContext, pszInteractiveInstall, &InfContext));
  9281. }
  9282. InteractiveInstallSearchDone:
  9283. //
  9284. // We're done with the INF--close it.
  9285. //
  9286. fpCloseInfFile(hInf);
  9287. if(b) {
  9288. Err = ERROR_REQUIRES_INTERACTIVE_WINDOWSTATION;
  9289. goto clean0;
  9290. }
  9291. //
  9292. // Check to see if it's OK to install this driver.
  9293. //
  9294. if(!fpCallClassInstaller(DIF_ALLOW_INSTALL, DeviceInfoSet, &DeviceInfoData) &&
  9295. ((Err = GetLastError()) != ERROR_DI_DO_DEFAULT)) {
  9296. goto clean0;
  9297. }
  9298. //
  9299. // Tell our client-side UI (if any) it's time to update the device's
  9300. // description and class icon.
  9301. //
  9302. if (pDeviceInstallClient) {
  9303. //
  9304. // Retrieve the device description from the driver node we're about to
  9305. // install. We don't want to write this out as the devnode's DeviceDesc
  9306. // property, because some class installers have dependencies upon being
  9307. // able to retrieve the unaltered description as reported by the
  9308. // enumerator. So instead, we write this out as the REG_SZ
  9309. // NewDeviceDesc value entry to the devnode's hardware key.
  9310. //
  9311. DriverInfoData.cbSize = sizeof(SP_DRVINFO_DATA);
  9312. b = fpGetSelectedDriver(DeviceInfoSet, &DeviceInfoData, &DriverInfoData);
  9313. ASSERT(b); // the above call shouldn't fail
  9314. if(b) {
  9315. //
  9316. // Make sure that the hardware key is created (with the right
  9317. // security).
  9318. //
  9319. PNP_CreateKey(NULL,
  9320. pszDeviceId,
  9321. KEY_READ,
  9322. 0
  9323. );
  9324. //
  9325. // Now, open the Device Parameters subkey so we can write out the
  9326. // device's new description.
  9327. //
  9328. if (SUCCEEDED(StringCchPrintf(
  9329. szBuffer,
  9330. SIZECHARS(szBuffer),
  9331. L"%s\\%s",
  9332. pszDeviceId,
  9333. pszRegKeyDeviceParam))) {
  9334. if(ERROR_SUCCESS == RegOpenKeyEx(ghEnumKey,
  9335. szBuffer,
  9336. 0,
  9337. KEY_READ | KEY_WRITE,
  9338. &hKey)) {
  9339. if(ERROR_SUCCESS == RegSetValueEx(
  9340. hKey,
  9341. pszRegValueNewDeviceDesc,
  9342. 0,
  9343. REG_SZ,
  9344. (LPBYTE)(DriverInfoData.Description),
  9345. (lstrlen(DriverInfoData.Description) + 1) * sizeof(WCHAR))) {
  9346. RemoveNewDevDescValue = TRUE;
  9347. }
  9348. RegCloseKey(hKey);
  9349. hKey = NULL;
  9350. }
  9351. }
  9352. }
  9353. //
  9354. // Wait for the device install to be signaled from newdev.dll to let us
  9355. // know that it has completed displaying the UI request.
  9356. //
  9357. // Wait on the client's process as well, to catch the case
  9358. // where the process crashes (or goes away) without signaling the
  9359. // device install event.
  9360. //
  9361. // Also wait on the disconnect event in case we have explicitly
  9362. // disconnected from the client while switching sessions.
  9363. //
  9364. // We don't want to wait forever in case NEWDEV.DLL hangs for some
  9365. // reason. So we will give it 5 seconds to complete the UI only
  9366. // install and then continue on without it.
  9367. //
  9368. // Note that the client is still referenced for our use, and should be
  9369. // dereferenced when we're done with it.
  9370. //
  9371. hFinishEvents[0] = pDeviceInstallClient->hProcess;
  9372. hFinishEvents[1] = pDeviceInstallClient->hEvent;
  9373. hFinishEvents[2] = pDeviceInstallClient->hDisconnectEvent;
  9374. dwWait = WaitForMultipleObjects(3, hFinishEvents, FALSE, 5000);
  9375. if (dwWait == WAIT_OBJECT_0) {
  9376. //
  9377. // If the return is WAIT_OBJECT_0 then the newdev.dll process has
  9378. // gone away. Close the device install client and clean up all of
  9379. // the associated handles.
  9380. //
  9381. KdPrintEx((DPFLTR_PNPMGR_ID,
  9382. DBGF_INSTALL,
  9383. "UMPNPMGR: InstallDeviceServerSide: process signalled, closing device install client!\n"));
  9384. } else if (dwWait == (WAIT_OBJECT_0 + 1)) {
  9385. //
  9386. // If the return is WAIT_OBJECT_0 + 1 then the device installer
  9387. // successfully received the request. This is the only case where
  9388. // we don't want to close the client, since we may want to reuse it
  9389. // later.
  9390. //
  9391. KdPrintEx((DPFLTR_PNPMGR_ID,
  9392. DBGF_INSTALL,
  9393. "UMPNPMGR: InstallDeviceServerSide: device install client succeeded\n"));
  9394. } else if (dwWait == (WAIT_OBJECT_0 + 2)) {
  9395. //
  9396. // If the return is WAIT_OBJECT_0 + 2 then we were explicitly
  9397. // disconnected from the device install client. For server-side
  9398. // installation, we don't need to keep the client UI around on the
  9399. // disconnected session, so we should close it here also.
  9400. //
  9401. KdPrintEx((DPFLTR_PNPMGR_ID,
  9402. DBGF_INSTALL,
  9403. "UMPNPMGR: InstallDeviceServerSide: device install client disconnected\n"));
  9404. } else if (dwWait == WAIT_TIMEOUT) {
  9405. //
  9406. // Timed out while waiting for the device install client.
  9407. //
  9408. KdPrintEx((DPFLTR_PNPMGR_ID,
  9409. DBGF_INSTALL | DBGF_WARNINGS,
  9410. "UMPNPMGR: InstallDeviceServerSide: timed out waiting for device install client!\n"));
  9411. } else {
  9412. //
  9413. // The wait was satisfied for some reason other than the
  9414. // specified objects. This should never happen, but just in
  9415. // case, we'll close the client.
  9416. //
  9417. KdPrintEx((DPFLTR_PNPMGR_ID,
  9418. DBGF_INSTALL | DBGF_ERRORS,
  9419. "UMPNPMGR: InstallDeviceServerSide: wait completed unexpectedly!\n"));
  9420. }
  9421. LockNotifyList(&InstallClientList.Lock);
  9422. //
  9423. // Remove the reference placed on the client while it was in use.
  9424. //
  9425. DereferenceDeviceInstallClient(pDeviceInstallClient);
  9426. if (dwWait != (WAIT_OBJECT_0 + 1)) {
  9427. //
  9428. // Unless the client signalled successful receipt of the
  9429. // request, we probably won't be able to use this client
  9430. // anymore. Remove the initial reference so all
  9431. // associated handles will be closed and the entry will be
  9432. // freed when it is no longer in use.
  9433. //
  9434. //
  9435. // Note that if we were unsuccessful because of a
  9436. // logoff, we would have already dereferenced the
  9437. // client then, in which case the above dereference
  9438. // was the final one, and pDeviceInstallClient would
  9439. // be invalid. Instead, attempt to re-locate the
  9440. // client by the session id.
  9441. //
  9442. pDeviceInstallClient = LocateDeviceInstallClient(ulSessionId);
  9443. if (pDeviceInstallClient) {
  9444. ASSERT(pDeviceInstallClient->RefCount == 1);
  9445. DereferenceDeviceInstallClient(pDeviceInstallClient);
  9446. }
  9447. ulSessionId = INVALID_SESSION;
  9448. }
  9449. pDeviceInstallClient = NULL;
  9450. UnlockNotifyList(&InstallClientList.Lock);
  9451. }
  9452. //
  9453. // If we're doing client side UI for this device, attempt to refresh the UI again.
  9454. //
  9455. if (bDoClientUI) {
  9456. //
  9457. // When we attempt to refresh the client-side UI, if we display the
  9458. // refreshed UI on a different session than the one we had previously,
  9459. // close the previous device install client.
  9460. //
  9461. ULONG ulPrevSessionId = ulSessionId;
  9462. //
  9463. // If we're not going to determine the session to use for UI, use the
  9464. // SessionId supplied by the caller.
  9465. //
  9466. if ((Flags & DEVICE_INSTALL_DISPLAY_ON_CONSOLE) == 0) {
  9467. ASSERT(*SessionId != INVALID_SESSION);
  9468. ulSessionId = *SessionId;
  9469. }
  9470. DoDeviceInstallClient(pszDeviceId,
  9471. &ulSessionId,
  9472. Flags | DEVICE_INSTALL_UI_ONLY,
  9473. &pDeviceInstallClient);
  9474. if ((ulPrevSessionId != INVALID_SESSION) &&
  9475. (ulPrevSessionId != ulSessionId)) {
  9476. PINSTALL_CLIENT_ENTRY pPrevDeviceInstallClient;
  9477. LockNotifyList(&InstallClientList.Lock);
  9478. pPrevDeviceInstallClient = LocateDeviceInstallClient(ulPrevSessionId);
  9479. if (pPrevDeviceInstallClient) {
  9480. ASSERT(pPrevDeviceInstallClient->RefCount == 1);
  9481. DereferenceDeviceInstallClient(pPrevDeviceInstallClient);
  9482. }
  9483. UnlockNotifyList(&InstallClientList.Lock);
  9484. }
  9485. }
  9486. //
  9487. // OK, everything looks good for installing this driver. Check to see if
  9488. // this INF's class is already installed--if not, then we need to install
  9489. // it before proceeding.
  9490. //
  9491. if(RPC_S_OK != UuidToString(&(DeviceInfoData.ClassGuid), &pszClassGuid)) {
  9492. Err = ERROR_NOT_ENOUGH_MEMORY;
  9493. goto clean0;
  9494. }
  9495. hr = StringCchPrintf(szBuffer,
  9496. SIZECHARS(szBuffer),
  9497. L"{%s}",
  9498. pszClassGuid);
  9499. RpcStringFree(&pszClassGuid);
  9500. if (FAILED(hr)) {
  9501. Err = HRESULT_CODE(hr);
  9502. goto clean0;
  9503. }
  9504. if(RegOpenKeyEx(ghClassKey,
  9505. szBuffer,
  9506. 0,
  9507. KEY_READ,
  9508. &hKey) != ERROR_SUCCESS) {
  9509. if(!fpInstallClass(NULL,
  9510. pDriverInfoDetailData->InfFileName,
  9511. 0,
  9512. NULL)) {
  9513. goto clean1;
  9514. }
  9515. } else {
  9516. //
  9517. // The class key already exists--assume that the class has previously
  9518. // been installed.
  9519. //
  9520. RegCloseKey(hKey);
  9521. }
  9522. //
  9523. // Now we're ready to install the device. First, install the files.
  9524. //
  9525. if(!fpCallClassInstaller(DIF_INSTALLDEVICEFILES, DeviceInfoSet, &DeviceInfoData)) {
  9526. goto clean1;
  9527. }
  9528. //
  9529. // Set a flag in the device install parameters so that we don't try to
  9530. // re-copy the files during subsequent DIF operations.
  9531. //
  9532. DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
  9533. b = fpGetDeviceInstallParams(DeviceInfoSet, &DeviceInfoData, &DeviceInstallParams);
  9534. ASSERT(b); // the above call shouldn't fail
  9535. if(!b) {
  9536. goto clean1;
  9537. }
  9538. DeviceInstallParams.Flags |= DI_NOFILECOPY;
  9539. b = fpSetDeviceInstallParams(DeviceInfoSet, &DeviceInfoData, &DeviceInstallParams);
  9540. ASSERT(b); // the above call shouldn't fail
  9541. if(!b) {
  9542. goto clean1;
  9543. }
  9544. //
  9545. // Now finish up the installation.
  9546. //
  9547. if(!fpCallClassInstaller(DIF_REGISTER_COINSTALLERS, DeviceInfoSet, &DeviceInfoData)) {
  9548. goto clean1;
  9549. }
  9550. if(!fpCallClassInstaller(DIF_INSTALLINTERFACES, DeviceInfoSet, &DeviceInfoData)) {
  9551. goto clean1;
  9552. }
  9553. if(!fpCallClassInstaller(DIF_INSTALLDEVICE, DeviceInfoSet, &DeviceInfoData)) {
  9554. ULONG ulConfig;
  9555. //
  9556. // Before we do anything to blow away last error, retrieve it.
  9557. //
  9558. Err = GetLastError();
  9559. //
  9560. // It's possible that the installation got far enough to have cleared
  9561. // any problems on the device (i.e., SetupDiInstallDevice succeeded,
  9562. // but the class installer or co-installer subsequently failed during
  9563. // some post-processing).
  9564. //
  9565. // We want to make sure that the devnode is marked as needing re-install
  9566. // because we might lose the client-side install request (e.g., the
  9567. // user reboots without logging in).
  9568. //
  9569. ulConfig = GetDeviceConfigFlags(pszDeviceId, NULL);
  9570. ulConfig |= CONFIGFLAG_REINSTALL;
  9571. PNP_SetDeviceRegProp(NULL,
  9572. pszDeviceId,
  9573. CM_DRP_CONFIGFLAGS,
  9574. REG_DWORD,
  9575. (LPBYTE)&ulConfig,
  9576. sizeof(ulConfig),
  9577. 0
  9578. );
  9579. goto clean0;
  9580. }
  9581. //
  9582. // We're not quite out of the woods yet. We need to check if the class-/
  9583. // co-installers want to display finish-install wizard pages. If so, then
  9584. // we need to set the CONFIGFLAG_REINSTALL flag for this devnode and report
  9585. // failure so that we'll re-attempt the install as a client-side
  9586. // installation (where a wizard can actually be displayed).
  9587. //
  9588. ZeroMemory(&NewDevWizData, sizeof(NewDevWizData));
  9589. NewDevWizData.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
  9590. NewDevWizData.ClassInstallHeader.InstallFunction = DIF_NEWDEVICEWIZARD_FINISHINSTALL;
  9591. b = fpSetClassInstallParams(DeviceInfoSet,
  9592. &DeviceInfoData,
  9593. (PSP_CLASSINSTALL_HEADER)&NewDevWizData,
  9594. sizeof(NewDevWizData)
  9595. );
  9596. ASSERT(b); // the above call shouldn't fail
  9597. if(b) {
  9598. b = fpCallClassInstaller(DIF_NEWDEVICEWIZARD_FINISHINSTALL,
  9599. DeviceInfoSet,
  9600. &DeviceInfoData
  9601. );
  9602. if(b || (ERROR_DI_DO_DEFAULT == GetLastError())) {
  9603. //
  9604. // Retrieve the install params
  9605. //
  9606. b = (fpGetClassInstallParams(DeviceInfoSet,
  9607. &DeviceInfoData,
  9608. (PSP_CLASSINSTALL_HEADER)&NewDevWizData,
  9609. sizeof(NewDevWizData),
  9610. NULL)
  9611. && (NewDevWizData.ClassInstallHeader.InstallFunction == DIF_NEWDEVICEWIZARD_FINISHINSTALL)
  9612. );
  9613. if(b) {
  9614. //
  9615. // Are there any pages?
  9616. //
  9617. if(!NewDevWizData.NumDynamicPages) {
  9618. b = FALSE;
  9619. } else {
  9620. //
  9621. // b is already TRUE if we made it here so no need to set
  9622. //
  9623. HMODULE hComCtl32;
  9624. FP_DESTROYPROPERTYSHEETPAGE fpDestroyPropertySheetPage;
  9625. //
  9626. // We don't want to link to comctl32, nor do we want to
  9627. // always explicitly load it every time we load the device
  9628. // installer. (The number of devices that request finish-
  9629. // install pages should be small.) Thus, we load it on-
  9630. // demand right here, retrieve the entrypoint to the
  9631. // DestroyPropertySheetPage routine, and then unload the
  9632. // DLL once we've destroyed all the property pages.
  9633. //
  9634. // NOTE: (lonnym): If we can't load comctl32 or get the
  9635. // entrypont for DestroyPropertySheetPage, then we'll leak
  9636. // these wizard pages!
  9637. //
  9638. hComCtl32 = LoadLibrary(TEXT("comctl32.dll"));
  9639. if(hComCtl32) {
  9640. fpDestroyPropertySheetPage = (FP_DESTROYPROPERTYSHEETPAGE)GetProcAddress(
  9641. hComCtl32,
  9642. "DestroyPropertySheetPage"
  9643. );
  9644. if(fpDestroyPropertySheetPage) {
  9645. for(i = 0; i < NewDevWizData.NumDynamicPages; i++) {
  9646. fpDestroyPropertySheetPage(NewDevWizData.DynamicPages[i]);
  9647. }
  9648. }
  9649. FreeLibrary(hComCtl32);
  9650. }
  9651. }
  9652. }
  9653. }
  9654. }
  9655. if(b) {
  9656. ULONG ulConfig;
  9657. CONFIGRET cr;
  9658. //
  9659. // One or more finish-install wizard pages were provided--we must defer
  9660. // this installation to the client-side.
  9661. //
  9662. ulConfig = GetDeviceConfigFlags(pszDeviceId, NULL);
  9663. ulConfig |= CONFIGFLAG_REINSTALL;
  9664. cr = PNP_SetDeviceRegProp(NULL,
  9665. pszDeviceId,
  9666. CM_DRP_CONFIGFLAGS,
  9667. REG_DWORD,
  9668. (LPBYTE)&ulConfig,
  9669. sizeof(ulConfig),
  9670. 0
  9671. );
  9672. ASSERT(cr == CR_SUCCESS);
  9673. Err = ERROR_REQUIRES_INTERACTIVE_WINDOWSTATION;
  9674. goto clean0;
  9675. }
  9676. //
  9677. // The installation was a success! Check to see if a reboot is needed.
  9678. //
  9679. b = fpGetDeviceInstallParams(DeviceInfoSet, &DeviceInfoData, &DeviceInstallParams);
  9680. ASSERT(b); // the above call shouldn't fail
  9681. if(b) {
  9682. if(DeviceInstallParams.Flags & (DI_NEEDRESTART | DI_NEEDREBOOT)) {
  9683. *RebootRequired = TRUE;
  9684. }
  9685. }
  9686. //
  9687. // Process any RunOnce (RunDll32) entries that may have been queued up
  9688. // during this installation.
  9689. //
  9690. DoRunOnce();
  9691. //
  9692. // Check to see if the device has a problem.
  9693. //
  9694. if ((GetDeviceStatus(pszDeviceId, &ulStatus, &ulProblem) != CR_SUCCESS) ||
  9695. (ulStatus & DN_HAS_PROBLEM)) {
  9696. *DeviceHasProblem = TRUE;
  9697. } else {
  9698. *DeviceHasProblem = FALSE;
  9699. }
  9700. Err = NO_ERROR;
  9701. goto clean0;
  9702. clean1:
  9703. //
  9704. // Failures where error is in GetLastError() can come here.
  9705. //
  9706. Err = GetLastError();
  9707. clean0:
  9708. fpDestroyDeviceInfoList(DeviceInfoSet);
  9709. if(pDriverInfoDetailData) {
  9710. HeapFree(ghPnPHeap, 0, pDriverInfoDetailData);
  9711. }
  9712. //
  9713. // Clear out our list of RunOnce work items (note that the list will
  9714. // already be empty if the device install succeeded and we called
  9715. // DoRunOnce() above).
  9716. //
  9717. fpDestroyRunOnceNodeList();
  9718. //
  9719. // If we stored out a NewDeviceDesc value to the devnode's hardware key
  9720. // above, go and remove that turd now.
  9721. //
  9722. if(RemoveNewDevDescValue) {
  9723. //
  9724. // Open the Device Parameters subkey so we can delete the value.
  9725. //
  9726. if (SUCCEEDED(StringCchPrintf(
  9727. szBuffer,
  9728. SIZECHARS(szBuffer),
  9729. L"%s\\%s",
  9730. pszDeviceId,
  9731. pszRegKeyDeviceParam))) {
  9732. if (RegOpenKeyEx(ghEnumKey,
  9733. szBuffer,
  9734. 0,
  9735. KEY_READ | KEY_WRITE,
  9736. &hKey) == ERROR_SUCCESS) {
  9737. RegDeleteValue(hKey, pszRegValueNewDeviceDesc);
  9738. RegCloseKey(hKey);
  9739. }
  9740. }
  9741. }
  9742. if (pDeviceInstallClient) {
  9743. //
  9744. // Wait for the device install to be signaled from newdev.dll to let us
  9745. // know that it has completed displaying the UI request.
  9746. //
  9747. // Wait on the client's process as well, to catch the case
  9748. // where the process crashes (or goes away) without signaling the
  9749. // device install event.
  9750. //
  9751. // Also wait on the disconnect event in case we have explicitly
  9752. // disconnected from the client while switching sessions.
  9753. //
  9754. // We don't want to wait forever in case NEWDEV.DLL hangs for some
  9755. // reason. So we will give it 5 seconds to complete the UI only
  9756. // install and then continue on without it.
  9757. //
  9758. // Note that the client is still referenced for our use, and should be
  9759. // dereferenced when we're done with it.
  9760. //
  9761. hFinishEvents[0] = pDeviceInstallClient->hProcess;
  9762. hFinishEvents[1] = pDeviceInstallClient->hEvent;
  9763. hFinishEvents[2] = pDeviceInstallClient->hDisconnectEvent;
  9764. dwWait = WaitForMultipleObjects(3, hFinishEvents, FALSE, 5000);
  9765. if (dwWait == WAIT_OBJECT_0) {
  9766. //
  9767. // If the return is WAIT_OBJECT_0 then the newdev.dll process has
  9768. // gone away. Close the device install client and clean up all of
  9769. // the associated handles.
  9770. //
  9771. KdPrintEx((DPFLTR_PNPMGR_ID,
  9772. DBGF_INSTALL,
  9773. "UMPNPMGR: InstallDeviceServerSide: "
  9774. "process signalled, closing device install client!\n"));
  9775. } else if (dwWait == (WAIT_OBJECT_0 + 1)) {
  9776. //
  9777. // If the return is WAIT_OBJECT_0 + 1 then the device installer
  9778. // successfully received the request. This is the only case where
  9779. // we don't want to close the client, since we may want to reuse it
  9780. // later.
  9781. //
  9782. KdPrintEx((DPFLTR_PNPMGR_ID,
  9783. DBGF_INSTALL,
  9784. "UMPNPMGR: InstallDeviceServerSide: "
  9785. "device install client succeeded\n"));
  9786. } else if (dwWait == (WAIT_OBJECT_0 + 2)) {
  9787. //
  9788. // If the return is WAIT_OBJECT_0 + 2 then we were explicitly
  9789. // disconnected from the device install client. For server-side
  9790. // installation, we don't need to keep the client UI around on the
  9791. // disconnected session, so we should close it here also.
  9792. //
  9793. KdPrintEx((DPFLTR_PNPMGR_ID,
  9794. DBGF_INSTALL,
  9795. "UMPNPMGR: InstallDeviceServerSide: "
  9796. "device install client disconnected\n"));
  9797. } else if (dwWait == WAIT_TIMEOUT) {
  9798. //
  9799. // Timed out while waiting for the device install client.
  9800. //
  9801. KdPrintEx((DPFLTR_PNPMGR_ID,
  9802. DBGF_INSTALL | DBGF_WARNINGS,
  9803. "UMPNPMGR: InstallDeviceServerSide: "
  9804. "timed out waiting for device install client!\n"));
  9805. } else {
  9806. //
  9807. // The wait was satisfied for some reason other than the
  9808. // specified objects. This should never happen, but just in
  9809. // case, we'll close the client.
  9810. //
  9811. KdPrintEx((DPFLTR_PNPMGR_ID,
  9812. DBGF_INSTALL | DBGF_ERRORS,
  9813. "UMPNPMGR: InstallDeviceServerSide: "
  9814. "wait completed unexpectedly!\n"));
  9815. }
  9816. LockNotifyList(&InstallClientList.Lock);
  9817. //
  9818. // Remove the reference placed on the client while it was in use.
  9819. //
  9820. DereferenceDeviceInstallClient(pDeviceInstallClient);
  9821. if (dwWait != (WAIT_OBJECT_0 + 1)) {
  9822. //
  9823. // Unless the client signalled successful receipt of the
  9824. // request, we probably won't be able to use this client
  9825. // anymore. Remove the initial reference so all
  9826. // associated handles will be closed and the entry will be
  9827. // freed when it is no longer in use.
  9828. //
  9829. //
  9830. // Note that if we were unsuccessful because of a
  9831. // logoff, we would have already dereferenced the
  9832. // client then, in which case the above dereference
  9833. // was the final one, and pDeviceInstallClient would
  9834. // be invalid. Instead, attempt to re-locate the
  9835. // client by the session id.
  9836. //
  9837. pDeviceInstallClient = LocateDeviceInstallClient(ulSessionId);
  9838. if (pDeviceInstallClient) {
  9839. ASSERT(pDeviceInstallClient->RefCount == 1);
  9840. DereferenceDeviceInstallClient(pDeviceInstallClient);
  9841. }
  9842. ulSessionId = INVALID_SESSION;
  9843. }
  9844. pDeviceInstallClient = NULL;
  9845. UnlockNotifyList(&InstallClientList.Lock);
  9846. }
  9847. if (bDoClientUI) {
  9848. //
  9849. // Note that if client-side UI was created during the server-side device
  9850. // install, it will still exist when we are done. The caller should
  9851. // dereference it when it is done installing all devices to make it go
  9852. // away.
  9853. //
  9854. *SessionId = ulSessionId;
  9855. } else {
  9856. //
  9857. // There was never any client-side UI for this device install.
  9858. //
  9859. *SessionId = INVALID_SESSION;
  9860. }
  9861. return Err;
  9862. } // InstallDeviceServerSide
  9863. BOOL
  9864. PromptUser(
  9865. IN OUT PULONG SessionId,
  9866. IN ULONG Flags
  9867. )
  9868. /*++
  9869. Routine Description:
  9870. This routine will notify the logged-on user (if any) with a specified
  9871. message.
  9872. Arguments:
  9873. SessionId - Supplies the address of a variable containing the SessionId on
  9874. which the device install client is to be displayed. If successful, the
  9875. SessionId will contain the id of the session in which the reboot dialog
  9876. process was launched. Otherwise, will contain an invalid session id,
  9877. INVALID_SESSION, (0xFFFFFFFF).
  9878. Flags - Specifies flags describing the behavior of the reboot dialog
  9879. displayed by the device install client.
  9880. The following flags are currently defined:
  9881. DEVICE_INSTALL_FINISHED_REBOOT - if specified, the user should be
  9882. prompted to reboot.
  9883. DEVICE_INSTALL_BATCH_COMPLETE - if specified, the user should be
  9884. prompted that the plug and play manager is finished installing a
  9885. batch of devices.
  9886. DEVICE_INSTALL_DISPLAY_ON_CONSOLE - if specified, the value in the
  9887. SessionId variable will be ignored, and the device installclient will
  9888. always be displayed on the current active console session.
  9889. Return Value:
  9890. If the user is successfully notified, the return value is TRUE.
  9891. If we couldn't ask the user (i.e., no user was logged in), the return
  9892. value is FALSE.
  9893. Notes:
  9894. If the user was prompted for a reboot, this doesn't necessarily mean that a
  9895. reboot is in progress.
  9896. --*/
  9897. {
  9898. BOOL bStatus = FALSE;
  9899. ULONG ulValue, ulSize, ulSessionId = INVALID_SESSION;
  9900. HANDLE hFinishEvents[3] = { NULL, NULL, NULL };
  9901. DWORD dwWait;
  9902. PINSTALL_CLIENT_ENTRY pDeviceInstallClient = NULL;
  9903. try {
  9904. //
  9905. // Check if we should skip client side UI.
  9906. //
  9907. if (gbSuppressUI) {
  9908. KdPrintEx((DPFLTR_PNPMGR_ID,
  9909. DBGF_INSTALL | DBGF_WARNINGS,
  9910. "UMPNPMGR: PromptUser: Client-side UI has been suppressed, exiting.\n"));
  9911. LogWarningEvent(WRN_REBOOT_UI_SUPPRESSED, 0, NULL);
  9912. *SessionId = INVALID_SESSION;
  9913. return FALSE;
  9914. }
  9915. //
  9916. // Determine the session to use, based on the supplied flags.
  9917. //
  9918. if (Flags & DEVICE_INSTALL_DISPLAY_ON_CONSOLE) {
  9919. ulSessionId = GetActiveConsoleSessionId();
  9920. } else {
  9921. ASSERT(*SessionId != INVALID_SESSION);
  9922. ulSessionId = *SessionId;
  9923. }
  9924. ASSERT(ulSessionId != INVALID_SESSION);
  9925. //
  9926. // If the specified session is not currently connected anywhere, don't
  9927. // bother creating any UI.
  9928. //
  9929. if (!IsSessionConnected(ulSessionId)) {
  9930. KdPrintEx((DPFLTR_PNPMGR_ID,
  9931. DBGF_EVENT,
  9932. "UMPNPMGR: PromptUser: SessionId %d not connected, exiting\n",
  9933. ulSessionId));
  9934. return FALSE;
  9935. }
  9936. //
  9937. // If a device install client is already running on this session,
  9938. // connect to it. Otherwise, create a new one.
  9939. //
  9940. LockNotifyList(&InstallClientList.Lock);
  9941. //
  9942. // First, try to connect to an existing client already running on this
  9943. // session.
  9944. //
  9945. bStatus = ConnectDeviceInstallClient(ulSessionId,
  9946. &pDeviceInstallClient);
  9947. if (bStatus) {
  9948. if ((Flags & DEVICE_INSTALL_BATCH_COMPLETE) &&
  9949. (pDeviceInstallClient->ulInstallFlags & DEVICE_INSTALL_BATCH_COMPLETE)) {
  9950. //
  9951. // If there is an existing client, and we're sending it the
  9952. // "we're done" message, and the last thing this client did was
  9953. // display that message, don't bother sending it again.
  9954. //
  9955. pDeviceInstallClient = NULL;
  9956. bStatus = FALSE;
  9957. }
  9958. } else if (!(Flags & DEVICE_INSTALL_BATCH_COMPLETE)) {
  9959. //
  9960. // If there isn't an existing client for this session, and we're not
  9961. // launching one just to say "we're done", then go ahead and create
  9962. // a new device install client for this session.
  9963. //
  9964. bStatus = CreateDeviceInstallClient(ulSessionId,
  9965. &pDeviceInstallClient);
  9966. }
  9967. if (bStatus) {
  9968. //
  9969. // Whether we are using an existing client, or created a
  9970. // new one, the client should only have the initial
  9971. // reference from when it was added to the list, since any
  9972. // use of the client is done on this single install
  9973. // thread.
  9974. //
  9975. ASSERT(pDeviceInstallClient);
  9976. ASSERT(pDeviceInstallClient->RefCount == 1);
  9977. //
  9978. // Reference the device install client while it is in use.
  9979. // We'll remove this reference when we're done with it.
  9980. //
  9981. ReferenceDeviceInstallClient(pDeviceInstallClient);
  9982. }
  9983. UnlockNotifyList(&InstallClientList.Lock);
  9984. if (!bStatus) {
  9985. *SessionId = INVALID_SESSION;
  9986. return FALSE;
  9987. }
  9988. ASSERT(pDeviceInstallClient);
  9989. //
  9990. // Don't send newdev the display on console flag, if it was specified.
  9991. //
  9992. ulValue = Flags & ~DEVICE_INSTALL_DISPLAY_ON_CONSOLE;
  9993. //
  9994. // Send newdev.dll the specified signal.
  9995. //
  9996. if (WriteFile(pDeviceInstallClient->hPipe,
  9997. &ulValue,
  9998. sizeof(ulValue),
  9999. &ulSize,
  10000. NULL
  10001. )) {
  10002. //
  10003. // newdev.dll expects two DWORDs to be sent over the pipe each time. The second
  10004. // DWORD should just be set to 0 in this case.
  10005. //
  10006. ulValue = 0;
  10007. if (WriteFile(pDeviceInstallClient->hPipe,
  10008. &ulValue,
  10009. sizeof(ulValue),
  10010. &ulSize,
  10011. NULL
  10012. )) {
  10013. bStatus = TRUE;
  10014. } else {
  10015. bStatus = FALSE;
  10016. LogErrorEvent(ERR_WRITING_SERVER_INSTALL_PIPE, GetLastError(), 0);
  10017. }
  10018. } else {
  10019. bStatus = FALSE;
  10020. LogErrorEvent(ERR_WRITING_SERVER_INSTALL_PIPE, GetLastError(), 0);
  10021. }
  10022. if (bStatus) {
  10023. bStatus = FALSE;
  10024. //
  10025. // Wait for the event to be signaled from newdev.dll
  10026. // to let us know that it has received the information.
  10027. //
  10028. // Wait on the process as well, to catch the case where the process
  10029. // crashes (or goes away) without signaling the event.
  10030. //
  10031. // Also wait on the disconnect event in case we have just
  10032. // disconnected from the device install client, in which case the
  10033. // event and process handles are no longer valid.
  10034. //
  10035. hFinishEvents[0] = pDeviceInstallClient->hProcess;
  10036. hFinishEvents[1] = pDeviceInstallClient->hEvent;
  10037. hFinishEvents[2] = pDeviceInstallClient->hDisconnectEvent;
  10038. dwWait = WaitForMultipleObjects(3, hFinishEvents, FALSE, INFINITE);
  10039. if (dwWait == WAIT_OBJECT_0) {
  10040. //
  10041. // If the return is WAIT_OBJECT_0 then the newdev.dll
  10042. // process has gone away. Consider the request unsuccessful
  10043. // so that we will retry again at a later time. Orphan the
  10044. // device install client and clean up all of the associated
  10045. // handles.
  10046. //
  10047. KdPrintEx((DPFLTR_PNPMGR_ID,
  10048. DBGF_EVENT,
  10049. "UMPNPMGR: PromptUser: process signalled, orphaning device install client!\n"));
  10050. } else if (dwWait == (WAIT_OBJECT_0 + 1)) {
  10051. //
  10052. // If the return is WAIT_OBJECT_0 + 1 then the request was
  10053. // received successfully.
  10054. //
  10055. KdPrintEx((DPFLTR_PNPMGR_ID,
  10056. DBGF_EVENT,
  10057. "UMPNPMGR: PromptUser: device install client succeeded\n"));
  10058. //
  10059. // Remember the last request serviced by this client.
  10060. //
  10061. pDeviceInstallClient->ulInstallFlags = Flags;
  10062. bStatus = TRUE;
  10063. } else if (dwWait == (WAIT_OBJECT_0 + 2)) {
  10064. //
  10065. // If the return is WAIT_OBJECT_0 + 2 then the device
  10066. // install client was explicitly disconnected before
  10067. // the request was received. Consider the request
  10068. // unsuccessful so that we will retry again at a later
  10069. // time.
  10070. //
  10071. KdPrintEx((DPFLTR_PNPMGR_ID,
  10072. DBGF_EVENT,
  10073. "UMPNPMGR: PromptUser: device install client orphaned!\n"));
  10074. }
  10075. }
  10076. LockNotifyList(&InstallClientList.Lock);
  10077. //
  10078. // Remove the reference placed on the client while it was in use.
  10079. //
  10080. DereferenceDeviceInstallClient(pDeviceInstallClient);
  10081. if (!bStatus) {
  10082. //
  10083. // Unless the client signalled successful receipt of the
  10084. // request, we probably won't be able to use this client
  10085. // anymore. Remove the initial reference so all
  10086. // associated handles will be closed and the entry will be
  10087. // freed when it is no longer in use.
  10088. //
  10089. //
  10090. // Note that if we were unsuccessful because of a
  10091. // logoff, we would have already dereferenced the
  10092. // client then, in which case the above dereference
  10093. // was the final one, and pDeviceInstallClient would
  10094. // be invalid. Instead, attempt to re-locate the
  10095. // client by the session id.
  10096. //
  10097. pDeviceInstallClient = LocateDeviceInstallClient(ulSessionId);
  10098. if (pDeviceInstallClient) {
  10099. ASSERT(pDeviceInstallClient->RefCount == 1);
  10100. DereferenceDeviceInstallClient(pDeviceInstallClient);
  10101. }
  10102. }
  10103. UnlockNotifyList(&InstallClientList.Lock);
  10104. } except (EXCEPTION_EXECUTE_HANDLER) {
  10105. KdPrintEx((DPFLTR_PNPMGR_ID,
  10106. DBGF_ERRORS | DBGF_WARNINGS,
  10107. "UMPNPMGR: Exception during PromptUser!\n"));
  10108. ASSERT(0);
  10109. bStatus = FALSE;
  10110. }
  10111. if (!bStatus) {
  10112. *SessionId = INVALID_SESSION;
  10113. } else {
  10114. *SessionId = ulSessionId;
  10115. }
  10116. return bStatus;
  10117. } // PromptUser
  10118. BOOL
  10119. CreateDeviceInstallClient(
  10120. IN ULONG SessionId,
  10121. OUT PINSTALL_CLIENT_ENTRY *DeviceInstallClient
  10122. )
  10123. /*++
  10124. Routine Description:
  10125. This routine kicks off a newdev.dll process (if someone is logged in).
  10126. We use a named pipe to comunicate with the user mode process
  10127. and have it either display UI for a server side install, or do the install
  10128. itself on the client side.
  10129. Arguments:
  10130. SessionId - Session for which a device install client should be
  10131. created or connected to.
  10132. DeviceInstallClient - Receives a pointer to receive a pointer to the
  10133. device install client for this session.
  10134. Return Value:
  10135. Returns TRUE if a device install client was created, or if an existing
  10136. device install client was found for the specified session. This routine
  10137. doesn't wait until the process terminates. Returns FALSE if a device
  10138. install client could not be created.
  10139. Notes:
  10140. The InstallClientList lock must be acquired by the caller of this routine.
  10141. --*/
  10142. {
  10143. STARTUPINFO StartupInfo;
  10144. PROCESS_INFORMATION ProcessInfo;
  10145. WCHAR szCmdLine[MAX_PATH];
  10146. WCHAR szDeviceInstallPipeName[MAX_PATH];
  10147. WCHAR szDeviceInstallEventName[MAX_PATH];
  10148. ULONG ulDeviceInstallEventNameSize;
  10149. HANDLE hFinishEvents[2] = { NULL, NULL };
  10150. HANDLE hTemp, hUserToken = NULL;
  10151. PINSTALL_CLIENT_ENTRY entry;
  10152. RPC_STATUS rpcStatus = RPC_S_OK;
  10153. GUID newGuid;
  10154. WCHAR szGuidString[MAX_GUID_STRING_LEN];
  10155. HANDLE hDeviceInstallPipe = NULL, hDeviceInstallEvent = NULL;
  10156. HANDLE hDeviceInstallDisconnectEvent = NULL;
  10157. PINSTALL_CLIENT_ENTRY pDeviceInstallClient = NULL;
  10158. ULONG ulSize;
  10159. WIN32_FIND_DATA findData;
  10160. BOOL bStatus;
  10161. PVOID lpEnvironment = NULL;
  10162. OVERLAPPED overlapped = {0,0,0,0,0};
  10163. DWORD dwError, dwWait, dwBytes;
  10164. HRESULT hr;
  10165. size_t Len = 0;
  10166. //
  10167. // Validate output parameter.
  10168. //
  10169. ASSERT(DeviceInstallClient);
  10170. if (!DeviceInstallClient) {
  10171. return FALSE;
  10172. }
  10173. //
  10174. // Make sure the specified SessionId is valid.
  10175. //
  10176. ASSERT(SessionId != INVALID_SESSION);
  10177. if (SessionId == INVALID_SESSION) {
  10178. KdPrintEx((DPFLTR_PNPMGR_ID,
  10179. DBGF_INSTALL | DBGF_ERRORS,
  10180. "UMPNPMGR: CreateDeviceInstallClient: Invalid Console SessionId %d, exiting!\n",
  10181. SessionId));
  10182. return FALSE;
  10183. }
  10184. //
  10185. // Initialize process, startup and overlapped structures, since we
  10186. // depend on them being NULL during cleanup here on out.
  10187. //
  10188. ZeroMemory(&ProcessInfo, sizeof(ProcessInfo));
  10189. ZeroMemory(&StartupInfo, sizeof(StartupInfo));
  10190. ZeroMemory(&overlapped, sizeof(overlapped));
  10191. //
  10192. // Assume failure
  10193. //
  10194. bStatus = FALSE;
  10195. try {
  10196. //
  10197. // Before doing anything, check that newdev.dll is actually present on
  10198. // the system.
  10199. //
  10200. szCmdLine[0] = L'\0';
  10201. ulSize = GetSystemDirectory(szCmdLine, MAX_PATH);
  10202. if ((ulSize == 0) || ((ulSize + 2 + ARRAY_SIZE(NEWDEV_DLL)) > MAX_PATH)) {
  10203. return FALSE;
  10204. }
  10205. hr = StringCchCat(szCmdLine,
  10206. SIZECHARS(szCmdLine),
  10207. L"\\");
  10208. if (SUCCEEDED(hr)) {
  10209. hr = StringCchCat(szCmdLine,
  10210. SIZECHARS(szCmdLine),
  10211. NEWDEV_DLL);
  10212. }
  10213. if (FAILED(hr)) {
  10214. return FALSE;
  10215. }
  10216. hTemp = FindFirstFile(szCmdLine, &findData);
  10217. if(hTemp != INVALID_HANDLE_VALUE) {
  10218. FindClose(hTemp);
  10219. } else {
  10220. KdPrintEx((DPFLTR_PNPMGR_ID,
  10221. DBGF_INSTALL | DBGF_ERRORS,
  10222. "UMPNPMGR: CreateDeviceInstallClient: %ws not found, error = %d, exiting\n",
  10223. szCmdLine,
  10224. GetLastError()));
  10225. LogWarningEvent(WRN_NEWDEV_NOT_PRESENT, 1, szCmdLine);
  10226. return FALSE;
  10227. }
  10228. //
  10229. // Get the user access token for the active console session user.
  10230. //
  10231. if (!GetSessionUserToken(SessionId, &hUserToken) || (hUserToken == NULL)) {
  10232. KdPrintEx((DPFLTR_PNPMGR_ID,
  10233. DBGF_INSTALL,
  10234. "UMPNPMGR: CreateDeviceInstallClient: Unable to get user token for Session %d,\n"
  10235. " postponing client-side installation, error = %d\n",
  10236. SessionId,
  10237. GetLastError()));
  10238. return FALSE;
  10239. }
  10240. //
  10241. // If the user Winstation for this session is locked, and Fast User
  10242. // Switching is enabled, then we're at the welcome screen. Don't create
  10243. // a device install client, because we don't want to hang the install
  10244. // thread if nobody's actually around to do anything about it. If the
  10245. // session is locked, but FUS is not disabled, maintain previous
  10246. // behavior, and launch the device install client. The user will have
  10247. // to unlock or logoff before another user can logon anyways.
  10248. //
  10249. if (IsSessionLocked(SessionId) && IsFastUserSwitchingEnabled()) {
  10250. KdPrintEx((DPFLTR_PNPMGR_ID,
  10251. DBGF_INSTALL,
  10252. "UMPNPMGR: CreateDeviceInstallClient: Session %d locked with FUS enabled,\n"
  10253. " postponing client-side installation.\n",
  10254. SessionId));
  10255. CloseHandle(hUserToken);
  10256. return FALSE;
  10257. }
  10258. //
  10259. // Create a named pipe and event for communication and synchronization
  10260. // with the client-side device installer. The event and named pipe must
  10261. // be global so that UMPNPMGR can interact with a device install client
  10262. // in a different session, but it must still be unique for that session.
  10263. // Add a generated GUID so the names are not entirely well-known.
  10264. //
  10265. rpcStatus = UuidCreate(&newGuid);
  10266. if ((rpcStatus != RPC_S_OK) &&
  10267. (rpcStatus != RPC_S_UUID_LOCAL_ONLY)) {
  10268. goto Clean0;
  10269. }
  10270. if (StringFromGuid((LPGUID)&newGuid,
  10271. szGuidString,
  10272. MAX_GUID_STRING_LEN) != NO_ERROR) {
  10273. goto Clean0;
  10274. }
  10275. if (FAILED(StringCchPrintf(
  10276. szDeviceInstallPipeName,
  10277. SIZECHARS(szDeviceInstallPipeName),
  10278. L"%ws_%d.%ws",
  10279. PNP_DEVICE_INSTALL_PIPE,
  10280. SessionId,
  10281. szGuidString))) {
  10282. goto Clean0;
  10283. }
  10284. if (FAILED(StringCchPrintf(
  10285. szDeviceInstallEventName,
  10286. SIZECHARS(szDeviceInstallEventName),
  10287. L"Global\\%ws_%d.%ws",
  10288. PNP_DEVICE_INSTALL_EVENT,
  10289. SessionId,
  10290. szGuidString))) {
  10291. goto Clean0;
  10292. }
  10293. if (FAILED(StringCchLength(
  10294. szDeviceInstallEventName,
  10295. SIZECHARS(szDeviceInstallEventName),
  10296. &Len))) {
  10297. goto Clean0;
  10298. }
  10299. ulDeviceInstallEventNameSize = (ULONG)((Len + 1) * sizeof(WCHAR));
  10300. //
  10301. // The approximate size of the named pipe output buffer should be large
  10302. // enough to hold the greater of either:
  10303. // - The name and size of the named event string, OR
  10304. // - The install flags, name and device instance id size for at least
  10305. // one device install.
  10306. //
  10307. ulSize = max(sizeof(ulDeviceInstallEventNameSize) +
  10308. ulDeviceInstallEventNameSize,
  10309. 2 * sizeof(ULONG) +
  10310. (MAX_DEVICE_ID_LEN * sizeof(WCHAR)));
  10311. //
  10312. // Open up a named pipe to communicate with the newdev user-client.
  10313. //
  10314. if (CreateUserReadNamedPipe(
  10315. hUserToken,
  10316. szDeviceInstallPipeName,
  10317. ulSize,
  10318. &hDeviceInstallPipe) != NO_ERROR) {
  10319. ASSERT(hDeviceInstallPipe == NULL);
  10320. goto Clean0;
  10321. }
  10322. //
  10323. // Create an event that a user-client can synchronize with and set, and
  10324. // that we will block on after we send a device install to newdev.dll.
  10325. //
  10326. if (CreateUserSynchEvent(
  10327. hUserToken,
  10328. szDeviceInstallEventName,
  10329. &hDeviceInstallEvent) != NO_ERROR) {
  10330. ASSERT(hDeviceInstallEvent == NULL);
  10331. goto Clean0;
  10332. }
  10333. //
  10334. // Create an event that we can use internally such that waiters can know
  10335. // when to disconnect from the device install client.
  10336. //
  10337. hDeviceInstallDisconnectEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  10338. if (!hDeviceInstallDisconnectEvent) {
  10339. goto Clean0;
  10340. }
  10341. //
  10342. // Launch newdev.dll using rundll32.exe, passing it the pipe name.
  10343. // "rundll32.exe newdev.dll,ClientSideInstall <device-install-pipe-name>"
  10344. //
  10345. if (FAILED(StringCchPrintf(
  10346. szCmdLine,
  10347. SIZECHARS(szCmdLine),
  10348. L"%ws %ws,%ws %ws",
  10349. RUNDLL32_EXE,
  10350. NEWDEV_DLL,
  10351. L"ClientSideInstall",
  10352. szDeviceInstallPipeName))) {
  10353. goto Clean0;
  10354. }
  10355. #if DBG
  10356. //
  10357. // Retrieve debugger settings from the service key.
  10358. //
  10359. {
  10360. HKEY hKey;
  10361. if (RegOpenKeyEx(ghServicesKey,
  10362. pszRegKeyPlugPlayServiceParams,
  10363. 0,
  10364. KEY_READ,
  10365. &hKey) == ERROR_SUCCESS) {
  10366. ULONG ulValue = 0;
  10367. WCHAR szDebugCmdLine[MAX_PATH];
  10368. ulSize = sizeof(ulValue);
  10369. if ((RegQueryValueEx(hKey,
  10370. pszRegValueDebugInstall,
  10371. NULL,
  10372. NULL,
  10373. (LPBYTE)&ulValue,
  10374. &ulSize) == ERROR_SUCCESS) &&(ulValue == 1)) {
  10375. ulSize = sizeof(szDebugCmdLine);
  10376. if (RegQueryValueEx(hKey,
  10377. pszRegValueDebugInstallCommand,
  10378. NULL,
  10379. NULL,
  10380. (LPBYTE)szDebugCmdLine,
  10381. &ulSize) != ERROR_SUCCESS) {
  10382. //
  10383. // If no debugger was retrieved, use the default
  10384. // debugger (ntsd.exe).
  10385. //
  10386. if (FAILED(StringCchCopyEx(
  10387. szDebugCmdLine,
  10388. SIZECHARS(szDebugCmdLine),
  10389. NTSD_EXE,
  10390. NULL, NULL,
  10391. STRSAFE_NULL_ON_FAILURE))) {
  10392. //
  10393. // No debugger will be used.
  10394. //
  10395. NOTHING;
  10396. }
  10397. }
  10398. if ((SUCCEEDED(StringCchCatEx(
  10399. szDebugCmdLine,
  10400. SIZECHARS(szDebugCmdLine),
  10401. L" ",
  10402. NULL, NULL,
  10403. STRSAFE_NULL_ON_FAILURE |
  10404. STRSAFE_IGNORE_NULLS))) &&
  10405. (SUCCEEDED(StringCchCatEx(
  10406. szDebugCmdLine,
  10407. SIZECHARS(szDebugCmdLine),
  10408. szCmdLine,
  10409. NULL, NULL,
  10410. STRSAFE_NULL_ON_FAILURE |
  10411. STRSAFE_IGNORE_NULLS)))) {
  10412. //
  10413. // Only overwrite the original command line buffer with
  10414. // a debug command line info if we were successful in
  10415. // builing a debug command line.
  10416. //
  10417. if (FAILED(StringCchCopyEx(
  10418. szCmdLine,
  10419. SIZECHARS(szCmdLine),
  10420. szDebugCmdLine,
  10421. NULL, NULL,
  10422. STRSAFE_IGNORE_NULLS))) {
  10423. //
  10424. // Nothing more we can do here.
  10425. //
  10426. NOTHING;
  10427. }
  10428. }
  10429. }
  10430. RegCloseKey(hKey);
  10431. }
  10432. }
  10433. #endif // DBG
  10434. //
  10435. // Attempt to create the user's environment block. If for some reason we
  10436. // can't, we'll just have to create the process without it.
  10437. //
  10438. if (!CreateEnvironmentBlock(&lpEnvironment,
  10439. hUserToken,
  10440. FALSE)) {
  10441. KdPrintEx((DPFLTR_PNPMGR_ID,
  10442. DBGF_INSTALL | DBGF_ERRORS,
  10443. "UMPNPMGR: CreateDeviceInstallClient: "
  10444. "Failed to allocate environment block, error = %d!\n",
  10445. GetLastError()));
  10446. lpEnvironment = NULL;
  10447. }
  10448. StartupInfo.cb = sizeof(StartupInfo);
  10449. StartupInfo.wShowWindow = SW_SHOW;
  10450. StartupInfo.lpDesktop = DEFAULT_INTERACTIVE_DESKTOP; // WinSta0\Default
  10451. //
  10452. // CreateProcessAsUser will create the process in the session
  10453. // specified by the by user-token.
  10454. //
  10455. if (!CreateProcessAsUser(hUserToken, // hToken
  10456. NULL, // lpApplicationName
  10457. szCmdLine, // lpCommandLine
  10458. NULL, // lpProcessAttributes
  10459. NULL, // lpThreadAttributes
  10460. FALSE, // bInheritHandles
  10461. CREATE_UNICODE_ENVIRONMENT |
  10462. DETACHED_PROCESS, // dwCreationFlags
  10463. lpEnvironment, // lpEnvironment
  10464. NULL, // lpDirectory
  10465. &StartupInfo, // lpStartupInfo
  10466. &ProcessInfo // lpProcessInfo
  10467. )) {
  10468. KdPrintEx((DPFLTR_PNPMGR_ID,
  10469. DBGF_INSTALL | DBGF_ERRORS,
  10470. "UMPNPMGR: CreateDeviceInstallClient: "
  10471. "Create rundll32 process failed, error = %d\n",
  10472. GetLastError()));
  10473. goto Clean0;
  10474. }
  10475. ASSERT(ProcessInfo.hProcess);
  10476. ASSERT(ProcessInfo.hThread);
  10477. //
  10478. // Create an event for use with overlapped I/O - no security, manual
  10479. // reset, not signalled, no name.
  10480. //
  10481. overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  10482. if (overlapped.hEvent == NULL) {
  10483. goto Clean0;
  10484. }
  10485. //
  10486. // Connect to the newly created named pipe. If newdev is not already
  10487. // connected to the named pipe, then ConnectNamedPipe() will fail with
  10488. // ERROR_IO_PENDING, and we will wait on the overlapped event. If
  10489. // newdev is already connected, it will fail with ERROR_PIPE_CONNECTED.
  10490. // Note however that neither of these is an error condition.
  10491. //
  10492. if (!ConnectNamedPipe(hDeviceInstallPipe, &overlapped)) {
  10493. //
  10494. // Overlapped ConnectNamedPipe should always return FALSE on
  10495. // success. Check the last error to see what really happened.
  10496. //
  10497. dwError = GetLastError();
  10498. if (dwError == ERROR_IO_PENDING) {
  10499. //
  10500. // I/O is pending, wait up to one minute for the client to
  10501. // connect, also wait on the process in case it terminates
  10502. // unexpectedly.
  10503. //
  10504. hFinishEvents[0] = overlapped.hEvent;
  10505. hFinishEvents[1] = ProcessInfo.hProcess;
  10506. dwWait = WaitForMultipleObjects(2, hFinishEvents,
  10507. FALSE,
  10508. PNP_PIPE_TIMEOUT); // 60 seconds
  10509. if (dwWait == WAIT_OBJECT_0) {
  10510. //
  10511. // The overlapped I/O operation completed. Check the status
  10512. // of the operation.
  10513. //
  10514. if (!GetOverlappedResult(hDeviceInstallPipe,
  10515. &overlapped,
  10516. &dwBytes,
  10517. FALSE)) {
  10518. goto Clean0;
  10519. }
  10520. } else {
  10521. //
  10522. // Either the connection timed out, or the client process
  10523. // exited. Cancel pending I/O against the pipe, and quit.
  10524. //
  10525. KdPrintEx((DPFLTR_PNPMGR_ID,
  10526. DBGF_INSTALL | DBGF_ERRORS,
  10527. "UMPNPMGR: CreateDeviceInstallClient: "
  10528. "Connect timed out, or client process exited!\n"));
  10529. CancelIo(hDeviceInstallPipe);
  10530. goto Clean0;
  10531. }
  10532. } else if (dwError != ERROR_PIPE_CONNECTED) {
  10533. //
  10534. // If the last error indicates anything other than pending I/O,
  10535. // or that The client is already connected to named pipe, fail.
  10536. //
  10537. goto Clean0;
  10538. }
  10539. } else {
  10540. //
  10541. // ConnectNamedPipe should not return anything but FALSE in
  10542. // overlapped mode.
  10543. //
  10544. goto Clean0;
  10545. }
  10546. //
  10547. // The client is now connected to the named pipe.
  10548. // Close the overlapped event.
  10549. //
  10550. CloseHandle(overlapped.hEvent);
  10551. overlapped.hEvent = NULL;
  10552. //
  10553. // The first data in the device install pipe will be the length of
  10554. // the name of the event that will be used to sync up umpnpmgr.dll
  10555. // and newdev.dll.
  10556. //
  10557. if (!WriteFile(hDeviceInstallPipe,
  10558. &ulDeviceInstallEventNameSize,
  10559. sizeof(ulDeviceInstallEventNameSize),
  10560. &ulSize,
  10561. NULL)) {
  10562. LogErrorEvent(ERR_WRITING_SERVER_INSTALL_PIPE, GetLastError(), 0);
  10563. goto Clean0;
  10564. }
  10565. //
  10566. // The next data in the device install pipe will be the name of the
  10567. // event that will be used to sync up umpnpmgr.dll and newdev.dll.
  10568. //
  10569. if (!WriteFile(hDeviceInstallPipe,
  10570. (LPCVOID)szDeviceInstallEventName,
  10571. ulDeviceInstallEventNameSize,
  10572. &ulSize,
  10573. NULL)) {
  10574. LogErrorEvent(ERR_WRITING_SERVER_INSTALL_PIPE, GetLastError(), 0);
  10575. goto Clean0;
  10576. }
  10577. //
  10578. // Allocate a new device install client entry for the list, and save all
  10579. // the handles with it.
  10580. //
  10581. pDeviceInstallClient = HeapAlloc(ghPnPHeap, 0, sizeof(INSTALL_CLIENT_ENTRY));
  10582. if(!pDeviceInstallClient) {
  10583. goto Clean0;
  10584. }
  10585. pDeviceInstallClient->Next = NULL;
  10586. pDeviceInstallClient->RefCount = 1;
  10587. pDeviceInstallClient->ulSessionId = SessionId;
  10588. pDeviceInstallClient->hEvent = hDeviceInstallEvent;
  10589. pDeviceInstallClient->hPipe = hDeviceInstallPipe;
  10590. pDeviceInstallClient->hProcess = ProcessInfo.hProcess;
  10591. pDeviceInstallClient->hDisconnectEvent = hDeviceInstallDisconnectEvent;
  10592. pDeviceInstallClient->ulInstallFlags = 0;
  10593. pDeviceInstallClient->LastDeviceId[0] = L'\0';
  10594. //
  10595. // Insert the newly created device install client info to our list.
  10596. // The caller must have previously acquired the InstallClientList lock.
  10597. //
  10598. entry = (PINSTALL_CLIENT_ENTRY)InstallClientList.Next;
  10599. if (!entry) {
  10600. InstallClientList.Next = pDeviceInstallClient;
  10601. } else {
  10602. while ((PINSTALL_CLIENT_ENTRY)entry->Next) {
  10603. entry = (PINSTALL_CLIENT_ENTRY)entry->Next;
  10604. }
  10605. entry->Next = pDeviceInstallClient;
  10606. }
  10607. bStatus = TRUE;
  10608. Clean0:
  10609. NOTHING;
  10610. } except (EXCEPTION_EXECUTE_HANDLER) {
  10611. KdPrintEx((DPFLTR_PNPMGR_ID,
  10612. DBGF_ERRORS | DBGF_INSTALL,
  10613. "UMPNPMGR: Exception during CreateDeviceInstallClient!\n"));
  10614. ASSERT(0);
  10615. bStatus = FALSE;
  10616. //
  10617. // Reference the following variable so the compiler will respect
  10618. // statement ordering w.r.t. its assignment.
  10619. //
  10620. lpEnvironment = lpEnvironment;
  10621. ProcessInfo.hThread = ProcessInfo.hThread;
  10622. ProcessInfo.hProcess = ProcessInfo.hProcess;
  10623. hUserToken = hUserToken;
  10624. hDeviceInstallDisconnectEvent = hDeviceInstallDisconnectEvent;
  10625. hDeviceInstallEvent = hDeviceInstallEvent;
  10626. hDeviceInstallPipe = hDeviceInstallPipe;
  10627. }
  10628. if (lpEnvironment) {
  10629. DestroyEnvironmentBlock(lpEnvironment);
  10630. }
  10631. //
  10632. // Close the handle to the thread since we don't need it.
  10633. //
  10634. if (ProcessInfo.hThread) {
  10635. CloseHandle(ProcessInfo.hThread);
  10636. }
  10637. if (hUserToken) {
  10638. CloseHandle(hUserToken);
  10639. }
  10640. if (overlapped.hEvent) {
  10641. CloseHandle(overlapped.hEvent);
  10642. }
  10643. if (!bStatus) {
  10644. ASSERT(!pDeviceInstallClient);
  10645. if (hDeviceInstallDisconnectEvent) {
  10646. CloseHandle(hDeviceInstallDisconnectEvent);
  10647. }
  10648. if (hDeviceInstallEvent) {
  10649. CloseHandle(hDeviceInstallEvent);
  10650. }
  10651. if (hDeviceInstallPipe) {
  10652. CloseHandle(hDeviceInstallPipe);
  10653. }
  10654. if (ProcessInfo.hProcess) {
  10655. CloseHandle(ProcessInfo.hProcess);
  10656. }
  10657. *DeviceInstallClient = NULL;
  10658. } else {
  10659. KdPrintEx((DPFLTR_PNPMGR_ID,
  10660. DBGF_INSTALL,
  10661. "UMPNPMGR: CreateDeviceInstallClient: created new client for Session %d.\n",
  10662. SessionId));
  10663. ASSERT(pDeviceInstallClient);
  10664. ASSERT(pDeviceInstallClient->hEvent);
  10665. ASSERT(pDeviceInstallClient->hPipe);
  10666. ASSERT(pDeviceInstallClient->hProcess);
  10667. ASSERT(pDeviceInstallClient->hDisconnectEvent);
  10668. *DeviceInstallClient = pDeviceInstallClient;
  10669. }
  10670. return bStatus;
  10671. } // CreateDeviceInstallClient
  10672. BOOL
  10673. ConnectDeviceInstallClient(
  10674. IN ULONG SessionId,
  10675. OUT PINSTALL_CLIENT_ENTRY *DeviceInstallClient
  10676. )
  10677. /*++
  10678. Routine Description:
  10679. Retrieves the device install client handles for the specified session,
  10680. if one exists.
  10681. Arguments:
  10682. SessionId - Session for which a device install client should be
  10683. created or connected to.
  10684. DeviceInstallClient - Receives a pointer to receive the a pointer to the
  10685. device install client for this session.
  10686. Return Value:
  10687. Returns TRUE if an existing device install client was found for the
  10688. specified session, FALSE otherwise.
  10689. Notes:
  10690. The InstallClientList lock must be acquired by the caller of this routine.
  10691. --*/
  10692. {
  10693. PINSTALL_CLIENT_ENTRY entry;
  10694. BOOL bClientFound = FALSE;
  10695. //
  10696. // Make sure the specified SessionId is valid.
  10697. //
  10698. ASSERT(SessionId != INVALID_SESSION);
  10699. if (SessionId == INVALID_SESSION) {
  10700. KdPrintEx((DPFLTR_PNPMGR_ID,
  10701. DBGF_INSTALL | DBGF_ERRORS,
  10702. "UMPNPMGR: ConnectDeviceInstallClient: Invalid SessionId %d, exiting!\n",
  10703. SessionId));
  10704. return FALSE;
  10705. }
  10706. //
  10707. // Validate output parameters.
  10708. //
  10709. ASSERT(DeviceInstallClient);
  10710. if (!DeviceInstallClient) {
  10711. return FALSE;
  10712. }
  10713. entry = LocateDeviceInstallClient(SessionId);
  10714. if (entry) {
  10715. //
  10716. // An existing client was found for this session, so we should already
  10717. // have event, pipe, and process handles for it.
  10718. //
  10719. ASSERT(entry->hEvent);
  10720. ASSERT(entry->hPipe);
  10721. ASSERT(entry->hProcess);
  10722. //
  10723. // Make sure the client's process object is in the nonsignalled state,
  10724. // else newdev has already gone away, and we can't use it.
  10725. //
  10726. if (WaitForSingleObject(entry->hProcess, 0) != WAIT_TIMEOUT) {
  10727. //
  10728. // Remove the initial reference to close the handles and remove it
  10729. // from our list.
  10730. //
  10731. ASSERT(entry->RefCount == 1);
  10732. DereferenceDeviceInstallClient(entry);
  10733. } else {
  10734. //
  10735. // If we are reconnecting to a client that was last used during a
  10736. // previous connection to this session, we will not have a disconnect
  10737. // event for it yet, so create one here. If we just created this client
  10738. // during the current connection to this session, we will already have a
  10739. // disconnect event for it.
  10740. //
  10741. if (!entry->hDisconnectEvent) {
  10742. entry->hDisconnectEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  10743. }
  10744. //
  10745. // Either way, make sure we have a disconnect event by now.
  10746. //
  10747. ASSERT(entry->hDisconnectEvent);
  10748. if (entry->hDisconnectEvent) {
  10749. bClientFound = TRUE;
  10750. KdPrintEx((DPFLTR_PNPMGR_ID,
  10751. DBGF_INSTALL,
  10752. "UMPNPMGR: ConnectDeviceInstallClient: found existing client on Session %d.\n",
  10753. SessionId));
  10754. *DeviceInstallClient = entry;
  10755. }
  10756. }
  10757. }
  10758. if (!bClientFound) {
  10759. *DeviceInstallClient = NULL;
  10760. }
  10761. return bClientFound;
  10762. } // ConnectDeviceInstallClient
  10763. BOOL
  10764. DisconnectDeviceInstallClient(
  10765. IN PINSTALL_CLIENT_ENTRY DeviceInstallClient
  10766. )
  10767. /*++
  10768. Routine Description:
  10769. This routine disconnects from the current client-side install process (if
  10770. one exists) by signalling the appropriate hDisconnectEvent and closing the
  10771. handle.
  10772. Arguments:
  10773. DeviceInstallClient - Receives a pointer to the device install client that
  10774. should be disconnected.
  10775. Return Value:
  10776. Returns TRUE if successful, FALSE otherwise.
  10777. Notes:
  10778. The InstallClientList lock must be acquired by the caller of this routine.
  10779. --*/
  10780. {
  10781. BOOL bStatus = FALSE;
  10782. ASSERT(DeviceInstallClient);
  10783. if (DeviceInstallClient) {
  10784. ASSERT(DeviceInstallClient->hEvent);
  10785. ASSERT(DeviceInstallClient->hPipe);
  10786. ASSERT(DeviceInstallClient->hProcess);
  10787. //
  10788. // We may or may not have a handle to a diconnect event because we may
  10789. // have an existing client for this session, but not reconnected to it.
  10790. //
  10791. // If we do have an hDisconnectEvent, set the event now since we
  10792. // will otherwise block waiting for newdev.dll to set the
  10793. // hDeviceInstallEvent. Setting the hDisconnectEvent alerts the
  10794. // waiter that the device install was NOT successful, and that it
  10795. // should preserve the device in the install list.
  10796. //
  10797. if (DeviceInstallClient->hDisconnectEvent) {
  10798. SetEvent(DeviceInstallClient->hDisconnectEvent);
  10799. CloseHandle(DeviceInstallClient->hDisconnectEvent);
  10800. DeviceInstallClient->hDisconnectEvent = NULL;
  10801. }
  10802. KdPrintEx((DPFLTR_PNPMGR_ID,
  10803. DBGF_INSTALL,
  10804. "UMPNPMGR: Disconnected from device install client on Console SessionId %d\n",
  10805. DeviceInstallClient->ulSessionId));
  10806. bStatus = TRUE;
  10807. }
  10808. return bStatus;
  10809. } // DisconnectDeviceInstallClient
  10810. PINSTALL_CLIENT_ENTRY
  10811. LocateDeviceInstallClient(
  10812. IN ULONG SessionId
  10813. )
  10814. /*++
  10815. Routine Description:
  10816. This routine locates the client-side install process for a given session (if
  10817. one exists).
  10818. Arguments:
  10819. SessionId - Session whose device install client should be located.
  10820. Return Value:
  10821. Returns a device install client entry if successful, NULL otherwise.
  10822. Note:
  10823. The InstallClientList lock must be acquired by the caller of this routine.
  10824. --*/
  10825. {
  10826. PINSTALL_CLIENT_ENTRY entry, foundEntry = NULL;
  10827. BOOL bClientFound = FALSE;
  10828. //
  10829. // Make sure the specified SessionId is valid.
  10830. //
  10831. ASSERT(SessionId != INVALID_SESSION);
  10832. if (SessionId == INVALID_SESSION) {
  10833. KdPrintEx((DPFLTR_PNPMGR_ID,
  10834. DBGF_INSTALL | DBGF_ERRORS,
  10835. "UMPNPMGR: LocateDeviceInstallClient: Invalid Console SessionId %d, exiting!\n",
  10836. SessionId));
  10837. return FALSE;
  10838. }
  10839. //
  10840. // Search for a client on the specified session.
  10841. //
  10842. for (entry = (PINSTALL_CLIENT_ENTRY)InstallClientList.Next;
  10843. entry != NULL;
  10844. entry = entry->Next) {
  10845. if (entry->ulSessionId == SessionId) {
  10846. //
  10847. // Make sure we only have one entry per session.
  10848. //
  10849. ASSERT(!bClientFound);
  10850. bClientFound = TRUE;
  10851. foundEntry = entry;
  10852. }
  10853. }
  10854. return foundEntry;
  10855. } // LocateDeviceInstallClient
  10856. VOID
  10857. ReferenceDeviceInstallClient(
  10858. IN PINSTALL_CLIENT_ENTRY DeviceInstallClient
  10859. )
  10860. /*++
  10861. Routine Description:
  10862. This routine increments the reference count for a device install client
  10863. entry.
  10864. Parameters:
  10865. DeviceInstallClient - Supplies a pointer to the device install client to be
  10866. referenced.
  10867. Return Value:
  10868. None.
  10869. Note:
  10870. The appropriate synchronization lock must be held on the device install
  10871. client list before this routine can be called
  10872. --*/
  10873. {
  10874. ASSERT(DeviceInstallClient);
  10875. ASSERT(((LONG)DeviceInstallClient->RefCount) > 0);
  10876. KdPrintEx((DPFLTR_PNPMGR_ID,
  10877. DBGF_EVENT | DBGF_INSTALL,
  10878. "UMPNPMGR: ---------------- ReferenceDeviceInstallClient : Session %d [%d --> %d]\n",
  10879. DeviceInstallClient->ulSessionId,
  10880. DeviceInstallClient->RefCount,
  10881. DeviceInstallClient->RefCount + 1));
  10882. DeviceInstallClient->RefCount++;
  10883. return;
  10884. } // ReferenceDeviceInstallClient
  10885. VOID
  10886. DereferenceDeviceInstallClient(
  10887. IN PINSTALL_CLIENT_ENTRY DeviceInstallClient
  10888. )
  10889. /*++
  10890. Routine Description:
  10891. This routine decrements the reference count for a device install client
  10892. entry, removing the entry from the list and freeing the associated memory if
  10893. there are no outstanding reference counts.
  10894. Parameters:
  10895. DeviceInstallClient - Supplies a pointer to the device install client to be
  10896. dereferenced.
  10897. Return Value:
  10898. None.
  10899. Note:
  10900. The appropriate synchronization lock must be held on the device install
  10901. client list before this routine can be called
  10902. --*/
  10903. {
  10904. ASSERT(DeviceInstallClient);
  10905. ASSERT(((LONG)DeviceInstallClient->RefCount) > 0);
  10906. //
  10907. // Avoid over-dereferencing the client.
  10908. //
  10909. if (((LONG)DeviceInstallClient->RefCount) > 0) {
  10910. KdPrintEx((DPFLTR_PNPMGR_ID,
  10911. DBGF_EVENT | DBGF_INSTALL,
  10912. "UMPNPMGR: ---------------- DereferenceDeviceInstallClient: Session %d [%d --> %d]\n",
  10913. DeviceInstallClient->ulSessionId,
  10914. DeviceInstallClient->RefCount,
  10915. DeviceInstallClient->RefCount - 1));
  10916. DeviceInstallClient->RefCount--;
  10917. } else {
  10918. return;
  10919. }
  10920. //
  10921. // If the refcount is zero then the entry no longer needs to be in the list
  10922. // so remove and free it.
  10923. //
  10924. if (DeviceInstallClient->RefCount == 0) {
  10925. BOOL bClientFound = FALSE;
  10926. PINSTALL_CLIENT_ENTRY entry, prev;
  10927. entry = (PINSTALL_CLIENT_ENTRY)InstallClientList.Next;
  10928. prev = NULL;
  10929. while (entry) {
  10930. if (entry == DeviceInstallClient) {
  10931. KdPrintEx((DPFLTR_PNPMGR_ID,
  10932. DBGF_EVENT | DBGF_INSTALL,
  10933. "UMPNPMGR: ---------------- DereferenceDeviceInstallClient: Removing client for Session %d\n",
  10934. entry->ulSessionId));
  10935. //
  10936. // We should have handles to the pipe, event and process objects for
  10937. // the client, because we will close them here.
  10938. //
  10939. ASSERT(entry->hPipe);
  10940. ASSERT(entry->hEvent);
  10941. ASSERT(entry->hProcess);
  10942. //
  10943. // We may or may not have a handle to a diconnect event because we
  10944. // may have an existing client for this session, but not yet
  10945. // connected to it.
  10946. //
  10947. // If we do have an hDisconnectEvent, set the event now since we
  10948. // will otherwise block waiting for newdev.dll to set the
  10949. // hDeviceInstallEvent. Setting the hDisconnectEvent alerts the
  10950. // waiter that the device install was NOT successful, and that it
  10951. // should preserve the device in the install list.
  10952. //
  10953. if (entry->hDisconnectEvent) {
  10954. SetEvent(entry->hDisconnectEvent);
  10955. CloseHandle(entry->hDisconnectEvent);
  10956. }
  10957. //
  10958. // Close the pipe and event handles so that the client will get a
  10959. // ReadFile error and know that we are finished. Close the process
  10960. // handle as well.
  10961. //
  10962. if (entry->hPipe) {
  10963. CloseHandle(entry->hPipe);
  10964. }
  10965. if (entry->hEvent) {
  10966. CloseHandle(entry->hEvent);
  10967. }
  10968. if (entry->hProcess) {
  10969. CloseHandle(entry->hProcess);
  10970. }
  10971. //
  10972. // Remove the device install client entry from the list, and free it
  10973. // now.
  10974. //
  10975. if (prev) {
  10976. prev->Next = entry->Next;
  10977. } else {
  10978. InstallClientList.Next = entry->Next;
  10979. }
  10980. HeapFree(ghPnPHeap, 0, entry);
  10981. if(prev) {
  10982. entry = (PINSTALL_CLIENT_ENTRY)prev->Next;
  10983. } else {
  10984. entry = (PINSTALL_CLIENT_ENTRY)InstallClientList.Next;
  10985. }
  10986. bClientFound = TRUE;
  10987. break;
  10988. }
  10989. prev = entry;
  10990. entry = (PINSTALL_CLIENT_ENTRY)entry->Next;
  10991. }
  10992. ASSERT(bClientFound);
  10993. }
  10994. return;
  10995. } // DereferenceDeviceInstallClient
  10996. BOOL
  10997. DoDeviceInstallClient(
  10998. IN LPWSTR DeviceId,
  10999. IN PULONG SessionId,
  11000. IN ULONG Flags,
  11001. OUT PINSTALL_CLIENT_ENTRY *DeviceInstallClient
  11002. )
  11003. /*++
  11004. Routine Description:
  11005. This routine kicks off a newdev.dll process (if someone is logged in) that
  11006. displays UI informing the user of the status of the server-side device
  11007. installation.
  11008. Arguments:
  11009. DeviceId - Supplies the devnode ID of the device being installed.
  11010. SessionId - Specifies the session that the newdev client is to be launched
  11011. on. If the DEVICE_INSTALL_DISPLAY_ON_CONSOLE flag is
  11012. specified, the specified SessionId is ignored.
  11013. Upon successful return, the SessionId for the the session where
  11014. the device install client was created is returned.
  11015. If unsuccessful, the returned SessionId is INVALID_SESSION,
  11016. (0xFFFFFFFF).
  11017. Flags - Specifies flags describing the behavior of the device install client.
  11018. The following flags are currently defined:
  11019. DEVICE_INSTALL_UI_ONLY - tells newdev.dll whether to do a full
  11020. install or just show UI while umpnpmgr.dll is doing a server
  11021. side install.
  11022. DEVICE_INSTALL_PLAY_SOUND - tells newdev.dll whether to play a
  11023. sound.
  11024. DEVICE_INSTALL_DISPLAY_ON_CONSOLE - if specified, the value
  11025. specified in SessionId will be ignored, and the client will
  11026. always be displayed on the current active console session.
  11027. DeviceInstallClient - Supplies the address of a variable to receive, upon
  11028. success, a pointer to a pointer to a device install client.
  11029. Return Value:
  11030. If the process was successfully created, the return value is TRUE. This
  11031. routine doesn't wait until the process terminates.
  11032. If we couldn't create the process (e.g., because no user was logged in),
  11033. the return value is FALSE.
  11034. Notes:
  11035. None.
  11036. --*/
  11037. {
  11038. BOOL bStatus, bSameDevice = FALSE;
  11039. ULONG DeviceIdSize, ulSize, ulSessionId;
  11040. ULONG InstallFlags;
  11041. PINSTALL_CLIENT_ENTRY pDeviceInstallClient = NULL;
  11042. //
  11043. // Assume failure.
  11044. //
  11045. bStatus = FALSE;
  11046. //
  11047. // Validate output parameters.
  11048. //
  11049. if (!DeviceInstallClient || !SessionId) {
  11050. return FALSE;
  11051. }
  11052. try {
  11053. //
  11054. // Check if we should skip all client side UI.
  11055. //
  11056. if (gbSuppressUI) {
  11057. //
  11058. // If we were launching newdev for client-side installation, log an
  11059. // event to let someone know that we didn't.
  11060. //
  11061. if (!(Flags & DEVICE_INSTALL_UI_ONLY)) {
  11062. KdPrintEx((DPFLTR_PNPMGR_ID,
  11063. DBGF_INSTALL | DBGF_WARNINGS,
  11064. "UMPNPMGR: DoDeviceInstallClient: Client-side newdev UI has been suppressed, exiting.\n"));
  11065. LogWarningEvent(WRN_NEWDEV_UI_SUPPRESSED, 1, DeviceId);
  11066. }
  11067. goto Clean0;
  11068. }
  11069. //
  11070. // Determine the session to use, based on the supplied flags.
  11071. //
  11072. if (Flags & DEVICE_INSTALL_DISPLAY_ON_CONSOLE) {
  11073. ulSessionId = GetActiveConsoleSessionId();
  11074. } else {
  11075. ASSERT(*SessionId != INVALID_SESSION);
  11076. ulSessionId = *SessionId;
  11077. }
  11078. ASSERT(ulSessionId != INVALID_SESSION);
  11079. //
  11080. // If the specified session is not currently connected anywhere, don't
  11081. // bother creating any UI.
  11082. //
  11083. if (!IsSessionConnected(ulSessionId)) {
  11084. KdPrintEx((DPFLTR_PNPMGR_ID,
  11085. DBGF_EVENT,
  11086. "UMPNPMGR: DoDeviceInstallClient: SessionId %d not connected, exiting\n",
  11087. ulSessionId));
  11088. goto Clean0;
  11089. }
  11090. //
  11091. // Lock the client list while we retrieve / create a client to use.
  11092. //
  11093. LockNotifyList(&InstallClientList.Lock);
  11094. //
  11095. // First, try to connect to an existing client already running on this
  11096. // session.
  11097. //
  11098. bStatus = ConnectDeviceInstallClient(ulSessionId,
  11099. &pDeviceInstallClient);
  11100. if (bStatus) {
  11101. //
  11102. // If the client we just reconnected to was client-side installing
  11103. // this same device when it was last disconnected, don't send it
  11104. // again.
  11105. //
  11106. if ((IS_FLAG_CLEAR(Flags, DEVICE_INSTALL_UI_ONLY)) &&
  11107. (CompareString(
  11108. LOCALE_INVARIANT, NORM_IGNORECASE,
  11109. pDeviceInstallClient->LastDeviceId, -1,
  11110. DeviceId, -1) == CSTR_EQUAL)) {
  11111. bSameDevice = TRUE;
  11112. }
  11113. } else {
  11114. //
  11115. // Create a new device install client for this session.
  11116. //
  11117. bStatus = CreateDeviceInstallClient(ulSessionId,
  11118. &pDeviceInstallClient);
  11119. }
  11120. if (bStatus) {
  11121. //
  11122. // The client should only have the initial reference from when it
  11123. // was added to the list, since any use of the client is done on
  11124. // this single install thread.
  11125. //
  11126. ASSERT(pDeviceInstallClient);
  11127. ASSERT(pDeviceInstallClient->RefCount == 1);
  11128. //
  11129. // Keep track of both client and server flags.
  11130. //
  11131. pDeviceInstallClient->ulInstallFlags = Flags;
  11132. //
  11133. // Reference the device install client while it is in use. The
  11134. // caller must remove this reference when it is done with it.
  11135. //
  11136. ReferenceDeviceInstallClient(pDeviceInstallClient);
  11137. }
  11138. UnlockNotifyList(&InstallClientList.Lock);
  11139. if (!bStatus || bSameDevice) {
  11140. //
  11141. // If we don't have a client, or we don't need to resend the device
  11142. // instance to install, we're done.
  11143. //
  11144. goto Clean0;
  11145. }
  11146. //
  11147. // Filter out the install flags that the client doesn't know about.
  11148. //
  11149. InstallFlags = (Flags & DEVICE_INSTALL_CLIENT_MASK);
  11150. DeviceIdSize = (lstrlen(DeviceId) + 1) * sizeof(WCHAR);
  11151. //
  11152. // Make sure we reset the device install event since we will block waiting for
  11153. // newdev.dll to set this event to let us know that it is finished with the current
  11154. // installation.
  11155. //
  11156. if (pDeviceInstallClient->hEvent) {
  11157. ResetEvent(pDeviceInstallClient->hEvent);
  11158. }
  11159. //
  11160. // When sending stuff to newdev.dll over the device install pipe it expects
  11161. // two ULONGs followed by the DeviceID. The first ULONG is the Flags which
  11162. // tells newdev whether we are doing a UI only install or a full install.
  11163. // The next ULONG is the size of the Device ID and then we send the DeviceID.
  11164. //
  11165. if (WriteFile(pDeviceInstallClient->hPipe,
  11166. &InstallFlags,
  11167. sizeof(InstallFlags),
  11168. &ulSize,
  11169. NULL
  11170. )) {
  11171. if (WriteFile(pDeviceInstallClient->hPipe,
  11172. &DeviceIdSize,
  11173. sizeof(DeviceIdSize),
  11174. &ulSize,
  11175. NULL
  11176. )) {
  11177. if (WriteFile(pDeviceInstallClient->hPipe,
  11178. DeviceId,
  11179. DeviceIdSize,
  11180. &ulSize,
  11181. NULL
  11182. )) {
  11183. bStatus = TRUE;
  11184. } else {
  11185. LogErrorEvent(ERR_WRITING_SERVER_INSTALL_PIPE, GetLastError(), 0);
  11186. }
  11187. } else {
  11188. LogErrorEvent(ERR_WRITING_SERVER_INSTALL_PIPE, GetLastError(), 0);
  11189. }
  11190. } else {
  11191. LogErrorEvent(ERR_WRITING_SERVER_INSTALL_PIPE, GetLastError(), 0);
  11192. }
  11193. //
  11194. // Note that we don't remove the reference placed on the install client
  11195. // entry while it was in use, because it will be handed back to the
  11196. // caller, who will wait on the client's event and process handles. The
  11197. // caller should remove the reference when it is no longer using these.
  11198. // Removing the final reference will cause the client to be closed.
  11199. //
  11200. } except(EXCEPTION_EXECUTE_HANDLER) {
  11201. KdPrintEx((DPFLTR_PNPMGR_ID,
  11202. DBGF_ERRORS | DBGF_WARNINGS,
  11203. "UMPNPMGR: Exception during DoDeviceInstallClient!\n"));
  11204. ASSERT(0);
  11205. bStatus = FALSE;
  11206. //
  11207. // Reference the following variable so the compiler will respect
  11208. // statement ordering w.r.t. its assignment.
  11209. //
  11210. pDeviceInstallClient = pDeviceInstallClient;
  11211. }
  11212. Clean0:
  11213. if (!bStatus) {
  11214. //
  11215. // If we had a device install client at some point, but failed to send
  11216. // it the request, remove the reference we placed on it.
  11217. //
  11218. if (pDeviceInstallClient) {
  11219. LockNotifyList(&InstallClientList.Lock);
  11220. DereferenceDeviceInstallClient(pDeviceInstallClient);
  11221. UnlockNotifyList(&InstallClientList.Lock);
  11222. }
  11223. //
  11224. // Let the caller know there isn't a device install client handling
  11225. // this request.
  11226. //
  11227. *SessionId = INVALID_SESSION;
  11228. *DeviceInstallClient = NULL;
  11229. } else {
  11230. //
  11231. // Make sure we're returning valid client information.
  11232. //
  11233. ASSERT(pDeviceInstallClient);
  11234. ASSERT(pDeviceInstallClient->hEvent);
  11235. ASSERT(pDeviceInstallClient->hPipe);
  11236. ASSERT(pDeviceInstallClient->hProcess);
  11237. ASSERT(pDeviceInstallClient->hDisconnectEvent);
  11238. ASSERT(pDeviceInstallClient->ulSessionId != INVALID_SESSION);
  11239. *SessionId = pDeviceInstallClient->ulSessionId;
  11240. *DeviceInstallClient = pDeviceInstallClient;
  11241. }
  11242. return bStatus;
  11243. } // DoDeviceInstallClient
  11244. unsigned _stdcall
  11245. ThreadProc_RunOnce(
  11246. LPVOID lpThreadParameter
  11247. )
  11248. /*++
  11249. Routine Description:
  11250. This routine performs server-side processing of the RunOnce entries that
  11251. have been accumulated by setupapi. The RunOnce node list will be empty
  11252. upon return.
  11253. Arguments:
  11254. lpThreadParameter - Specifies the head of the RunOnce node list to be
  11255. processed.
  11256. Return Value:
  11257. If successful, the return value is NO_ERROR. If failure, the return value
  11258. is a Win32 error code indicating the cause of failure.
  11259. --*/
  11260. {
  11261. DWORD Err = NO_ERROR;
  11262. PPSP_RUNONCE_NODE RunOnceNode;
  11263. HINSTANCE hLib;
  11264. CHAR AnsiBuffer[MAX_PATH * 2];
  11265. PSTR EndPtr;
  11266. RUNDLLPROCA fpRunDllProcA = NULL;
  11267. RUNDLLPROCW fpRunDllProcW = NULL;
  11268. HRESULT hr;
  11269. //
  11270. // This thread is executed synchronously during the server-side device
  11271. // installer process, therefore setupapi must already be loaded. The
  11272. // RunOnce node list is stored as global state in SETUPAPI.DLL, while it is
  11273. // loaded for this instance of server-side device install processing.
  11274. //
  11275. ASSERT(ghDeviceInstallerLib != NULL);
  11276. //
  11277. // ISSUE-2002/02/20-jamesca: Consider a separate thread for each entry?
  11278. // Note that this routine processes all RunOnce entries in the context of
  11279. // a single thread. Any catastrophic errors encountered while processing
  11280. // one entry will affect or prevent subsequent entries. For greater
  11281. // isolation, we could consider creating a separate thread for each, but
  11282. // that would adversely affect performance to protect against things that
  11283. // are supposed to be signed in the first place, and any RunOnce entry we
  11284. // call could do worse things to this process anyways.
  11285. //
  11286. KdPrintEx((DPFLTR_PNPMGR_ID,
  11287. DBGF_INSTALL,
  11288. "UMPNPMGR: Processing RunOnce entries "
  11289. "during server-side device install.\n"));
  11290. //
  11291. // Process each node in the list supplied. This thread is only created
  11292. // because there are nodes to be processed, so the list must be non-NULL.
  11293. //
  11294. RunOnceNode = (PPSP_RUNONCE_NODE)lpThreadParameter;
  11295. ASSERT(RunOnceNode != NULL);
  11296. while (RunOnceNode != NULL) {
  11297. hLib = NULL;
  11298. try {
  11299. //
  11300. // First, load the DLL (setupapi already did the signature
  11301. // verification for us, so this should be safe).
  11302. //
  11303. hLib = LoadLibrary(RunOnceNode->DllFullPath);
  11304. if (hLib) {
  11305. //
  11306. // First, try to retrieve the 'W' (Unicode) version of the entrypoint.
  11307. //
  11308. if (SUCCEEDED(StringCchCopyExA(
  11309. AnsiBuffer,
  11310. (sizeof(AnsiBuffer) / sizeof(CHAR)) - 1,
  11311. RunOnceNode->DllEntryPointName,
  11312. &EndPtr, NULL,
  11313. STRSAFE_IGNORE_NULLS |
  11314. STRSAFE_NULL_ON_FAILURE))) {
  11315. *EndPtr = 'W';
  11316. *(EndPtr + 1) = '\0';
  11317. fpRunDllProcW = (RUNDLLPROCW)GetProcAddress(hLib, AnsiBuffer);
  11318. }
  11319. if (!fpRunDllProcW) {
  11320. //
  11321. // Couldn't find unicode entrypt, try 'A' decorated one
  11322. //
  11323. *EndPtr = 'A';
  11324. fpRunDllProcA = (RUNDLLPROCA)GetProcAddress(hLib, AnsiBuffer);
  11325. if (!fpRunDllProcA) {
  11326. //
  11327. // Couldn't find 'A' decorated entrypt, try undecorated name
  11328. // undecorated entrypts are assumed to be ANSI
  11329. //
  11330. *EndPtr = '\0';
  11331. fpRunDllProcA = (RUNDLLPROCA)GetProcAddress(hLib, AnsiBuffer);
  11332. }
  11333. }
  11334. //
  11335. // We shoulda found one of these...
  11336. //
  11337. ASSERT(fpRunDllProcW || fpRunDllProcA);
  11338. if (fpRunDllProcW) {
  11339. //
  11340. // Re-use our ANSI buffer to hold a writeable copy of our
  11341. // DLL argument string.
  11342. //
  11343. hr = StringCchCopyW((LPWSTR)AnsiBuffer,
  11344. sizeof(AnsiBuffer) / sizeof(WCHAR), // size of buffer in WCHARs
  11345. RunOnceNode->DllParams);
  11346. ASSERT(SUCCEEDED(hr));
  11347. fpRunDllProcW(NULL, ghInst, (LPWSTR)AnsiBuffer, SW_HIDE);
  11348. } else if (fpRunDllProcA) {
  11349. //
  11350. // Need to convert the arg string to ANSI first...
  11351. //
  11352. WideCharToMultiByte(CP_ACP,
  11353. 0, // default composite char behavior
  11354. RunOnceNode->DllParams,
  11355. -1,
  11356. AnsiBuffer,
  11357. sizeof(AnsiBuffer),
  11358. NULL,
  11359. NULL
  11360. );
  11361. fpRunDllProcA(NULL, ghInst, AnsiBuffer, SW_HIDE);
  11362. }
  11363. }
  11364. } except(EXCEPTION_EXECUTE_HANDLER) {
  11365. KdPrintEx((DPFLTR_PNPMGR_ID,
  11366. DBGF_ERRORS | DBGF_INSTALL,
  11367. "UMPNPMGR: Exception %d during ThreadProc_RunOnce!\n",
  11368. GetExceptionCode()));
  11369. Err = GetExceptionCode();
  11370. ASSERT(0);
  11371. //
  11372. // Reference the following variable so the compiler will respect
  11373. // statement ordering w.r.t. its assignment.
  11374. //
  11375. hLib = hLib;
  11376. }
  11377. //
  11378. // Free the library, if loaded.
  11379. //
  11380. if (hLib != NULL) {
  11381. FreeLibrary(hLib);
  11382. hLib = NULL;
  11383. }
  11384. //
  11385. // If we encountered an exception processing this entry, exit
  11386. // immediately. Don't process any additional entries because the
  11387. // exception may have occured because the thread state was corrupted by
  11388. // one of our callees, which could cause problems for the others. Note,
  11389. // the main device install thread is waiting on this thread, and will
  11390. // log an error in the eventlog if we exit with an error.
  11391. //
  11392. if (Err != NO_ERROR) {
  11393. goto Clean0;
  11394. }
  11395. //
  11396. // We're still doing ok, move on to the next one.
  11397. //
  11398. RunOnceNode = RunOnceNode->Next;
  11399. }
  11400. //
  11401. // If we make it here, we managed to process all queued RunOnce entries
  11402. // without any catastrophic failures.
  11403. //
  11404. ASSERT(Err == NO_ERROR);
  11405. Clean0:
  11406. _endthreadex(Err);
  11407. //
  11408. // Unreachable code, but it makes the compiler happy.
  11409. //
  11410. return Err;
  11411. } // ThreadProc_RunOnce
  11412. VOID
  11413. DoRunOnce(
  11414. VOID
  11415. )
  11416. /*++
  11417. Routine Description:
  11418. This routine performs server-side processing of the RunOnce entries that
  11419. have been accumulated by setupapi. The RunOnce node list will be empty
  11420. upon return.
  11421. Arguments:
  11422. None.
  11423. Return Value:
  11424. None.
  11425. --*/
  11426. {
  11427. PPSP_RUNONCE_NODE RunOnceNode;
  11428. HANDLE hRunOnceThread;
  11429. DWORD ThreadID = 0, ThreadExitCode = NO_ERROR, WaitStatus;
  11430. //
  11431. // First, check to see if there are any RunOnce entries that need to be
  11432. // processed.
  11433. //
  11434. RunOnceNode = fpAccessRunOnceNodeList();
  11435. if (RunOnceNode != NULL) {
  11436. //
  11437. // Create the thread that will process the RunOnce RUNDLL entries that have
  11438. // been queued up.
  11439. //
  11440. hRunOnceThread =
  11441. (HANDLE)_beginthreadex(
  11442. (void*)NULL,
  11443. (unsigned)0,
  11444. (unsigned int (__stdcall *)(void *))ThreadProc_RunOnce,
  11445. (void*)RunOnceNode,
  11446. (unsigned)0,
  11447. (unsigned int*)&ThreadID);
  11448. if (hRunOnceThread != NULL) {
  11449. //
  11450. // Wait synchronously for the RunOnce thread to complete processing the
  11451. // nodes, and exit.
  11452. //
  11453. WaitStatus =
  11454. WaitForSingleObject(
  11455. hRunOnceThread, INFINITE);
  11456. ASSERT(WaitStatus == WAIT_OBJECT_0);
  11457. if (GetExitCodeThread(
  11458. hRunOnceThread, &ThreadExitCode)) {
  11459. //
  11460. // If the thread exit code was not NO_ERROR, some exception
  11461. // occured while processing the RunOnce entries.
  11462. //
  11463. if (ThreadExitCode != NO_ERROR) {
  11464. LogErrorEvent(ERR_PROCESSING_RUNONCE, ThreadExitCode, 0);
  11465. }
  11466. } else {
  11467. //
  11468. // The above wait on the thread handle succeeded, so the thread
  11469. // should NOT still be active.
  11470. //
  11471. ASSERT(GetLastError() != STILL_ACTIVE);
  11472. }
  11473. //
  11474. // Close the handle to the thread object.
  11475. //
  11476. CloseHandle(hRunOnceThread);
  11477. }
  11478. }
  11479. //
  11480. // Free all the members in the list.
  11481. //
  11482. fpDestroyRunOnceNodeList();
  11483. return;
  11484. } // DoRunOnce
  11485. DWORD
  11486. SessionNotificationHandler(
  11487. IN DWORD EventType,
  11488. IN PWTSSESSION_NOTIFICATION SessionNotification
  11489. )
  11490. /*++
  11491. Routine Description:
  11492. This routine handles console switch events.
  11493. Arguments:
  11494. EventType - The type of event that has occurred.
  11495. SessionNotification - Additional event information.
  11496. Return Value:
  11497. If successful, the return value is NO_ERROR.
  11498. If failure, the return value is a Win32 error code indicating the cause of
  11499. failure.
  11500. Notes:
  11501. Session change notification events are used to determine when there is a
  11502. session with a logged on user currently connected to the Console. When a
  11503. user session is connected to the Console, we signal the "logged on" event,
  11504. which will wake the device installation thread to perform any pending
  11505. client-side device install events. When there is no user session connected
  11506. to the Console, the "logged on" event is reset. The "logged on" event may
  11507. also be set/reset for logon/logoff events to session 0 by PNP_ReportLogOn /
  11508. PnpConsoleCtrlHandler, in the event that Terminal Services are not
  11509. available.
  11510. --*/
  11511. {
  11512. PINSTALL_CLIENT_ENTRY pDeviceInstallClient;
  11513. //
  11514. // Validate the session change notification structure.
  11515. //
  11516. ASSERT(SessionNotification);
  11517. ASSERT(SessionNotification->cbSize >= sizeof(WTSSESSION_NOTIFICATION));
  11518. if ((!ARGUMENT_PRESENT(SessionNotification)) ||
  11519. (SessionNotification->cbSize < sizeof(WTSSESSION_NOTIFICATION))) {
  11520. return ERROR_INVALID_PARAMETER;
  11521. }
  11522. switch (EventType) {
  11523. case WTS_CONSOLE_CONNECT:
  11524. //
  11525. // The notification was sent because the specified session was
  11526. // connected to the Console.
  11527. //
  11528. KdPrintEx((DPFLTR_PNPMGR_ID,
  11529. DBGF_EVENT | DBGF_INSTALL,
  11530. "UMPNPMGR: WTS_CONSOLE_CONNECT: "
  11531. "SessionId %d\n",
  11532. SessionNotification->dwSessionId));
  11533. //
  11534. // Keep track globally of the current active console session, and
  11535. // signal that it's safe to access it.
  11536. //
  11537. // NOTE - we must set the ghActiveConsoleSessionEvent here, prior to
  11538. // calling IsConsoleSession below, which waits on it, else we will
  11539. // deadlock out service's control handler.
  11540. //
  11541. gActiveConsoleSessionId = (ULONG)SessionNotification->dwSessionId;
  11542. if (ghActiveConsoleSessionEvent) {
  11543. SetEvent(ghActiveConsoleSessionEvent);
  11544. }
  11545. //
  11546. // If the session just connected to the Console already has a logged
  11547. // on user, signal the "logged on" event.
  11548. //
  11549. if (IsConsoleSession((ULONG)SessionNotification->dwSessionId) &&
  11550. IsUserLoggedOnSession((ULONG)SessionNotification->dwSessionId)) {
  11551. if (InstallEvents[LOGGED_ON_EVENT]) {
  11552. SetEvent(InstallEvents[LOGGED_ON_EVENT]);
  11553. KdPrintEx((DPFLTR_PNPMGR_ID,
  11554. DBGF_EVENT | DBGF_INSTALL,
  11555. "UMPNPMGR: WTS_CONSOLE_CONNECT: "
  11556. "SetEvent LOGGED_ON_EVENT\n"));
  11557. }
  11558. }
  11559. break;
  11560. case WTS_CONSOLE_DISCONNECT:
  11561. //
  11562. // The notification was sent because the specified session
  11563. // was disconnected from the Console.
  11564. //
  11565. KdPrintEx((DPFLTR_PNPMGR_ID,
  11566. DBGF_EVENT | DBGF_INSTALL,
  11567. "UMPNPMGR: WTS_CONSOLE_DISCONNECT: "
  11568. "SessionId %d\n",
  11569. SessionNotification->dwSessionId));
  11570. //
  11571. // Check if the session just disconnected from the "Console" has a
  11572. // logged on user.
  11573. //
  11574. if (IsConsoleSession((ULONG)SessionNotification->dwSessionId) &&
  11575. IsUserLoggedOnSession((ULONG)SessionNotification->dwSessionId)) {
  11576. //
  11577. // Reset the "logged on" event.
  11578. //
  11579. if (InstallEvents[LOGGED_ON_EVENT]) {
  11580. KdPrintEx((DPFLTR_PNPMGR_ID,
  11581. DBGF_EVENT | DBGF_INSTALL,
  11582. "UMPNPMGR: WTS_CONSOLE_DISCONNECT: "
  11583. "ResetEvent LOGGED_ON_EVENT\n"));
  11584. ResetEvent(InstallEvents[LOGGED_ON_EVENT]);
  11585. }
  11586. //
  11587. // Since this is a console switch event, only do something with
  11588. // a device install client on the console session if it's
  11589. // behavior was specifically designated for the console (i.e. -
  11590. // it was put on this session because it was the active console
  11591. // session at the time).
  11592. //
  11593. LockNotifyList(&InstallClientList.Lock);
  11594. pDeviceInstallClient = LocateDeviceInstallClient((ULONG)SessionNotification->dwSessionId);
  11595. if ((pDeviceInstallClient) &&
  11596. (pDeviceInstallClient->ulInstallFlags & DEVICE_INSTALL_DISPLAY_ON_CONSOLE)) {
  11597. if (pDeviceInstallClient->ulInstallFlags & DEVICE_INSTALL_UI_ONLY) {
  11598. //
  11599. // If it was just for UI only, dereference it to make it
  11600. // go away when it's no longer in use.
  11601. //
  11602. DereferenceDeviceInstallClient(pDeviceInstallClient);
  11603. } else {
  11604. //
  11605. // Otherwise, it is a legitimate client-side
  11606. // installation in progress, so just disconnect from it.
  11607. // This does not remove a reference because we want it
  11608. // to stay around in case the session is reconnected to
  11609. // and the device still needs to be installed, - or
  11610. // until we find out that there are no more devices to
  11611. // install, in which case we'll close it.
  11612. //
  11613. DisconnectDeviceInstallClient(pDeviceInstallClient);
  11614. }
  11615. }
  11616. UnlockNotifyList(&InstallClientList.Lock);
  11617. }
  11618. //
  11619. // The current active console session is invalid until we receive a
  11620. // subsequent console connect event. Reset the event.
  11621. //
  11622. // NOTE - we must reset the ghActiveConsoleSessionEvent here, after
  11623. // calling IsConsoleSession above, which waits on it, else we will
  11624. // deadlock out service's control handler.
  11625. //
  11626. if (ghActiveConsoleSessionEvent) {
  11627. ResetEvent(ghActiveConsoleSessionEvent);
  11628. }
  11629. gActiveConsoleSessionId = INVALID_SESSION;
  11630. break;
  11631. case WTS_REMOTE_CONNECT:
  11632. //
  11633. // The specified session was connected remotely.
  11634. //
  11635. KdPrintEx((DPFLTR_PNPMGR_ID,
  11636. DBGF_EVENT | DBGF_INSTALL,
  11637. "UMPNPMGR: WTS_REMOTE_CONNECT: "
  11638. "SessionId %d\n",
  11639. SessionNotification->dwSessionId));
  11640. if (((ULONG)SessionNotification->dwSessionId == MAIN_SESSION) &&
  11641. (IsUserLoggedOnSession((ULONG)SessionNotification->dwSessionId)) &&
  11642. (!IsFastUserSwitchingEnabled())) {
  11643. //
  11644. // If the remote session that was just connected from the "Console"
  11645. // has a logged on user, signal the "logged on" event.
  11646. //
  11647. if (InstallEvents[LOGGED_ON_EVENT]) {
  11648. SetEvent(InstallEvents[LOGGED_ON_EVENT]);
  11649. KdPrintEx((DPFLTR_PNPMGR_ID,
  11650. DBGF_EVENT | DBGF_INSTALL,
  11651. "UMPNPMGR: WTS_REMOTE_CONNECT: "
  11652. "SetEvent LOGGED_ON_EVENT\n"));
  11653. }
  11654. }
  11655. break;
  11656. case WTS_REMOTE_DISCONNECT:
  11657. //
  11658. // The specified session was disconnected remotely.
  11659. //
  11660. KdPrintEx((DPFLTR_PNPMGR_ID,
  11661. DBGF_EVENT | DBGF_INSTALL,
  11662. "UMPNPMGR: WTS_REMOTE_DISCONNECT: "
  11663. "SessionId %d\n",
  11664. SessionNotification->dwSessionId));
  11665. if (((ULONG)SessionNotification->dwSessionId == MAIN_SESSION) &&
  11666. (IsUserLoggedOnSession((ULONG)SessionNotification->dwSessionId)) &&
  11667. (!IsFastUserSwitchingEnabled())) {
  11668. //
  11669. // If the remote session that was disconnected from the "Console"
  11670. // has a logged on user, reset the "logged on" event.
  11671. //
  11672. if (InstallEvents[LOGGED_ON_EVENT]) {
  11673. ResetEvent(InstallEvents[LOGGED_ON_EVENT]);
  11674. KdPrintEx((DPFLTR_PNPMGR_ID,
  11675. DBGF_EVENT | DBGF_INSTALL,
  11676. "UMPNPMGR: WTS_REMOTE_DISCONNECT: "
  11677. "ResetEvent LOGGED_ON_EVENT\n"));
  11678. }
  11679. //
  11680. // Since this remote session is being treated as the console,
  11681. // only do something with a device install client if it's
  11682. // behavior was NOT specifically designated for the console
  11683. // (i.e. - it was put on this session because it was the active
  11684. // console session at the time).
  11685. //
  11686. LockNotifyList(&InstallClientList.Lock);
  11687. pDeviceInstallClient = LocateDeviceInstallClient((ULONG)SessionNotification->dwSessionId);
  11688. if ((pDeviceInstallClient) &&
  11689. ((pDeviceInstallClient->ulInstallFlags & DEVICE_INSTALL_DISPLAY_ON_CONSOLE) == 0)) {
  11690. if (pDeviceInstallClient->ulInstallFlags & DEVICE_INSTALL_UI_ONLY) {
  11691. //
  11692. // If it was just for UI only, dereference it to make it
  11693. // go away when it's no longer in use.
  11694. //
  11695. DereferenceDeviceInstallClient(pDeviceInstallClient);
  11696. } else {
  11697. //
  11698. // Otherwise, it is a legitimate client-side
  11699. // installation in progress, so just disconnect from it.
  11700. // This does not remove a reference because we want it
  11701. // to stay around in case the session is reconnected to
  11702. // and the device still needs to be installed, - or
  11703. // until we find out that there are no more devices to
  11704. // install, in which case we'll close it.
  11705. //
  11706. DisconnectDeviceInstallClient(pDeviceInstallClient);
  11707. }
  11708. }
  11709. UnlockNotifyList(&InstallClientList.Lock);
  11710. }
  11711. break;
  11712. case WTS_SESSION_UNLOCK:
  11713. //
  11714. // The interactive windowstation on the specified session was unlocked.
  11715. //
  11716. KdPrintEx((DPFLTR_PNPMGR_ID,
  11717. DBGF_EVENT | DBGF_INSTALL,
  11718. "UMPNPMGR: WTS_SESSION_UNLOCK: "
  11719. "SessionId %d\n",
  11720. SessionNotification->dwSessionId));
  11721. if (SessionNotification->dwSessionId == MAIN_SESSION) {
  11722. //
  11723. // For the main session, Terminal Services may or may not be
  11724. // available, so we keep track of this state ourselves.
  11725. //
  11726. gbMainSessionLocked = FALSE;
  11727. }
  11728. if (IsFastUserSwitchingEnabled()) {
  11729. //
  11730. // When Fast User Switching is enabled, unlocking the windowstation
  11731. // is a return from the "Welcome" desktop, so we treat it as a
  11732. // logon ...
  11733. //
  11734. //
  11735. // If this is a logon to the "Console" session, signal the event that
  11736. // indicates a Console user is currently logged on.
  11737. //
  11738. // NOTE: we check gActiveConsoleSessionId directly here, without
  11739. // waiting on the corresponding event because this unlock may
  11740. // happen during a Console session change for another session,
  11741. // in which case we will hang here in the service control
  11742. // handler, waiting for the event to be set - and not be able to
  11743. // receive the service control that actually lets us set the
  11744. // event!!! Synchronization is not so important here because we
  11745. // are not using the session for anything, just comparing
  11746. // against it. If a session change really is in progress, this
  11747. // session can't be the Console session anyways.
  11748. //
  11749. // Also, since Fast User Switching is enabled, we can just
  11750. // compare against the active Console session id, and not bother
  11751. // with the session 0 thing.
  11752. //
  11753. if (SessionNotification->dwSessionId == gActiveConsoleSessionId) {
  11754. if (InstallEvents[LOGGED_ON_EVENT]) {
  11755. SetEvent(InstallEvents[LOGGED_ON_EVENT]);
  11756. KdPrintEx((DPFLTR_PNPMGR_ID,
  11757. DBGF_EVENT | DBGF_INSTALL,
  11758. "UMPNPMGR: WTS_SESSION_UNLOCK with FUS: "
  11759. "SetEvent LOGGED_ON_EVENT\n"));
  11760. }
  11761. }
  11762. } else {
  11763. //
  11764. // When Fast User Switching is not enabled, we don't do anything
  11765. // special when the winstation is unlocked.
  11766. //
  11767. // No-FUS, no-muss.
  11768. NOTHING;
  11769. }
  11770. break;
  11771. case WTS_SESSION_LOGON:
  11772. //
  11773. // NTRAID #181685-2000/09/11-jamesca:
  11774. //
  11775. // Currently, terminal services sends notification of logons to
  11776. // "remote" sessions before the server's process creation thread
  11777. // is running. If we set the logged on event, and there are
  11778. // devices waiting to be installed, we will immediately call
  11779. // CreateProcessAsUser on that session, which will fail. As a
  11780. // (temporary?) workaround, we'll continue to use PNP_ReportLogOn
  11781. // to receive logon notification from userinit.exe, now for all
  11782. // sessions.
  11783. //
  11784. break;
  11785. case WTS_SESSION_LOCK:
  11786. //
  11787. // The interactive windowstation on the specified session was locked.
  11788. //
  11789. KdPrintEx((DPFLTR_PNPMGR_ID,
  11790. DBGF_EVENT | DBGF_INSTALL,
  11791. "UMPNPMGR: WTS_SESSION_LOCK: "
  11792. "SessionId %d\n",
  11793. SessionNotification->dwSessionId));
  11794. if (SessionNotification->dwSessionId == MAIN_SESSION) {
  11795. //
  11796. // For the main session, Terminal Services may or may not be
  11797. // available, so we keep track of this state ourselves.
  11798. //
  11799. gbMainSessionLocked = TRUE;
  11800. }
  11801. if (IsFastUserSwitchingEnabled()) {
  11802. //
  11803. // When Fast User Switching is enabled, locking the windowstation
  11804. // displays the "Welcome" desktop, potentially allowing a different
  11805. // user to logon, so we treat it as a logoff ...
  11806. //
  11807. //
  11808. // If this is a "logoff" from the "Console" session, reset the event
  11809. // that indicates a Console user is currently logged on.
  11810. //
  11811. //
  11812. // NOTE: we check gActiveConsoleSessionId directly here, without
  11813. // waiting on the corresponding event because this lock may
  11814. // happen during a Console session change for another session,
  11815. // in which case we will hang here in the service control
  11816. // handler, waiting for the event to be set - and not be able to
  11817. // receive the service control that actually lets us set the
  11818. // event!!! Synchronization is not so important here because we
  11819. // are not using the session for anything, just comparing
  11820. // against it. If a session change really is in progress, this
  11821. // session can't be the Console session anyways.
  11822. //
  11823. // Also, since Fast User Switching is enabled, we can just
  11824. // compare against the active Console session id, and not bother
  11825. // with the session 0 thing.
  11826. //
  11827. if (SessionNotification->dwSessionId == gActiveConsoleSessionId) {
  11828. if (InstallEvents[LOGGED_ON_EVENT]) {
  11829. ResetEvent(InstallEvents[LOGGED_ON_EVENT]);
  11830. KdPrintEx((DPFLTR_PNPMGR_ID,
  11831. DBGF_EVENT | DBGF_INSTALL,
  11832. "UMPNPMGR: WTS_SESSION_LOCK with FUS: "
  11833. "ResetEvent LOGGED_ON_EVENT\n"));
  11834. }
  11835. }
  11836. } else {
  11837. //
  11838. // When Fast User Switching is not enabled, we don't do anything
  11839. // special when the winstation is locked.
  11840. //
  11841. // No-FUS, no-muss.
  11842. NOTHING;
  11843. }
  11844. break;
  11845. case WTS_SESSION_LOGOFF:
  11846. //
  11847. // A user logged off from the specified session.
  11848. //
  11849. KdPrintEx((DPFLTR_PNPMGR_ID,
  11850. DBGF_EVENT | DBGF_INSTALL,
  11851. "UMPNPMGR: WTS_SESSION_LOGOFF: "
  11852. "SessionId %d\n",
  11853. SessionNotification->dwSessionId));
  11854. if (((ULONG)SessionNotification->dwSessionId != MAIN_SESSION) &&
  11855. ((ULONG)SessionNotification->dwSessionId == gActiveConsoleSessionId)) {
  11856. //
  11857. // If the logoff occurred on the Console session (but not
  11858. // session 0), reset the "logged on" event.
  11859. // Session 0 logoffs are still handled by PnpConsoleCtrlHandler.
  11860. //
  11861. if (InstallEvents[LOGGED_ON_EVENT]) {
  11862. ResetEvent(InstallEvents[LOGGED_ON_EVENT]);
  11863. KdPrintEx((DPFLTR_PNPMGR_ID,
  11864. DBGF_EVENT | DBGF_INSTALL,
  11865. "UMPNPMGR: WTS_SESSION_LOGOFF: "
  11866. "ResetEvent LOGGED_ON_EVENT\n",
  11867. SessionNotification->dwSessionId));
  11868. }
  11869. //
  11870. // If we currently have a device install UI client on this session,
  11871. // we should attempt to close it now, before logging off.
  11872. //
  11873. LockNotifyList(&InstallClientList.Lock);
  11874. pDeviceInstallClient = LocateDeviceInstallClient((ULONG)SessionNotification->dwSessionId);
  11875. if (pDeviceInstallClient) {
  11876. DereferenceDeviceInstallClient(pDeviceInstallClient);
  11877. }
  11878. UnlockNotifyList(&InstallClientList.Lock);
  11879. }
  11880. break;
  11881. default:
  11882. //
  11883. // Unrecognized session change notification event.
  11884. //
  11885. KdPrintEx((DPFLTR_PNPMGR_ID,
  11886. DBGF_EVENT | DBGF_INSTALL | DBGF_ERRORS,
  11887. "UMPNPMGR: Unknown SERVICE_CONTROL_SESSIONCHANGE event type (%d) "
  11888. "received for SessionId %d!!\n",
  11889. EventType,
  11890. SessionNotification->dwSessionId));
  11891. break;
  11892. }
  11893. return NO_ERROR;
  11894. } // SessionNotificationHandler
  11895. BOOL
  11896. IsUserLoggedOnSession(
  11897. IN ULONG ulSessionId
  11898. )
  11899. /*++
  11900. Routine Description:
  11901. Checks to see if a user is logged on to the specified session.
  11902. Arguments:
  11903. ulSessionId - The session to be checked.
  11904. Return Value:
  11905. Returns TRUE if a user is currently logged on to the specified session,
  11906. FALSE otherwise.
  11907. --*/
  11908. {
  11909. BOOL bResult = FALSE;
  11910. LPWSTR pszUserName;
  11911. DWORD dwSize;
  11912. if (ulSessionId == MAIN_SESSION) {
  11913. //
  11914. // For the main session, Terminal Services may or may not be available,
  11915. // so we just check if we currently have a handle to the user token.
  11916. //
  11917. ASSERT(gTokenLock.LockHandles);
  11918. LockPrivateResource(&gTokenLock);
  11919. if (ghUserToken != NULL) {
  11920. bResult = TRUE;
  11921. }
  11922. UnlockPrivateResource(&gTokenLock);
  11923. } else {
  11924. //
  11925. // If the specified session is not the main session,
  11926. // query the session information to see if there is already a
  11927. // user logged on.
  11928. //
  11929. if (fpWTSQuerySessionInformation && fpWTSFreeMemory) {
  11930. pszUserName = NULL;
  11931. dwSize = 0;
  11932. if (fpWTSQuerySessionInformation((HANDLE)WTS_CURRENT_SERVER_HANDLE,
  11933. (DWORD)ulSessionId,
  11934. (WTS_INFO_CLASS)WTSUserName,
  11935. (LPWSTR*)&pszUserName,
  11936. &dwSize)) {
  11937. if ((pszUserName != NULL) && (lstrlen(pszUserName) != 0)) {
  11938. bResult = TRUE;
  11939. }
  11940. //
  11941. // Free the supplied buffer
  11942. //
  11943. if (pszUserName) {
  11944. fpWTSFreeMemory((PVOID)pszUserName);
  11945. }
  11946. } else {
  11947. KdPrintEx((DPFLTR_PNPMGR_ID,
  11948. DBGF_WARNINGS,
  11949. "UMPNPMGR: WTSQuerySessionInformation failed for SessionId %d, "
  11950. "error = %d\n",
  11951. ulSessionId, GetLastError()));
  11952. }
  11953. }
  11954. }
  11955. return bResult;
  11956. } // IsUserLoggedOnSession
  11957. BOOL
  11958. IsSessionConnected(
  11959. IN ULONG ulSessionId
  11960. )
  11961. /*++
  11962. Routine Description:
  11963. Checks if the specified session is connected.
  11964. Arguments:
  11965. ulSessionId - The session to be checked.
  11966. Return Value:
  11967. Returns TRUE if the specified session is currently connected, FALSE
  11968. otherwise.
  11969. Notes:
  11970. This routine assumes that the specified session is connected, unless we can
  11971. poitively determine that it is not. i.e., if Terminal Services are not
  11972. available, it is assumed that the specified session is connected.
  11973. --*/
  11974. {
  11975. BOOL bResult = TRUE;
  11976. LPWSTR pBuffer;
  11977. DWORD dwSize;
  11978. //
  11979. // Query the specified session.
  11980. //
  11981. if (fpWTSQuerySessionInformation && fpWTSFreeMemory) {
  11982. pBuffer = NULL;
  11983. dwSize = 0;
  11984. if (fpWTSQuerySessionInformation((HANDLE)WTS_CURRENT_SERVER_HANDLE,
  11985. (DWORD)ulSessionId,
  11986. (WTS_INFO_CLASS)WTSConnectState,
  11987. (LPWSTR*)&pBuffer,
  11988. &dwSize)) {
  11989. //
  11990. // The session state must be either Active or Connected.
  11991. //
  11992. if ((pBuffer == NULL) ||
  11993. ((((INT)*pBuffer) != WTSActive) &&
  11994. (((INT)*pBuffer) != WTSConnected))) {
  11995. //
  11996. // The specified session is not currently connected.
  11997. //
  11998. bResult = FALSE;
  11999. }
  12000. //
  12001. // Free the supplied buffer
  12002. //
  12003. if (pBuffer) {
  12004. fpWTSFreeMemory((PVOID)pBuffer);
  12005. }
  12006. }
  12007. } else {
  12008. //
  12009. // If the above TS entrypoints are not set, terminal services is not
  12010. // enabled. This must be session 0, and it must be connected.
  12011. //
  12012. ASSERT(ulSessionId == MAIN_SESSION);
  12013. }
  12014. return bResult;
  12015. } // IsSessionConnected
  12016. BOOL
  12017. IsSessionLocked(
  12018. IN ULONG ulSessionId
  12019. )
  12020. /*++
  12021. Routine Description:
  12022. Checks to see if the interactive windowstation for the specified session is
  12023. locked.
  12024. Arguments:
  12025. ulSessionId - The session to be checked.
  12026. Return Value:
  12027. Returns TRUE if the interactive windowstation for the specified session is
  12028. locked, FALSE otherwise.
  12029. --*/
  12030. {
  12031. BOOL bLocked = FALSE;
  12032. DWORD dwReturnLength;
  12033. if (ulSessionId == MAIN_SESSION) {
  12034. //
  12035. // For the main session, Terminal Services may or may not be available,
  12036. // so we just check our internal state variable.
  12037. //
  12038. bLocked = gbMainSessionLocked;
  12039. } else {
  12040. //
  12041. // If the specified session is not the main session, query Terminal
  12042. // Services for that session's WinStation information.
  12043. //
  12044. try {
  12045. if (!fpWinStationQueryInformationW(SERVERNAME_CURRENT,
  12046. ulSessionId,
  12047. WinStationLockedState,
  12048. (PVOID)&bLocked,
  12049. sizeof(bLocked),
  12050. &dwReturnLength)) {
  12051. bLocked = FALSE;
  12052. KdPrintEx((DPFLTR_PNPMGR_ID,
  12053. DBGF_WARNINGS,
  12054. "UMPNPMGR: WinStationQueryInformation failed for SessionId %d, "
  12055. "error = %d\n",
  12056. ulSessionId, GetLastError()));
  12057. }
  12058. } except(EXCEPTION_EXECUTE_HANDLER) {
  12059. bLocked = FALSE;
  12060. }
  12061. }
  12062. return bLocked;
  12063. } // IsSessionLocked
  12064. BOOL
  12065. IsConsoleSession(
  12066. IN ULONG ulSessionId
  12067. )
  12068. /*++
  12069. Routine Description:
  12070. Checks to see if the specified session is the "Console" session.
  12071. When Terminal Services Fast User Switching is enabled, this means that the
  12072. session is the session connected to the physical display. When Fast User
  12073. Switching is disabled, this means that the session is Session 0.
  12074. Arguments:
  12075. ulSessionId - The session to be checked.
  12076. Return Value:
  12077. Returns TRUE if the specified session should currently be considered the
  12078. "Console" session.
  12079. Notes:
  12080. Note that this routine may potentially wait in GetActiveConsoleSessionId(),
  12081. on the event we use to guard access to the active console session. Because
  12082. of that, this routine should not be called in cases where it prevents a
  12083. console connect or console disconnect from taking place, unless the event is
  12084. known to be set appropriately.
  12085. --*/
  12086. {
  12087. BOOL bFusEnabled;
  12088. bFusEnabled = IsFastUserSwitchingEnabled();
  12089. if ((!bFusEnabled && (ulSessionId == MAIN_SESSION)) ||
  12090. ( bFusEnabled && (ulSessionId == GetActiveConsoleSessionId()))) {
  12091. return TRUE;
  12092. } else {
  12093. return FALSE;
  12094. }
  12095. } // IsConsoleSession
  12096. ULONG
  12097. GetActiveConsoleSessionId(
  12098. VOID
  12099. )
  12100. /*++
  12101. Routine Description:
  12102. This routine returns the session id for the current active Console session.
  12103. If a Console session switch event is in progress, it will wait until it is
  12104. complete before returning.
  12105. Arguments:
  12106. None.
  12107. Return Value:
  12108. Session Id of the current active Console session.
  12109. --*/
  12110. {
  12111. ULONG ulConsoleSessionId;
  12112. DWORD dwWait;
  12113. ASSERT(ghActiveConsoleSessionEvent != NULL);
  12114. //
  12115. // If we have nothing to wait on, just return the current state.
  12116. //
  12117. if (ghActiveConsoleSessionEvent == NULL) {
  12118. return gActiveConsoleSessionId;
  12119. }
  12120. ulConsoleSessionId = INVALID_SESSION;
  12121. while (ulConsoleSessionId == INVALID_SESSION) {
  12122. //
  12123. // Wait on the console session event until we retrieve a valid Console
  12124. // session id.
  12125. //
  12126. // We do this because a subtle race can occur when our service's control
  12127. // handler processes a Console connect, which signals the console
  12128. // session event and satisfies this wait, but then immediately processes
  12129. // a subsequent Console disconnect, resetting the event, and
  12130. // invalidating the active console session id -- BEFORE this
  12131. // wait-satisfied thread is rescheduled to run. Once rescheduled, this
  12132. // thread could end up reading an invalid value as the current active
  12133. // Console session id.
  12134. //
  12135. // In that case however, the console session event would have been reset
  12136. // already, so we can simply wait until it is signalled again, and
  12137. // return the session id of the active Console session when the
  12138. // succession of connect/disconnect requests that have been processed by
  12139. // our service's control handler handler have been synchronized with
  12140. // this waiting thread.
  12141. //
  12142. dwWait = WaitForSingleObject(ghActiveConsoleSessionEvent, INFINITE);
  12143. ASSERT(dwWait == WAIT_OBJECT_0);
  12144. ulConsoleSessionId = gActiveConsoleSessionId;
  12145. }
  12146. ASSERT(ulConsoleSessionId != INVALID_SESSION);
  12147. return ulConsoleSessionId;
  12148. } // GetActiveConsoleSessionId
  12149. BOOL
  12150. GetSessionUserToken(
  12151. IN ULONG ulSessionId,
  12152. OUT LPHANDLE lphUserToken
  12153. )
  12154. /*++
  12155. Routine Description:
  12156. This routine returns a handle to the user access token for the user at the
  12157. Console session.
  12158. Arguments:
  12159. ulSession - Specifies the session for which the interactive user's token is
  12160. to be retrieved.
  12161. lphUserToken - Specifies the address to receive the handle to the user access
  12162. token. Note that if this routine was successful, the caller is
  12163. responsible for closing this handle.
  12164. Return Value:
  12165. Returns TRUE if successful, FALSE otherwise.
  12166. --*/
  12167. {
  12168. BOOL bResult = FALSE;
  12169. HANDLE hImpersonationToken = INVALID_HANDLE_VALUE;
  12170. RPC_STATUS rpcStatus;
  12171. //
  12172. // Verify that we were supplied a location to store the user token handle.
  12173. //
  12174. if (lphUserToken == NULL) {
  12175. KdPrintEx((DPFLTR_PNPMGR_ID,
  12176. DBGF_ERRORS,
  12177. "UMPNPMGR: NULL lphUserToken supplied to GetSessionUserToken!\n"));
  12178. return FALSE;
  12179. }
  12180. if (ulSessionId == MAIN_SESSION) {
  12181. //
  12182. // A logon to session 0 can't be dependent on termsrv.exe, so we always
  12183. // cache a handle to the user access token for that session during the
  12184. // call to PNP_ReportLogon for session 0. If we currently have a handle
  12185. // to the token, return it.
  12186. //
  12187. ASSERT(gTokenLock.LockHandles);
  12188. LockPrivateResource(&gTokenLock);
  12189. if (ghUserToken) {
  12190. //
  12191. // Duplicate the handle so that the caller can always safely close
  12192. // it, no matter where it came from.
  12193. //
  12194. bResult = DuplicateHandle(GetCurrentProcess(),
  12195. ghUserToken,
  12196. GetCurrentProcess(),
  12197. lphUserToken,
  12198. 0,
  12199. TRUE,
  12200. DUPLICATE_SAME_ACCESS);
  12201. if (!bResult) {
  12202. KdPrintEx((DPFLTR_PNPMGR_ID,
  12203. DBGF_ERRORS,
  12204. "UMPNPMGR: DuplicateHandle failed for ghUserToken for SessionId %d, error = %d\n",
  12205. ulSessionId, GetLastError()));
  12206. }
  12207. } else {
  12208. //
  12209. // If we don't have a handle to a user access token for session 0,
  12210. // there is probably not any user logged on to that session.
  12211. //
  12212. bResult = FALSE;
  12213. }
  12214. UnlockPrivateResource(&gTokenLock);
  12215. } else {
  12216. //
  12217. // If the specified session is some session other than session 0,
  12218. // Terminal Services must necessarily be available. Call
  12219. // GetWinStationUserToken to retrieve a handle to the user access token
  12220. // for this session.
  12221. //
  12222. bResult = GetWinStationUserToken(ulSessionId, &hImpersonationToken);
  12223. if (bResult) {
  12224. //
  12225. // The token retrieved by GetWinStationUserToken is an impersonation
  12226. // token. CreateProcessAsUser requires a primary token, so we must
  12227. // duplicate the impersonation token to get one. Create a primary
  12228. // token with the same access rights as the original token.
  12229. //
  12230. bResult = DuplicateTokenEx(hImpersonationToken,
  12231. 0,
  12232. NULL,
  12233. SecurityImpersonation,
  12234. TokenPrimary,
  12235. lphUserToken);
  12236. //
  12237. // Close the handle to the impersonation token.
  12238. //
  12239. CloseHandle(hImpersonationToken);
  12240. if (!bResult) {
  12241. KdPrintEx((DPFLTR_PNPMGR_ID,
  12242. DBGF_ERRORS,
  12243. "UMPNPMGR: DuplicateTokenEx failed, error = %d\n",
  12244. GetLastError()));
  12245. }
  12246. } else {
  12247. //
  12248. // Find out what the problem was.
  12249. //
  12250. rpcStatus = GetLastError();
  12251. if (rpcStatus == RPC_S_INVALID_BINDING) {
  12252. //
  12253. // This is some error related to the service not being
  12254. // available. Since we only call this for sessions other than
  12255. // the main session, termsrv should definitely be available.
  12256. //
  12257. KdPrintEx((DPFLTR_PNPMGR_ID,
  12258. DBGF_ERRORS,
  12259. "UMPNPMGR: GetWinStationUserToken returned error = %d for SessionId %d!!\n",
  12260. rpcStatus, ulSessionId));
  12261. ASSERT(FALSE);
  12262. } else {
  12263. //
  12264. // Some other error, the service may never be avaiable so bail
  12265. // out now.
  12266. //
  12267. KdPrintEx((DPFLTR_PNPMGR_ID,
  12268. DBGF_WARNINGS,
  12269. "UMPNPMGR: GetWinStationUserToken failed for SessionId %d, error = %d\n",
  12270. ulSessionId, rpcStatus));
  12271. }
  12272. }
  12273. }
  12274. //
  12275. // If successful, we should always be returning a valid handle.
  12276. //
  12277. ASSERT(!bResult || ((*lphUserToken != INVALID_HANDLE_VALUE) && (*lphUserToken != NULL)));
  12278. return bResult;
  12279. } // GetSessionUserToken
  12280. DWORD
  12281. CreateUserSynchEvent(
  12282. IN HANDLE hUserToken,
  12283. IN LPCWSTR lpName,
  12284. OUT HANDLE *phEvent
  12285. )
  12286. /*++
  12287. Routine Description:
  12288. This routine creates an event that the specified user can synchronize with.
  12289. This is used so that we can communicate with NewDev and HotPlug processes
  12290. running in the user's context.
  12291. Arguments:
  12292. hUserToken - Specifies a handle to the user access token for whom the event
  12293. will be created.
  12294. lpName - Name of event to create.
  12295. phEvent - Supplies the address of a variable that will receive a handle
  12296. to the event.
  12297. Return Value:
  12298. If successful, the return value is NO_ERROR. If failure, the return value
  12299. is a Win32 error code indicating the cause of failure.
  12300. --*/
  12301. {
  12302. DWORD Err = ERROR_SUCCESS;
  12303. PSID pUserSid = NULL;
  12304. PACL pDacl = NULL;
  12305. ULONG ulAclSize;
  12306. SECURITY_DESCRIPTOR sd;
  12307. SECURITY_ATTRIBUTES sa;
  12308. //
  12309. // Retrieve the User SID
  12310. //
  12311. pUserSid =
  12312. GetUserSid(hUserToken);
  12313. if (pUserSid == NULL) {
  12314. Err = GetLastError();
  12315. goto Clean0;
  12316. }
  12317. ASSERT(IsValidSid(pUserSid));
  12318. //
  12319. // Use the LocalSystem SID provided in the SCM global data.
  12320. //
  12321. ASSERT(PnPGlobalData != NULL);
  12322. ASSERT(IsValidSid(PnPGlobalData->LocalSystemSid));
  12323. //
  12324. // Determine the size required for the DACL
  12325. //
  12326. ulAclSize = sizeof(ACL);
  12327. ulAclSize += sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(pUserSid) - sizeof(DWORD);
  12328. ulAclSize += sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(PnPGlobalData->LocalSystemSid) - sizeof(DWORD);
  12329. //
  12330. // Allocate and initialize the DACL
  12331. //
  12332. pDacl =
  12333. (PACL)HeapAlloc(
  12334. ghPnPHeap, 0, ulAclSize);
  12335. if (pDacl == NULL) {
  12336. Err = ERROR_NOT_ENOUGH_MEMORY;
  12337. goto Clean0;
  12338. }
  12339. if (!InitializeAcl(pDacl, ulAclSize, ACL_REVISION)) {
  12340. Err = GetLastError();
  12341. goto Clean0;
  12342. }
  12343. //
  12344. // Add an ACE to the DACL for LocalSystem EVENT_ALL_ACCESS
  12345. //
  12346. if (!AddAccessAllowedAceEx(
  12347. pDacl,
  12348. ACL_REVISION,
  12349. 0,
  12350. EVENT_ALL_ACCESS,
  12351. PnPGlobalData->LocalSystemSid)) {
  12352. Err = GetLastError();
  12353. goto Clean0;
  12354. }
  12355. //
  12356. // Add an ACE to the DACL for User EVENT_QUERY_STATE, EVENT_MODIFY_STATE, and SYNCHRONIZE
  12357. //
  12358. if (!AddAccessAllowedAceEx(
  12359. pDacl,
  12360. ACL_REVISION,
  12361. 0,
  12362. EVENT_QUERY_STATE | EVENT_MODIFY_STATE | SYNCHRONIZE,
  12363. pUserSid)) {
  12364. Err = GetLastError();
  12365. goto Clean0;
  12366. }
  12367. ASSERT(IsValidAcl(pDacl));
  12368. //
  12369. // Allocate and initialize the security descriptor
  12370. //
  12371. if (!InitializeSecurityDescriptor(
  12372. &sd, SECURITY_DESCRIPTOR_REVISION)) {
  12373. Err = GetLastError();
  12374. goto Clean0;
  12375. }
  12376. //
  12377. // Set the new DACL in the security descriptor
  12378. //
  12379. if (!SetSecurityDescriptorDacl(
  12380. &sd, TRUE, pDacl, FALSE)) {
  12381. Err = GetLastError();
  12382. goto Clean0;
  12383. }
  12384. ASSERT(IsValidSecurityDescriptor(&sd));
  12385. //
  12386. // Add the security descriptor to the security attributes
  12387. //
  12388. sa.nLength = sizeof(sa);
  12389. sa.lpSecurityDescriptor = &sd;
  12390. sa.bInheritHandle = FALSE;
  12391. //
  12392. // Create the manual-reset event with a nonsignaled initial state.
  12393. //
  12394. *phEvent = CreateEvent(&sa, TRUE, FALSE, lpName);
  12395. if (*phEvent == NULL) {
  12396. Err = GetLastError();
  12397. goto Clean0;
  12398. }
  12399. //
  12400. // Check that the named event did not already exist.
  12401. //
  12402. ASSERT(GetLastError() != ERROR_ALREADY_EXISTS);
  12403. if (GetLastError() == ERROR_ALREADY_EXISTS) {
  12404. Err = ERROR_ALREADY_EXISTS;
  12405. CloseHandle(*phEvent);
  12406. *phEvent = NULL;
  12407. goto Clean0;
  12408. }
  12409. Clean0:
  12410. //
  12411. // Cleanup.
  12412. //
  12413. if (pUserSid != NULL) {
  12414. HeapFree(ghPnPHeap, 0, pUserSid);
  12415. }
  12416. if (pDacl != NULL) {
  12417. HeapFree(ghPnPHeap, 0, pDacl);
  12418. }
  12419. return Err;
  12420. } // CreateUserSynchEvent
  12421. BOOL
  12422. CreateNoPendingInstallEvent(
  12423. VOID
  12424. )
  12425. /*++
  12426. Routine Description:
  12427. This routine creates the "PnP_No_Pending_Install_Events" global named event,
  12428. which is set and reset by the UMPNPMGR ThreadProc_DeviceInstall server-side
  12429. device install thread, and waited on by the CMP_WaitNoPendingInstalls
  12430. CFGMGR32 API, which allows clients to synchronize with the event directly,
  12431. to determine when PNP is done actively installing any devices.
  12432. Arguments:
  12433. None.
  12434. Return Value:
  12435. Returns TRUE if successful, FALSE otherwise.
  12436. --*/
  12437. {
  12438. DWORD Err = NO_ERROR;
  12439. PACL pDacl = NULL;
  12440. ULONG ulAclSize;
  12441. SECURITY_DESCRIPTOR sd;
  12442. SECURITY_ATTRIBUTES sa;
  12443. //
  12444. // Use the SIDs provided in the SCM global data. This routine is called
  12445. // from our initialization thread, which is created during our service start
  12446. // routine, so the SCM provided global data is available to us by now.
  12447. //
  12448. ASSERT(PnPGlobalData != NULL);
  12449. ASSERT(IsValidSid(PnPGlobalData->LocalSystemSid));
  12450. ASSERT(IsValidSid(PnPGlobalData->AliasAdminsSid));
  12451. ASSERT(IsValidSid(PnPGlobalData->AliasUsersSid));
  12452. //
  12453. // Determine the size required for the DACL
  12454. //
  12455. ulAclSize = sizeof(ACL);
  12456. ulAclSize += sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(PnPGlobalData->LocalSystemSid) - sizeof(DWORD);
  12457. ulAclSize += sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(PnPGlobalData->AliasAdminsSid) - sizeof(DWORD);
  12458. ulAclSize += sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(PnPGlobalData->AliasUsersSid) - sizeof(DWORD);
  12459. //
  12460. // Allocate and initialize the DACL
  12461. //
  12462. pDacl =
  12463. (PACL)HeapAlloc(
  12464. ghPnPHeap, 0, ulAclSize);
  12465. if (pDacl == NULL) {
  12466. Err = ERROR_NOT_ENOUGH_MEMORY;
  12467. goto Clean0;
  12468. }
  12469. if (!InitializeAcl(pDacl, ulAclSize, ACL_REVISION)) {
  12470. Err = GetLastError();
  12471. goto Clean0;
  12472. }
  12473. //
  12474. // Add an ACE to the DACL for LocalSystem EVENT_ALL_ACCESS
  12475. //
  12476. if (!AddAccessAllowedAceEx(
  12477. pDacl,
  12478. ACL_REVISION,
  12479. 0,
  12480. EVENT_ALL_ACCESS,
  12481. PnPGlobalData->LocalSystemSid)) {
  12482. Err = GetLastError();
  12483. goto Clean0;
  12484. }
  12485. //
  12486. // Add an ACE to the DACL for Administrators EVENT_QUERY_STATE and SYNCHRONIZE
  12487. //
  12488. if (!AddAccessAllowedAceEx(
  12489. pDacl,
  12490. ACL_REVISION,
  12491. 0,
  12492. EVENT_QUERY_STATE | SYNCHRONIZE,
  12493. PnPGlobalData->AliasAdminsSid)) {
  12494. Err = GetLastError();
  12495. goto Clean0;
  12496. }
  12497. //
  12498. // Add an ACE to the DACL for Users EVENT_QUERY_STATE and SYNCHRONIZE
  12499. //
  12500. if (!AddAccessAllowedAceEx(
  12501. pDacl,
  12502. ACL_REVISION,
  12503. 0,
  12504. EVENT_QUERY_STATE | SYNCHRONIZE,
  12505. PnPGlobalData->AliasUsersSid)) {
  12506. Err = GetLastError();
  12507. goto Clean0;
  12508. }
  12509. ASSERT(IsValidAcl(pDacl));
  12510. //
  12511. // Allocate and initialize the security descriptor
  12512. //
  12513. if (!InitializeSecurityDescriptor(
  12514. &sd, SECURITY_DESCRIPTOR_REVISION)) {
  12515. Err = GetLastError();
  12516. goto Clean0;
  12517. }
  12518. //
  12519. // Set the new DACL in the security descriptor
  12520. //
  12521. if (!SetSecurityDescriptorDacl(
  12522. &sd, TRUE, pDacl, FALSE)) {
  12523. Err = GetLastError();
  12524. goto Clean0;
  12525. }
  12526. ASSERT(IsValidSecurityDescriptor(&sd));
  12527. //
  12528. // Add the security descriptor to the security attributes
  12529. //
  12530. sa.nLength = sizeof(sa);
  12531. sa.lpSecurityDescriptor = &sd;
  12532. sa.bInheritHandle = FALSE;
  12533. //
  12534. // Create the manual-reset event with a nonsignaled initial state.
  12535. //
  12536. ghNoPendingInstalls =
  12537. CreateEvent(&sa, TRUE, FALSE, PNP_NO_INSTALL_EVENTS);
  12538. if (ghNoPendingInstalls == NULL) {
  12539. Err = GetLastError();
  12540. goto Clean0;
  12541. }
  12542. //
  12543. // Check that the named event did not already exist.
  12544. //
  12545. ASSERT(GetLastError() != ERROR_ALREADY_EXISTS);
  12546. if (GetLastError() == ERROR_ALREADY_EXISTS) {
  12547. Err = ERROR_ALREADY_EXISTS;
  12548. CloseHandle(ghNoPendingInstalls);
  12549. ghNoPendingInstalls = NULL;
  12550. goto Clean0;
  12551. }
  12552. Clean0:
  12553. //
  12554. // Cleanup.
  12555. //
  12556. if (pDacl != NULL) {
  12557. HeapFree(ghPnPHeap, 0, pDacl);
  12558. }
  12559. SetLastError(Err);
  12560. return(Err == NO_ERROR);
  12561. } // CreateNoPendingInstallEvent
  12562. DWORD
  12563. CreateUserReadNamedPipe(
  12564. IN HANDLE hUserToken,
  12565. IN LPCWSTR lpName,
  12566. IN ULONG ulSize,
  12567. OUT HANDLE *phPipe
  12568. )
  12569. /*++
  12570. Routine Description:
  12571. This routine creates a named pipe that the specified user can read from.
  12572. This is used so that we can communicate with NewDev and HotPlug processes
  12573. running in the user's context.
  12574. Arguments:
  12575. hUserToken - Specifies a handle to the user access token for whom the named
  12576. pipe will be created.
  12577. lpName - Name of pipe to create.
  12578. ulSize - Specifies the size of the output buffer for the named pipe.
  12579. phPipe - Supplies the address of a variable that will receive a handle
  12580. to the pipe.
  12581. Return Value:
  12582. If successful, the return value is NO_ERROR. If failure, the return value
  12583. is a Win32 error code indicating the cause of failure.
  12584. --*/
  12585. {
  12586. DWORD Err = ERROR_SUCCESS;
  12587. PSID pUserSid = NULL;
  12588. PACL pDacl = NULL;
  12589. ULONG ulAclSize;
  12590. SECURITY_DESCRIPTOR sd;
  12591. SECURITY_ATTRIBUTES sa;
  12592. //
  12593. // Retrieve the User SID
  12594. //
  12595. pUserSid =
  12596. GetUserSid(hUserToken);
  12597. if (pUserSid == NULL) {
  12598. Err = GetLastError();
  12599. goto Clean0;
  12600. }
  12601. ASSERT(IsValidSid(pUserSid));
  12602. //
  12603. // Use the LocalSystem SID provided in the SCM global data.
  12604. //
  12605. ASSERT(PnPGlobalData != NULL);
  12606. ASSERT(IsValidSid(PnPGlobalData->LocalSystemSid));
  12607. //
  12608. // Determine the size required for the DACL
  12609. //
  12610. ulAclSize = sizeof(ACL);
  12611. ulAclSize += sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(pUserSid) - sizeof(DWORD);
  12612. ulAclSize += sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(PnPGlobalData->LocalSystemSid) - sizeof(DWORD);
  12613. //
  12614. // Allocate and initialize the DACL
  12615. //
  12616. pDacl =
  12617. (PACL)HeapAlloc(
  12618. ghPnPHeap, 0, ulAclSize);
  12619. if (pDacl == NULL) {
  12620. Err = ERROR_NOT_ENOUGH_MEMORY;
  12621. goto Clean0;
  12622. }
  12623. if (!InitializeAcl(pDacl, ulAclSize, ACL_REVISION)) {
  12624. Err = GetLastError();
  12625. goto Clean0;
  12626. }
  12627. //
  12628. // Add an ACE to the DACL for LocalSystem FILE_ALL_ACCESS
  12629. //
  12630. if (!AddAccessAllowedAceEx(
  12631. pDacl,
  12632. ACL_REVISION,
  12633. 0,
  12634. FILE_ALL_ACCESS,
  12635. PnPGlobalData->LocalSystemSid)) {
  12636. Err = GetLastError();
  12637. goto Clean0;
  12638. }
  12639. //
  12640. // Add an ACE to the DACL for User FILE_GENERIC_READ
  12641. //
  12642. if (!AddAccessAllowedAceEx(
  12643. pDacl,
  12644. ACL_REVISION,
  12645. 0,
  12646. FILE_GENERIC_READ,
  12647. pUserSid)) {
  12648. Err = GetLastError();
  12649. goto Clean0;
  12650. }
  12651. ASSERT(IsValidAcl(pDacl));
  12652. //
  12653. // Allocate and initialize the security descriptor
  12654. //
  12655. if (!InitializeSecurityDescriptor(
  12656. &sd, SECURITY_DESCRIPTOR_REVISION)) {
  12657. Err = GetLastError();
  12658. goto Clean0;
  12659. }
  12660. //
  12661. // Set the new DACL in the security descriptor
  12662. //
  12663. if (!SetSecurityDescriptorDacl(
  12664. &sd, TRUE, pDacl, FALSE)) {
  12665. Err = GetLastError();
  12666. goto Clean0;
  12667. }
  12668. ASSERT(IsValidSecurityDescriptor(&sd));
  12669. //
  12670. // Add the security descriptor to the security attributes
  12671. //
  12672. sa.nLength = sizeof(sa);
  12673. sa.lpSecurityDescriptor = &sd;
  12674. sa.bInheritHandle = FALSE;
  12675. //
  12676. // Create the named pipe.
  12677. //
  12678. *phPipe =
  12679. CreateNamedPipe(
  12680. lpName,
  12681. PIPE_ACCESS_OUTBOUND | // outbound data only
  12682. FILE_FLAG_OVERLAPPED | // use overlapped structure
  12683. FILE_FLAG_FIRST_PIPE_INSTANCE, // make sure we are the creator of the pipe
  12684. PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
  12685. 1, // only one instance is allowed, and we are its creator
  12686. ulSize, // out buffer size
  12687. 0, // in buffer size
  12688. PNP_PIPE_TIMEOUT, // default timeout
  12689. &sa); // security attributes
  12690. if (*phPipe == INVALID_HANDLE_VALUE) {
  12691. Err = GetLastError();
  12692. *phPipe = NULL;
  12693. goto Clean0;
  12694. }
  12695. //
  12696. // Check that the named pipe did not already exist.
  12697. //
  12698. ASSERT(GetLastError() != ERROR_ALREADY_EXISTS);
  12699. if (GetLastError() == ERROR_ALREADY_EXISTS) {
  12700. Err = ERROR_ALREADY_EXISTS;
  12701. CloseHandle(*phPipe);
  12702. *phPipe = NULL;
  12703. goto Clean0;
  12704. }
  12705. Clean0:
  12706. //
  12707. // Cleanup.
  12708. //
  12709. if (pUserSid != NULL) {
  12710. HeapFree(ghPnPHeap, 0, pUserSid);
  12711. }
  12712. if (pDacl != NULL) {
  12713. HeapFree(ghPnPHeap, 0, pDacl);
  12714. }
  12715. return Err;
  12716. } // CreateUserReadNamedPipe
  12717. VOID
  12718. LogSurpriseRemovalEvent(
  12719. IN LPWSTR MultiSzList
  12720. )
  12721. /*++
  12722. Routine Description:
  12723. One or more non-SurpriseRemovalOK devices were removed without prior
  12724. warning. Record the removals in the event log.
  12725. Arguments:
  12726. MultiSz list of device instance paths.
  12727. Return Value:
  12728. None.
  12729. --*/
  12730. {
  12731. LPWSTR instancePath, friendlyName;
  12732. CONFIGRET configRet;
  12733. ULONG ulRegDataType, ulRemovalPolicy, ulVerifierFlags, ulTransferLen, ulLength;
  12734. HKEY hMmKey = NULL;
  12735. LONG lResult;
  12736. for(instancePath = MultiSzList;
  12737. ((*instancePath) != UNICODE_NULL);
  12738. instancePath += lstrlen(instancePath) + 1) {
  12739. ulTransferLen = ulLength = sizeof(ULONG);
  12740. configRet = PNP_GetDeviceRegProp(
  12741. NULL,
  12742. instancePath,
  12743. CM_DRP_REMOVAL_POLICY,
  12744. &ulRegDataType,
  12745. (LPBYTE) &ulRemovalPolicy,
  12746. &ulTransferLen,
  12747. &ulLength,
  12748. 0
  12749. );
  12750. if (configRet != CR_SUCCESS) {
  12751. continue;
  12752. }
  12753. if (ulRemovalPolicy == CM_REMOVAL_POLICY_EXPECT_SURPRISE_REMOVAL) {
  12754. //
  12755. // For devices which we expect surprise removal, we look to see if
  12756. // the verifier is enabled.
  12757. //
  12758. lResult = RegOpenKeyEx(
  12759. HKEY_LOCAL_MACHINE,
  12760. RegMemoryManagementKeyName,
  12761. 0,
  12762. KEY_QUERY_VALUE,
  12763. &hMmKey
  12764. );
  12765. if ( lResult == ERROR_SUCCESS ) {
  12766. ulLength = sizeof(ULONG);
  12767. lResult = RegQueryValueEx(
  12768. hMmKey,
  12769. RegVerifyDriverLevelValueName,
  12770. 0,
  12771. &ulRegDataType,
  12772. (LPBYTE) &ulVerifierFlags,
  12773. &ulLength
  12774. );
  12775. RegCloseKey(hMmKey);
  12776. //
  12777. // ADRIAO ISSUE 2001/02/14 -
  12778. // We don't yet have a BIOS verification flag yet, so even
  12779. // though the verifier may be targetted at a specific driver
  12780. // for a WHQL test, we will log an event log here.
  12781. //
  12782. if ((lResult != ERROR_SUCCESS) ||
  12783. (!(ulVerifierFlags & DRIVER_VERIFIER_ENHANCED_IO_CHECKING))) {
  12784. continue;
  12785. }
  12786. }
  12787. }
  12788. friendlyName = BuildFriendlyName(instancePath);
  12789. if (friendlyName) {
  12790. LogErrorEvent(
  12791. ERR_SURPRISE_REMOVAL_2,
  12792. 0,
  12793. 2,
  12794. friendlyName,
  12795. instancePath
  12796. );
  12797. HeapFree(ghPnPHeap, 0, friendlyName);
  12798. } else {
  12799. LogErrorEvent(
  12800. ERR_SURPRISE_REMOVAL_1,
  12801. 0,
  12802. 1,
  12803. instancePath
  12804. );
  12805. }
  12806. }
  12807. }
  12808. PWCHAR
  12809. BuildFriendlyName(
  12810. IN LPWSTR InstancePath
  12811. )
  12812. {
  12813. PWCHAR friendlyName;
  12814. CONFIGRET configRet;
  12815. ULONG ulLength, ulTransferLen;
  12816. WCHAR szBuffer[MAX_PATH];
  12817. ULONG ulRegDataType;
  12818. GUID classGuid;
  12819. handle_t hBinding;
  12820. hBinding = NULL;
  12821. //
  12822. // Try the registry for FRIENDLYNAME
  12823. //
  12824. ulLength = ulTransferLen = sizeof(szBuffer);
  12825. configRet = PNP_GetDeviceRegProp(
  12826. hBinding,
  12827. InstancePath,
  12828. CM_DRP_FRIENDLYNAME,
  12829. &ulRegDataType,
  12830. (LPBYTE) szBuffer,
  12831. &ulTransferLen,
  12832. &ulLength,
  12833. 0
  12834. );
  12835. if (configRet != CR_SUCCESS || !*szBuffer) {
  12836. //
  12837. // Try the registry for DEVICEDESC
  12838. //
  12839. ulLength = ulTransferLen = sizeof(szBuffer);
  12840. configRet = PNP_GetDeviceRegProp(
  12841. hBinding,
  12842. InstancePath,
  12843. CM_DRP_DEVICEDESC,
  12844. &ulRegDataType,
  12845. (LPBYTE) szBuffer,
  12846. &ulTransferLen,
  12847. &ulLength,
  12848. 0
  12849. );
  12850. if (configRet != CR_SUCCESS || !*szBuffer) {
  12851. //
  12852. // Initialize ClassGuid to GUID_NULL
  12853. //
  12854. CopyMemory(&classGuid, &GUID_NULL, sizeof(GUID));
  12855. //
  12856. // Try the registry for CLASSNAME
  12857. //
  12858. ulLength = ulTransferLen = sizeof(szBuffer);
  12859. configRet = PNP_GetDeviceRegProp(
  12860. hBinding,
  12861. InstancePath,
  12862. CM_DRP_CLASSGUID,
  12863. &ulRegDataType,
  12864. (LPBYTE) szBuffer,
  12865. &ulTransferLen,
  12866. &ulLength,
  12867. 0
  12868. );
  12869. if (configRet == CR_SUCCESS) {
  12870. GuidFromString(szBuffer, &classGuid);
  12871. }
  12872. if (!GuidEqual(&classGuid, &GUID_NULL) &&
  12873. !GuidEqual(&classGuid, &GUID_DEVCLASS_UNKNOWN)) {
  12874. ulLength = ulTransferLen = sizeof(szBuffer);
  12875. configRet = PNP_GetDeviceRegProp(
  12876. hBinding,
  12877. InstancePath,
  12878. CM_DRP_CLASS,
  12879. &ulRegDataType,
  12880. (LPBYTE) szBuffer,
  12881. &ulTransferLen,
  12882. &ulLength,
  12883. 0
  12884. );
  12885. } else {
  12886. configRet = CR_NO_SUCH_VALUE;
  12887. }
  12888. }
  12889. }
  12890. if (configRet == CR_SUCCESS && *szBuffer) {
  12891. friendlyName = HeapAlloc(ghPnPHeap, HEAP_ZERO_MEMORY, ulLength);
  12892. if (friendlyName) {
  12893. memcpy(friendlyName, szBuffer, ulLength);
  12894. }
  12895. } else {
  12896. friendlyName = NULL;
  12897. }
  12898. return friendlyName;
  12899. }
  12900. ENUM_ACTION
  12901. QueueInstallationCallback(
  12902. IN LPCWSTR DevInst,
  12903. IN OUT PVOID Context
  12904. )
  12905. /*++
  12906. Routine Description:
  12907. This routine is called back for each devnode in a given subtree. It places
  12908. each device node in that subtree into the installation queue so that it'll
  12909. be reinstalled *if* appropriate (the installation side code checked the
  12910. state of the devnode.)
  12911. Arguments:
  12912. DevInst InstancePath of current devnode.
  12913. Context A pointer to QI_CONTEXT data (needed to handle the single-level
  12914. enum case.)
  12915. Return Value:
  12916. ENUM_ACTION (Either EA_CONTINUE, EA_SKIP_SUBTREE, or EA_STOP_ENUMERATION)
  12917. --*/
  12918. {
  12919. PQI_CONTEXT pqiContext;
  12920. PPNP_INSTALL_ENTRY entry, current;
  12921. CONFIGRET status;
  12922. BOOL needsReinstall;
  12923. HRESULT hr;
  12924. pqiContext = (PQI_CONTEXT)Context;
  12925. status = DevInstNeedsInstall(DevInst, FALSE, &needsReinstall);
  12926. if (status != CR_SUCCESS) {
  12927. //
  12928. // The devnode disappeared out from under us. Skip it's subtree.
  12929. //
  12930. return EA_SKIP_SUBTREE;
  12931. }
  12932. if (needsReinstall) {
  12933. //
  12934. // This devnode needs installation. Allocate and initialize a new
  12935. // device install entry block.
  12936. //
  12937. entry = (PPNP_INSTALL_ENTRY)
  12938. HeapAlloc(
  12939. ghPnPHeap, 0,
  12940. sizeof(PNP_INSTALL_ENTRY));
  12941. if (entry == NULL) {
  12942. pqiContext->Status = CR_OUT_OF_MEMORY;
  12943. return EA_STOP_ENUMERATION;
  12944. }
  12945. hr = StringCchCopy(entry->szDeviceId,
  12946. MAX_DEVICE_ID_LEN,
  12947. DevInst);
  12948. ASSERT(SUCCEEDED(hr));
  12949. entry->Next = NULL;
  12950. entry->Flags = 0;
  12951. //
  12952. // Insert this entry in the device install list.
  12953. //
  12954. LockNotifyList(&InstallList.Lock);
  12955. current = (PPNP_INSTALL_ENTRY)InstallList.Next;
  12956. if (current == NULL) {
  12957. InstallList.Next = entry;
  12958. } else {
  12959. while ((PPNP_INSTALL_ENTRY)current->Next != NULL) {
  12960. current = (PPNP_INSTALL_ENTRY)current->Next;
  12961. }
  12962. current->Next = entry;
  12963. }
  12964. UnlockNotifyList(&InstallList.Lock);
  12965. SetEvent(InstallEvents[NEEDS_INSTALL_EVENT]);
  12966. //
  12967. // You might think we could skip the children if a parent is going to
  12968. // be reinstalled. However, setupapi might decide not to tear down the
  12969. // stack.
  12970. //
  12971. }
  12972. //
  12973. // If this is a single-level enumeration, we only want to touch the parent
  12974. // and his immediate children.
  12975. //
  12976. if (pqiContext->HeadNodeSeen && pqiContext->SingleLevelEnumOnly) {
  12977. return EA_SKIP_SUBTREE;
  12978. }
  12979. pqiContext->HeadNodeSeen = TRUE;
  12980. return EA_CONTINUE;
  12981. } // QueueInstallationCallback
  12982. CONFIGRET
  12983. DevInstNeedsInstall(
  12984. IN LPCWSTR DevInst,
  12985. IN BOOL CheckReinstallConfigFlag,
  12986. OUT BOOL *NeedsInstall
  12987. )
  12988. /*++
  12989. Routine Description:
  12990. This routine determines whether a particular DevInst needs to be passed off
  12991. to Setupapi for installation.
  12992. Arguments:
  12993. DevInst -
  12994. InstancePath of devnode to check.
  12995. CheckReinstallConfigFlag -
  12996. Specifies if the CONFIGFLAG_REINSTALL ConfigFlag should explicitly also
  12997. be checked.
  12998. NeedsInstall -
  12999. Recieves TRUE if the devnode is present and needs to be installed, FALSE
  13000. otherwise.
  13001. Return Value:
  13002. CONFIGRET (if the devnode isn't present, this will be CR_NO_SUCH_DEVINST.)
  13003. --*/
  13004. {
  13005. CONFIGRET status;
  13006. ULONG ulStatus, ulProblem, ulConfig;
  13007. //
  13008. // Preinit
  13009. //
  13010. *NeedsInstall = FALSE;
  13011. //
  13012. // Is the device present?
  13013. //
  13014. status = GetDeviceStatus(DevInst, &ulStatus, &ulProblem);
  13015. if (status == CR_SUCCESS) {
  13016. //
  13017. // Implementation note: In kernel-mode when we first process this
  13018. // device instance, if there is no ConfigFlag value present, then we
  13019. // set a problem of CM_PROB_NOT_CONFIGURED (this would always happen
  13020. // for brand new device instances). If there is already a ConfigFlag
  13021. // value of CONFIGFLAG_REINSTALL, then we set a problem of
  13022. // CM_PROB_REINSTALL. Either problem will trigger an installation of
  13023. // this device, the only difference is in how SetupDi routines handle
  13024. // a failed installation: If ConfigFlag is CONFIGFLAG_NOT_CONFIGURED,
  13025. // then a failed install will leave the ConfigFlag alone and set a
  13026. // problem of CM_PROB_FAILED_INSTALL. If there is no ConfigFlag, then
  13027. // ConfigFlag will be set to CONFIGFLAG_DISABLED.
  13028. //
  13029. if ((ulStatus & DN_HAS_PROBLEM) &&
  13030. ((ulProblem == CM_PROB_REINSTALL) ||
  13031. (ulProblem == CM_PROB_NOT_CONFIGURED))) {
  13032. *NeedsInstall = TRUE;
  13033. }
  13034. ulConfig = GetDeviceConfigFlags(DevInst, NULL);
  13035. //
  13036. // In some cases, we explicitly need to also check for the
  13037. // CONFIGFLAG_REINSTALL ConfigFlag, because the devnode may not yet have
  13038. // the CM_PROB_REINSTALL problem code.
  13039. //
  13040. if ((CheckReinstallConfigFlag) &&
  13041. (ulConfig & CONFIGFLAG_REINSTALL)) {
  13042. *NeedsInstall = TRUE;
  13043. }
  13044. //
  13045. // Addendum to Implementation note: If there is no ConfigFlag present,
  13046. // but the device has the RawDeviceOK capability - OR - a matching
  13047. // Service is found for the device in the CriticalDeviceDatabase, then
  13048. // the device is started, but marked by kernel-mode with the
  13049. // CONFIGFLAG_FINISH_INSTALL, indicating that user-mode should complete
  13050. // the installation.
  13051. //
  13052. if (ulConfig & CONFIGFLAG_FINISH_INSTALL) {
  13053. *NeedsInstall = TRUE;
  13054. if (gbPreservePreInstall) {
  13055. //
  13056. // If we are expected to preserve critical device database /
  13057. // device pre-installation settings, check if this finish
  13058. // install device indicates installation is complete.
  13059. //
  13060. HKEY hKeyDevInst;
  13061. ULONG ulValue, ulSize;
  13062. if (RegOpenKeyEx(
  13063. ghEnumKey,
  13064. DevInst,
  13065. 0,
  13066. KEY_READ | KEY_WRITE,
  13067. &hKeyDevInst) == ERROR_SUCCESS) {
  13068. ulValue = 0;
  13069. ulSize = sizeof(ulValue);
  13070. if (RegQueryValueEx(
  13071. hKeyDevInst,
  13072. pszRegValuePreservePreInstall,
  13073. NULL,
  13074. NULL,
  13075. (LPBYTE)&ulValue,
  13076. &ulSize) == ERROR_SUCCESS) {
  13077. if (ulValue == 1) {
  13078. //
  13079. // Unset the finish-install config flag.
  13080. //
  13081. ulConfig &= ~CONFIGFLAG_FINISH_INSTALL;
  13082. PNP_SetDeviceRegProp(
  13083. NULL,
  13084. DevInst,
  13085. CM_DRP_CONFIGFLAGS,
  13086. REG_DWORD,
  13087. (LPBYTE)&ulConfig,
  13088. sizeof(ulConfig),
  13089. 0);
  13090. //
  13091. // Device does not need to be installed.
  13092. //
  13093. *NeedsInstall = FALSE;
  13094. }
  13095. //
  13096. // Delete the PreservePreInstall value.
  13097. //
  13098. RegDeleteValue(
  13099. hKeyDevInst,
  13100. pszRegValuePreservePreInstall);
  13101. }
  13102. RegCloseKey(hKeyDevInst);
  13103. }
  13104. }
  13105. }
  13106. } else if (IsRootDeviceID(DevInst)) {
  13107. status = CR_SUCCESS;
  13108. }
  13109. return status;
  13110. }
  13111. PWSTR
  13112. BuildBlockedDriverList(
  13113. IN OUT LPGUID GuidList,
  13114. IN ULONG GuidCount
  13115. )
  13116. /*++
  13117. Routine Description:
  13118. This routine builds a multi-sz list of GUIDs, based on the array of GUIDs
  13119. supplied. If no GUIDs were supplied, this routine returns a list of all
  13120. drivers currently blocked by the system.
  13121. Arguments:
  13122. GuidList - Address of the array of blocked driver GUIDs to create the
  13123. multi-sz list from. This argument may be NULL to retrieve a
  13124. list of all drivers currently blocked by the system.
  13125. GuidCount - Specifies the number of GUIDs in the array. If GuidList is
  13126. NULL, this argument must be 0.
  13127. Return Value:
  13128. Returns a MultiSz list of blocked driver GUIDs, based on the supplied
  13129. parameters. Returns NULL if no GUIDs were supplied, and no GUIDs are
  13130. currently being blocked by the system.
  13131. If a multi-sz list was returned, the caller is responsible for freeing the
  13132. associated buffer.
  13133. --*/
  13134. {
  13135. CONFIGRET Status = STATUS_SUCCESS;
  13136. ULONG ulLength, ulTemp;
  13137. PBYTE Buffer = NULL;
  13138. PWSTR MultiSzList = NULL, p;
  13139. try {
  13140. //
  13141. // Validate parameters.
  13142. //
  13143. if (((!ARGUMENT_PRESENT(GuidList)) && (GuidCount != 0)) ||
  13144. ((ARGUMENT_PRESENT(GuidList)) && (GuidCount == 0))) {
  13145. Status = CR_FAILURE;
  13146. goto Clean0;
  13147. }
  13148. if (GuidCount == 0) {
  13149. //
  13150. // We were called without a list of GUIDs, so we need to get the
  13151. // list ourselves.
  13152. //
  13153. ASSERT(!ARGUMENT_PRESENT(GuidList));
  13154. ulLength = 0;
  13155. ulTemp = 0;
  13156. Status = PNP_GetBlockedDriverInfo(
  13157. NULL,
  13158. NULL,
  13159. &ulTemp,
  13160. &ulLength,
  13161. 0);
  13162. //
  13163. // If no drivers are currently being blocked, or we encountered some
  13164. // other failure, we have nothing to display, so just return.
  13165. //
  13166. if ((Status != CR_BUFFER_SMALL) || (ulLength == 0)) {
  13167. Status = CR_FAILURE;
  13168. goto Clean0;
  13169. }
  13170. //
  13171. // Allocate a buffer to retrieve the list of GUIDs.
  13172. //
  13173. Buffer = HeapAlloc(ghPnPHeap, 0, ulLength);
  13174. if (Buffer == NULL) {
  13175. Status = CR_FAILURE;
  13176. goto Clean0;
  13177. }
  13178. //
  13179. // Get the list of GUIDs for currently blocked drivers.
  13180. //
  13181. ulTemp = 0;
  13182. Status = PNP_GetBlockedDriverInfo(
  13183. NULL,
  13184. Buffer,
  13185. &ulTemp,
  13186. &ulLength,
  13187. 0);
  13188. //
  13189. // We thought there was a list when we checked before, so we better
  13190. // have one now.
  13191. //
  13192. ASSERT(Status != CR_BUFFER_SMALL);
  13193. ASSERT(ulLength != 0);
  13194. ASSERT(ulTemp != 0);
  13195. if (Status != CR_SUCCESS) {
  13196. goto Clean0;
  13197. }
  13198. //
  13199. // Use the list we just retrieved. Note that Buffer is non-NULL
  13200. // when we allocate our own buffer for the array, so make sure we
  13201. // free it below.
  13202. //
  13203. GuidCount = ulLength / sizeof(GUID);
  13204. GuidList = (LPGUID)Buffer;
  13205. }
  13206. //
  13207. // We must have a list of GUIDs to convert by this point.
  13208. //
  13209. ASSERT(GuidCount > 0);
  13210. ASSERT(GuidList != NULL);
  13211. //
  13212. // Allocate a buffer to hold the multi-sz list of stringified GUIDs.
  13213. //
  13214. ulLength = (GuidCount*MAX_GUID_STRING_LEN + 1) * sizeof(WCHAR);
  13215. MultiSzList = HeapAlloc(ghPnPHeap, 0, ulLength);
  13216. if (MultiSzList == NULL) {
  13217. Status = CR_FAILURE;
  13218. goto Clean0;
  13219. }
  13220. ZeroMemory(MultiSzList, ulLength);
  13221. //
  13222. // Traverse the list of GUIDs, converting to strings as we go.
  13223. //
  13224. for (p = MultiSzList, ulTemp = 0;
  13225. ulTemp < GuidCount;
  13226. ulTemp++, p+= lstrlen(p) + 1) {
  13227. if (StringFromGuid(
  13228. (LPGUID)&(GuidList[ulTemp]), p,
  13229. ((ulLength/sizeof(WCHAR)) - (ULONG)(p - MultiSzList))) != NO_ERROR) {
  13230. Status = CR_FAILURE;
  13231. goto Clean0;
  13232. }
  13233. }
  13234. *p = L'\0';
  13235. //
  13236. // Success!!
  13237. //
  13238. Status = CR_SUCCESS;
  13239. Clean0:
  13240. NOTHING;
  13241. } except(EXCEPTION_EXECUTE_HANDLER) {
  13242. KdPrintEx((DPFLTR_PNPMGR_ID,
  13243. DBGF_ERRORS,
  13244. "UMPNPMGR: Exception in BuildBlockedDriverList!\n"));
  13245. ASSERT(0);
  13246. Status = CR_FAILURE;
  13247. //
  13248. // Reference the following variables so the compiler will respect
  13249. // statement ordering w.r.t. their assignment.
  13250. //
  13251. Buffer = Buffer;
  13252. MultiSzList = MultiSzList;
  13253. }
  13254. //
  13255. // Free the GUID list buffer, if we allocated one.
  13256. //
  13257. if (Buffer != NULL) {
  13258. HeapFree(ghPnPHeap, 0, Buffer);
  13259. }
  13260. //
  13261. // Don't return a list if we were unsuccessful.
  13262. //
  13263. if ((Status != CR_SUCCESS) && (MultiSzList != NULL)) {
  13264. HeapFree(ghPnPHeap, 0, MultiSzList);
  13265. MultiSzList = NULL;
  13266. }
  13267. return MultiSzList;
  13268. } // BuildBlockedDriverList
  13269. CONFIGRET
  13270. PNP_GetServerSideDeviceInstallFlags(
  13271. IN handle_t hBinding,
  13272. PULONG pulSSDIFlags,
  13273. ULONG ulFlags
  13274. )
  13275. /*++
  13276. Routine Description:
  13277. This is the RPC server entry point for the CMP_GetServerSideDeviceInstallFlags
  13278. routine.
  13279. Arguments:
  13280. hBinding - RPC binding handle, not used.
  13281. pulSSDIFlags - A ULONG pointer, supplied by the caller. This is used
  13282. to pass back the following server side device install
  13283. flags:
  13284. SSDI_REBOOT_PENDING - A reboot is pending from a server
  13285. side device install.
  13286. ulFlags Not used, must be zero.
  13287. Return Value:
  13288. Return CR_SUCCESS if the function succeeds, otherwise it returns one of the
  13289. CR_* errors.
  13290. --*/
  13291. {
  13292. CONFIGRET Status = CR_SUCCESS;
  13293. UNREFERENCED_PARAMETER(hBinding);
  13294. try {
  13295. //
  13296. // Validate parameters
  13297. //
  13298. if (!ARGUMENT_PRESENT(pulSSDIFlags)) {
  13299. Status = CR_INVALID_POINTER;
  13300. goto Clean0;
  13301. }
  13302. if (INVALID_FLAGS(ulFlags, 0)) {
  13303. Status = CR_INVALID_FLAG;
  13304. goto Clean0;
  13305. }
  13306. *pulSSDIFlags = 0;
  13307. //
  13308. // SSDI_REBOOT_PENDING
  13309. // Determine if a server side device install reboot is pending.
  13310. //
  13311. if (gServerSideDeviceInstallRebootNeeded) {
  13312. *pulSSDIFlags |= SSDI_REBOOT_PENDING;
  13313. }
  13314. Clean0:
  13315. NOTHING;
  13316. } except(EXCEPTION_EXECUTE_HANDLER) {
  13317. Status = CR_FAILURE;
  13318. }
  13319. return Status;
  13320. } // PNP_GetServerSideDeviceInstallFlags
  13321. VOID
  13322. SendInvalidIDNotifications(
  13323. IN ULONG ulSessionId
  13324. )
  13325. /*++
  13326. Routine Description:
  13327. This routine scans the entire device tree looking for devices with
  13328. DN_CHILD_WITH_INVALID_ID set. For all those, it sends notification
  13329. to hotplug. There is a race here between this function and the
  13330. notifications from kernel mode but that's probably ok
  13331. (double notifications).
  13332. Arguments:
  13333. None.
  13334. Return Value:
  13335. None.
  13336. --*/
  13337. {
  13338. WCHAR szCurrentDevice[MAX_DEVICE_ID_LEN + 1], szNextDevice[MAX_DEVICE_ID_LEN + 1];
  13339. ULONG ulStatus, ulProblem;
  13340. CONFIGRET cr;
  13341. PLUGPLAY_CONTROL_RELATED_DEVICE_DATA controlData;
  13342. NTSTATUS ntStatus;
  13343. HRESULT hr;
  13344. //
  13345. // Start from the device tree root.
  13346. //
  13347. if (FAILED(StringCchCopyEx(
  13348. szCurrentDevice,
  13349. SIZECHARS(szCurrentDevice),
  13350. pszRegRootEnumerator,
  13351. NULL, NULL,
  13352. STRSAFE_NULL_ON_FAILURE))) {
  13353. return;
  13354. }
  13355. //
  13356. // Walk the entire tree and send notification to show the balloon for every device with
  13357. // DN_CHILD_WITH_INVALID_ID flag.
  13358. //
  13359. do {
  13360. //
  13361. // Check if this device has the DN_ bit set.
  13362. //
  13363. ulStatus = 0;
  13364. cr = GetDeviceStatus(szCurrentDevice, &ulStatus, &ulProblem);
  13365. if ((cr == CR_SUCCESS) &&
  13366. (ulStatus & DN_CHILD_WITH_INVALID_ID)) {
  13367. //
  13368. // terminate MULTI_SZ.
  13369. //
  13370. szCurrentDevice[wcslen(szCurrentDevice) + 1] = UNICODE_NULL;
  13371. //
  13372. // Notify the user via hotplug.
  13373. //
  13374. SendHotplugNotification((LPGUID)&GUID_DEVICE_INVALID_ID,
  13375. NULL,
  13376. szCurrentDevice,
  13377. &ulSessionId,
  13378. 0);
  13379. }
  13380. //
  13381. // Get the child.
  13382. //
  13383. controlData.Relation = PNP_RELATION_CHILD;
  13384. RtlInitUnicodeString(&controlData.TargetDeviceInstance, szCurrentDevice);
  13385. controlData.RelatedDeviceInstance = szNextDevice;
  13386. controlData.RelatedDeviceInstanceLength = SIZECHARS(szNextDevice) - 1; // MAX_DEVICE_ID_LEN
  13387. ntStatus = NtPlugPlayControl(PlugPlayControlGetRelatedDevice,
  13388. &controlData,
  13389. sizeof(controlData));
  13390. if (NT_SUCCESS(ntStatus)) {
  13391. if (FAILED(StringCchCopyEx(
  13392. szCurrentDevice,
  13393. SIZECHARS(szCurrentDevice),
  13394. szNextDevice,
  13395. NULL, NULL,
  13396. STRSAFE_NULL_ON_FAILURE))) {
  13397. //
  13398. // Unable to copy the device id, stop the walk.
  13399. //
  13400. break;
  13401. }
  13402. //
  13403. // Continue the walk.
  13404. //
  13405. continue;
  13406. }
  13407. if (ntStatus != STATUS_NO_SUCH_DEVICE) {
  13408. //
  13409. // We failed for some other reason, stop the walk.
  13410. //
  13411. break;
  13412. }
  13413. //
  13414. // If no child, get the sibling.
  13415. //
  13416. while (!IsRootDeviceID(szCurrentDevice)) {
  13417. controlData.Relation = PNP_GET_SIBLING_DEVICE_INSTANCE;
  13418. RtlInitUnicodeString(&controlData.TargetDeviceInstance, szCurrentDevice);
  13419. controlData.RelatedDeviceInstance = szNextDevice;
  13420. controlData.RelatedDeviceInstanceLength = SIZECHARS(szNextDevice) - 1; // MAX_DEVICE_ID_LEN
  13421. ntStatus = NtPlugPlayControl(PlugPlayControlGetRelatedDevice,
  13422. &controlData,
  13423. sizeof(controlData));
  13424. if (NT_SUCCESS(ntStatus)) {
  13425. hr = StringCchCopyEx(szCurrentDevice,
  13426. SIZECHARS(szCurrentDevice),
  13427. szNextDevice,
  13428. NULL, NULL,
  13429. STRSAFE_NULL_ON_FAILURE);
  13430. ASSERT(SUCCEEDED(hr));
  13431. break;
  13432. }
  13433. //
  13434. // If no more siblings, go up the tree one level.
  13435. //
  13436. controlData.Relation = PNP_GET_PARENT_DEVICE_INSTANCE;
  13437. RtlInitUnicodeString(&controlData.TargetDeviceInstance, szCurrentDevice);
  13438. controlData.RelatedDeviceInstance = szNextDevice;
  13439. controlData.RelatedDeviceInstanceLength = SIZECHARS(szNextDevice) - 1; // MAX_DEVICE_ID_LEN
  13440. ntStatus = NtPlugPlayControl(PlugPlayControlGetRelatedDevice,
  13441. &controlData,
  13442. sizeof(controlData));
  13443. if (!NT_SUCCESS(ntStatus)) {
  13444. //
  13445. // No parent? Something went wrong or we completed our tree walk.
  13446. //
  13447. break;
  13448. }
  13449. hr = StringCchCopyEx(szCurrentDevice,
  13450. SIZECHARS(szCurrentDevice),
  13451. szNextDevice,
  13452. NULL, NULL,
  13453. STRSAFE_NULL_ON_FAILURE);
  13454. ASSERT(SUCCEEDED(hr));
  13455. }
  13456. } while (NT_SUCCESS(ntStatus) && (!IsRootDeviceID(szCurrentDevice)));
  13457. return;
  13458. } // SendInvalidIDNotifications
  13459.