Source code of Windows XP (NT5)
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.

15291 lines
503 KiB

  1. /*++
  2. Copyright (c) 1995-2001 Microsoft Corporation
  3. Module Name:
  4. revent.c
  5. Abstract:
  6. This module contains the server-side misc configuration manager routines.
  7. Author:
  8. Paula Tomlinson (paulat) 6-28-1995
  9. Environment:
  10. User-mode only.
  11. Revision History:
  12. 28-June-1995 paulat
  13. Creation and initial implementation.
  14. --*/
  15. //
  16. // includes
  17. //
  18. #include "precomp.h"
  19. #include "umpnpi.h"
  20. #include "umpnpdat.h"
  21. #include "pnpipc.h"
  22. #include "pnpmsg.h"
  23. #include "setupapi.h"
  24. #include "spapip.h"
  25. #include <wtsapi32.h>
  26. #include <winsta.h>
  27. #include <userenv.h>
  28. #include <syslib.h>
  29. #include <initguid.h>
  30. #include <winioctl.h>
  31. #include <ntddpar.h>
  32. #include <pnpmgr.h>
  33. #include <wdmguid.h>
  34. #include <ioevent.h>
  35. #include <devguid.h>
  36. #include <winsvcp.h>
  37. //
  38. // Maximum number of times (per pass) we will reenumerate the device tree during
  39. // GUI setup in an attempt to find and install new devices.
  40. //
  41. #define MAX_REENUMERATION_COUNT 128
  42. //
  43. // Define and initialize a global variable, GUID_NULL
  44. // (from coguid.h)
  45. //
  46. DEFINE_GUID(GUID_NULL, 0L, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
  47. //
  48. // Private interface device class that is used to register for all devnode change
  49. // notifications. This is no longer supported, but we want to fail anyone who registers
  50. // this GUID.
  51. //
  52. DEFINE_GUID(GUID_DEVNODE_CHANGE, 0xfa1fb208L, 0xf892, 0x11d0, 0x8a, 0x2e, 0x00, 0x00, 0xf8, 0x75, 0x3f, 0x55);
  53. //
  54. // Private interface device class that is assigned to entries registered for
  55. // device interface change notifications, using the
  56. // DEVICE_NOTIFY_ALL_INTERFACE_CLASSES flag. For internal use only.
  57. //
  58. DEFINE_GUID(GUID_DEVINTERFACE_INCLUDE_ALL_INTERFACE_CLASSES,
  59. 0x2121db68, 0x0993, 0x4a29, 0xb8, 0xe0, 0x1e, 0x51, 0x9c, 0x43, 0x72, 0xe6);
  60. //
  61. // SessionId 0 is the main session, and is always created during system startup
  62. // and remains until system shutdown, whether or not terminal services is
  63. // running. This session always hosts services.exe and all services, so it is
  64. // the only session that our ConsoleCtrlHandler can receive events for.
  65. //
  66. #define MAIN_SESSION ((ULONG) 0)
  67. #define INVALID_SESSION ((ULONG)-1)
  68. //
  69. // The active console session is the session currently connected to the physical
  70. // Console. We store this value in a global variable, whose access is
  71. // controlled by an event. The routine GetActiveConsoleSessionId() is used to
  72. // retrieve the value when it is safe to do so.
  73. //
  74. // Note that SessionId 0 is the initial console session, and that the
  75. // SessionNotificationHandler is responsible for maintaining state.
  76. //
  77. ULONG gActiveConsoleSessionId = MAIN_SESSION; // system always starts with session 0
  78. HANDLE ghActiveConsoleSessionEvent = NULL; // nonsignaled while session change is in progress
  79. //
  80. // We always use DeviceEventWorker and BroadcastSystemMessage to deliver
  81. // notification to windows in SessionId 0. For all other sessions, we use
  82. // WinStationSendWindowMessage and WinStationBroadcastSystemMessage.
  83. // These are the timeout period (in seconds) for messages sent and broadcast to
  84. // sessions other than SessionId 0. These times should be the same as those
  85. // implemented by their SessionId 0 counterparts.
  86. //
  87. #define DEFAULT_SEND_TIME_OUT 30 // same as DeviceEventWorker
  88. #define DEFAULT_BROADCAST_TIME_OUT 5 // same as BroadcastSystemMessage
  89. //
  90. // Notification list structure.
  91. //
  92. typedef struct _PNP_NOTIFY_LIST {
  93. PVOID Next;
  94. PVOID Previous;
  95. LOCKINFO Lock;
  96. } PNP_NOTIFY_LIST, *PPNP_NOTIFY_LIST;
  97. //
  98. // Notification entry structure.
  99. //
  100. typedef struct _PNP_NOTIFY_ENTRY {
  101. PVOID Next;
  102. PVOID Previous;
  103. BOOL Unregistered;
  104. ULONG Signature;
  105. HANDLE Handle;
  106. DWORD Flags;
  107. ULONG SessionId;
  108. ULONG Freed;
  109. ULONG64 ClientCtxPtr;
  110. LPWSTR ClientName;
  111. union {
  112. struct {
  113. GUID ClassGuid;
  114. } Class;
  115. struct {
  116. HANDLE FileHandle;
  117. WCHAR DeviceId[MAX_DEVICE_ID_LEN];
  118. } Target;
  119. struct {
  120. DWORD Reserved;
  121. } Devnode;
  122. struct {
  123. DWORD scmControls;
  124. } Service;
  125. } u;
  126. } PNP_NOTIFY_ENTRY, *PPNP_NOTIFY_ENTRY;
  127. //
  128. // Deferred operation list structure.
  129. //
  130. typedef struct _PNP_DEFERRED_LIST {
  131. PVOID Next;
  132. handle_t hBinding;
  133. PPNP_NOTIFY_ENTRY Entry;
  134. } PNP_DEFERRED_LIST, *PPNP_DEFERRED_LIST;
  135. //
  136. // Signatures describing which notification list an entry currently belongs to.
  137. //
  138. #define CLASS_ENTRY_SIGNATURE (0x07625100)
  139. #define TARGET_ENTRY_SIGNATURE (0x17625100)
  140. #define SERVICE_ENTRY_SIGNATURE (0x37625100)
  141. #define LIST_ENTRY_SIGNATURE_MASK (0xFFFFFF00)
  142. #define LIST_ENTRY_INDEX_MASK (~LIST_ENTRY_SIGNATURE_MASK)
  143. #define MarkEntryWithList(ent,value) { ent->Signature &= LIST_ENTRY_SIGNATURE_MASK;\
  144. ent->Signature |= value; }
  145. //
  146. // Device event notification lists.
  147. //
  148. #define TARGET_HASH_BUCKETS 13
  149. #define CLASS_GUID_HASH_BUCKETS 13
  150. #define SERVICE_NUM_CONTROLS 3
  151. #define HashClassGuid(_Guid) \
  152. ( ( ((PULONG)_Guid)[0] + ((PULONG)_Guid)[1] + ((PULONG)_Guid)[2] \
  153. + ((PULONG)_Guid)[3]) % CLASS_GUID_HASH_BUCKETS)
  154. PNP_NOTIFY_LIST TargetList[TARGET_HASH_BUCKETS];
  155. PNP_NOTIFY_LIST ClassList[CLASS_GUID_HASH_BUCKETS];
  156. PNP_NOTIFY_LIST ServiceList[SERVICE_NUM_CONTROLS];
  157. PPNP_DEFERRED_LIST UnregisterList;
  158. PPNP_DEFERRED_LIST RegisterList;
  159. PPNP_DEFERRED_LIST RundownList;
  160. CRITICAL_SECTION RegistrationCS;
  161. //
  162. // These are indices into the global ServiceList array of lists containing
  163. // services registered for the corresponding service control events.
  164. //
  165. enum cBitIndex {
  166. CINDEX_HWPROFILE = 0,
  167. CINDEX_POWEREVENT = 1
  168. };
  169. //
  170. // These are a bit mask for the above lists.
  171. // (the two enums should match! One is 0,1,2,...n. The other 2^n.)
  172. //
  173. enum cBits {
  174. CBIT_HWPROFILE = 1,
  175. CBIT_POWEREVENT = 2
  176. };
  177. //
  178. // Properties describing how a notification entry was freed.
  179. //
  180. // (the entry has been removed from the notification list)
  181. #define DEFER_NOTIFY_FREE 0x80000000
  182. // (used for debugging only)
  183. #define PNP_UNREG_FREE 0x00000100
  184. #define PNP_UNREG_CLASS 0x00000200
  185. #define PNP_UNREG_TARGET 0x00000400
  186. #define PNP_UNREG_DEFER 0x00000800
  187. #define PNP_UNREG_WIN 0x00001000
  188. #define PNP_UNREG_SERVICE 0x00002000
  189. #define PNP_UNREG_CANCEL 0x00004000
  190. #define PNP_UNREG_RUNDOWN 0x00008000
  191. //
  192. // List of devices to be installed.
  193. //
  194. typedef struct _PNP_INSTALL_LIST {
  195. PVOID Next;
  196. LOCKINFO Lock;
  197. } PNP_INSTALL_LIST, *PPNP_INSTALL_LIST;
  198. //
  199. // Device install list entry structure.
  200. //
  201. typedef struct _PNP_INSTALL_ENTRY {
  202. PVOID Next;
  203. DWORD Flags;
  204. WCHAR szDeviceId[MAX_DEVICE_ID_LEN];
  205. } PNP_INSTALL_ENTRY, *PPNP_INSTALL_ENTRY;
  206. //
  207. // Install event list.
  208. //
  209. PNP_INSTALL_LIST InstallList;
  210. //
  211. // Flags for PNP_INSTALL_ENTRY nodes
  212. //
  213. #define PIE_SERVER_SIDE_INSTALL_ATTEMPTED 0x00000001
  214. #define PIE_DEVICE_INSTALL_REQUIRED_REBOOT 0x00000002
  215. //
  216. // Device install client information list structure.
  217. //
  218. typedef struct _INSTALL_CLIENT_LIST {
  219. PVOID Next;
  220. LOCKINFO Lock;
  221. } INSTALL_CLIENT_LIST, *PINSTALL_CLIENT_LIST;
  222. //
  223. // Device install client information list entry structure.
  224. //
  225. typedef struct _INSTALL_CLIENT_ENTRY {
  226. PVOID Next;
  227. ULONG RefCount;
  228. ULONG ulSessionId;
  229. HANDLE hEvent;
  230. HANDLE hPipe;
  231. HANDLE hProcess;
  232. HANDLE hDisconnectEvent;
  233. ULONG ulInstallFlags;
  234. WCHAR LastDeviceId[MAX_DEVICE_ID_LEN];
  235. } INSTALL_CLIENT_ENTRY, *PINSTALL_CLIENT_ENTRY;
  236. //
  237. // Device install client list.
  238. //
  239. INSTALL_CLIENT_LIST InstallClientList;
  240. //
  241. // Global BOOL that tracks if a server side device install reboot is needed.
  242. //
  243. BOOL gServerSideDeviceInstallRebootNeeded = FALSE;
  244. //
  245. // private prototypes
  246. //
  247. DWORD
  248. ThreadProc_DeviceEvent(
  249. LPDWORD lpParam
  250. );
  251. DWORD
  252. ThreadProc_DeviceInstall(
  253. LPDWORD lpParam
  254. );
  255. DWORD
  256. ThreadProc_GuiSetupDeviceInstall(
  257. LPDWORD lpThreadParam
  258. );
  259. DWORD
  260. ThreadProc_FactoryPreinstallDeviceInstall(
  261. LPDWORD lpThreadParam
  262. );
  263. DWORD
  264. ThreadProc_ReenumerateDeviceTree(
  265. LPVOID lpThreadParam
  266. );
  267. BOOL
  268. InstallDevice(
  269. IN LPWSTR pszDeviceId,
  270. IN OUT PULONG SessionId,
  271. IN ULONG Flags
  272. );
  273. DWORD
  274. InstallDeviceServerSide(
  275. IN LPWSTR pszDeviceId,
  276. IN OUT PBOOL RebootRequired,
  277. IN OUT PBOOL DeviceHasProblem,
  278. IN OUT PULONG SessionId,
  279. IN ULONG Flags
  280. );
  281. BOOL
  282. CreateDeviceInstallClient(
  283. IN ULONG SessionId,
  284. OUT PINSTALL_CLIENT_ENTRY *DeviceInstallClient
  285. );
  286. BOOL
  287. ConnectDeviceInstallClient(
  288. IN ULONG SessionId,
  289. OUT PINSTALL_CLIENT_ENTRY *DeviceInstallClient
  290. );
  291. BOOL
  292. DisconnectDeviceInstallClient(
  293. IN PINSTALL_CLIENT_ENTRY DeviceInstallClient
  294. );
  295. PINSTALL_CLIENT_ENTRY
  296. LocateDeviceInstallClient(
  297. IN ULONG SessionId
  298. );
  299. VOID
  300. ReferenceDeviceInstallClient(
  301. IN PINSTALL_CLIENT_ENTRY DeviceInstallClient
  302. );
  303. VOID
  304. DereferenceDeviceInstallClient(
  305. IN PINSTALL_CLIENT_ENTRY DeviceInstallClient
  306. );
  307. BOOL
  308. DoDeviceInstallClient(
  309. IN LPWSTR DeviceId,
  310. IN PULONG SessionId,
  311. IN ULONG Flags,
  312. OUT PINSTALL_CLIENT_ENTRY *DeviceInstallClient
  313. );
  314. BOOL
  315. InitNotification(
  316. VOID
  317. );
  318. VOID
  319. TermNotification(
  320. VOID
  321. );
  322. ULONG
  323. ProcessDeviceEvent(
  324. IN PPLUGPLAY_EVENT_BLOCK EventBlock,
  325. IN DWORD EventBufferSize,
  326. OUT PPNP_VETO_TYPE VetoType,
  327. OUT LPWSTR VetoName,
  328. IN OUT PULONG VetoNameLength
  329. );
  330. ULONG
  331. NotifyInterfaceClassChange(
  332. IN DWORD ServiceControl,
  333. IN DWORD EventId,
  334. IN DWORD Flags,
  335. IN PDEV_BROADCAST_DEVICEINTERFACE ClassData
  336. );
  337. ULONG
  338. NotifyTargetDeviceChange(
  339. IN DWORD ServiceControl,
  340. IN DWORD EventId,
  341. IN DWORD Flags,
  342. IN PDEV_BROADCAST_HANDLE HandleData,
  343. IN LPWSTR DeviceId,
  344. OUT PPNP_VETO_TYPE VetoType OPTIONAL,
  345. OUT LPWSTR VetoName OPTIONAL,
  346. IN OUT PULONG VetoNameLength OPTIONAL
  347. );
  348. ULONG
  349. NotifyHardwareProfileChange(
  350. IN DWORD ServiceControl,
  351. IN DWORD EventId,
  352. IN DWORD Flags,
  353. OUT PPNP_VETO_TYPE VetoType OPTIONAL,
  354. OUT LPWSTR VetoName OPTIONAL,
  355. IN OUT PULONG VetoNameLength OPTIONAL
  356. );
  357. ULONG
  358. NotifyPower(
  359. IN DWORD ServiceControl,
  360. IN DWORD EventId,
  361. IN DWORD EventData,
  362. IN DWORD Flags,
  363. OUT PPNP_VETO_TYPE VetoType OPTIONAL,
  364. OUT LPWSTR VetoName OPTIONAL,
  365. IN OUT PULONG VetoNameLength OPTIONAL
  366. );
  367. BOOL
  368. SendCancelNotification(
  369. IN PPNP_NOTIFY_ENTRY LastEntry,
  370. IN DWORD ServiceControl,
  371. IN DWORD EventId,
  372. IN ULONG Flags,
  373. IN PDEV_BROADCAST_HDR NotifyData OPTIONAL,
  374. IN LPWSTR DeviceId OPTIONAL
  375. );
  376. VOID
  377. BroadcastCompatibleDeviceMsg(
  378. IN DWORD EventId,
  379. IN PDEV_BROADCAST_DEVICEINTERFACE ClassData
  380. );
  381. VOID
  382. BroadcastVolumeNameChange(
  383. VOID
  384. );
  385. DWORD
  386. GetAllVolumeMountPoints(
  387. VOID
  388. );
  389. BOOL
  390. EventIdFromEventGuid(
  391. IN CONST GUID *EventGuid,
  392. OUT LPDWORD EventId,
  393. OUT LPDWORD Flags,
  394. OUT LPDWORD ServiceControl
  395. );
  396. ULONG
  397. SendHotplugNotification(
  398. IN CONST GUID *EventGuid,
  399. IN PPNP_VETO_TYPE VetoType OPTIONAL,
  400. IN LPWSTR MultiSzList,
  401. IN OUT PULONG SessionId,
  402. IN ULONG Flags
  403. );
  404. BOOL
  405. GuidEqual(
  406. CONST GUID UNALIGNED *Guid1,
  407. CONST GUID UNALIGNED *Guid2
  408. );
  409. VOID
  410. LogErrorEvent(
  411. DWORD dwEventID,
  412. DWORD dwError,
  413. WORD nStrings,
  414. ...
  415. );
  416. VOID
  417. LogWarningEvent(
  418. DWORD dwEventID,
  419. WORD nStrings,
  420. ...
  421. );
  422. BOOL
  423. LockNotifyList(
  424. IN LOCKINFO *Lock
  425. );
  426. VOID
  427. UnlockNotifyList(
  428. IN LOCKINFO *Lock
  429. );
  430. PPNP_NOTIFY_LIST
  431. GetNotifyListForEntry(
  432. IN PPNP_NOTIFY_ENTRY entry
  433. );
  434. BOOL
  435. DeleteNotifyEntry(
  436. IN PPNP_NOTIFY_ENTRY Entry,
  437. IN BOOLEAN RpcNotified
  438. );
  439. VOID
  440. AddNotifyEntry(
  441. IN PPNP_NOTIFY_LIST NotifyList,
  442. IN PPNP_NOTIFY_ENTRY NewEntry
  443. );
  444. ULONG
  445. HashString(
  446. IN LPWSTR String,
  447. IN ULONG Buckets
  448. );
  449. DWORD
  450. MapQueryEventToCancelEvent(
  451. IN DWORD QueryEventId
  452. );
  453. VOID
  454. FixUpDeviceId(
  455. IN OUT LPTSTR DeviceId
  456. );
  457. ULONG
  458. MapSCMControlsToControlBit(
  459. IN ULONG scmControls
  460. );
  461. DWORD
  462. GetFirstPass(
  463. IN BOOL bQuery
  464. );
  465. DWORD
  466. GetNextPass(
  467. IN DWORD curPass,
  468. IN BOOL bQuery
  469. );
  470. BOOL
  471. NotifyEntryThisPass(
  472. IN PPNP_NOTIFY_ENTRY Entry,
  473. IN DWORD Pass
  474. );
  475. DWORD
  476. GetPassFromEntry(
  477. IN PPNP_NOTIFY_ENTRY Entry
  478. );
  479. BOOL
  480. GetClientName(
  481. IN PPNP_NOTIFY_ENTRY entry,
  482. OUT LPWSTR lpszClientName,
  483. IN OUT PULONG pulClientNameLength
  484. );
  485. BOOL
  486. GetWindowsExeFileName(
  487. IN HWND hWnd,
  488. OUT LPWSTR lpszFileName,
  489. IN OUT PULONG pulFileNameLength
  490. );
  491. PPNP_NOTIFY_ENTRY
  492. GetNextNotifyEntry(
  493. IN PPNP_NOTIFY_ENTRY Entry,
  494. IN DWORD Flags
  495. );
  496. PPNP_NOTIFY_ENTRY
  497. GetFirstNotifyEntry(
  498. IN PPNP_NOTIFY_LIST List,
  499. IN DWORD Flags
  500. );
  501. BOOL
  502. InitializeHydraInterface(
  503. VOID
  504. );
  505. DWORD
  506. LoadDeviceInstaller(
  507. VOID
  508. );
  509. VOID
  510. UnloadDeviceInstaller(
  511. VOID
  512. );
  513. BOOL
  514. PromptUser(
  515. IN OUT PULONG SessionId,
  516. IN ULONG Flags
  517. );
  518. VOID
  519. DoRunOnce(
  520. VOID
  521. );
  522. BOOL
  523. GetSessionUserToken(
  524. IN ULONG ulSessionId,
  525. OUT LPHANDLE lphUserToken
  526. );
  527. BOOL
  528. IsUserLoggedOnSession(
  529. IN ULONG ulSessionId
  530. );
  531. BOOL
  532. IsSessionConnected(
  533. IN ULONG ulSessionId
  534. );
  535. BOOL
  536. IsSessionLocked(
  537. IN ULONG ulSessionId
  538. );
  539. BOOL
  540. IsConsoleSession(
  541. IN ULONG ulSessionId
  542. );
  543. DWORD
  544. CreateUserSynchEvent(
  545. IN LPCWSTR lpName,
  546. OUT HANDLE *phEvent
  547. );
  548. BOOL
  549. CreateNoPendingInstallEvent(
  550. VOID
  551. );
  552. ULONG
  553. CheckEjectPermissions(
  554. IN LPWSTR DeviceId,
  555. OUT PPNP_VETO_TYPE VetoType OPTIONAL,
  556. OUT LPWSTR VetoName OPTIONAL,
  557. IN OUT PULONG VetoNameLength OPTIONAL
  558. );
  559. VOID
  560. LogSurpriseRemovalEvent(
  561. IN LPWSTR MultiSzList
  562. );
  563. PWCHAR
  564. BuildFriendlyName(
  565. IN LPWSTR InstancePath
  566. );
  567. CONFIGRET
  568. DevInstNeedsInstall(
  569. IN LPCWSTR DevInst,
  570. OUT BOOL *NeedsInstall
  571. );
  572. PWSTR
  573. BuildBlockedDriverList(
  574. IN OUT LPGUID GuidList,
  575. IN ULONG GuidCount
  576. );
  577. //
  578. // global data
  579. //
  580. extern HANDLE ghInst; // Module handle
  581. extern HKEY ghEnumKey; // Key to HKLM\CCC\System\Enum - DO NOT MODIFY
  582. extern HKEY ghServicesKey; // Key to HKLM\CCC\System\Services - DO NOT MODIFY
  583. extern HKEY ghClassKey; // Key to HKLM\CCC\System\Class - DO NOT MODIFY
  584. extern DWORD CurrentServiceState; // PlugPlay service state - DO NOT MODIFY
  585. HANDLE ghInitMutex = NULL;
  586. HANDLE ghUserToken = NULL;
  587. LOCKINFO gTokenLock;
  588. BOOL gbMainSessionLocked = FALSE;
  589. ULONG gNotificationInProg = 0; // 0 -> No notification or unregister underway.
  590. DWORD gAllDrivesMask = 0; // bitmask of all physical volume mountpoints.
  591. BOOL gbSuppressUI = FALSE; // TRUE if PNP should never display UI (newdev, hotplug).
  592. BOOL gbOobeInProgress = FALSE;// TRUE if the OOBE is running during this boot.
  593. const TCHAR RegMemoryManagementKeyName[] =
  594. TEXT("System\\CurrentControlSet\\Control\\Session Manager\\Memory Management");
  595. const TCHAR RegVerifyDriverLevelValueName[] =
  596. TEXT("VerifyDriverLevel");
  597. //
  598. // Device Installer instance handle and necessary entrypoints.
  599. // This data is only referenced by the (non-GUI setup) device install thread
  600. // (ThreadProc_DeviceInstall).
  601. //
  602. typedef HDEVINFO (WINAPI *FP_CREATEDEVICEINFOLIST)(CONST GUID *, HWND);
  603. typedef BOOL (WINAPI *FP_OPENDEVICEINFO)(HDEVINFO, PCWSTR, HWND, DWORD, PSP_DEVINFO_DATA);
  604. typedef BOOL (WINAPI *FP_BUILDDRIVERINFOLIST)(HDEVINFO, PSP_DEVINFO_DATA, DWORD);
  605. typedef BOOL (WINAPI *FP_DESTROYDEVICEINFOLIST)(HDEVINFO);
  606. typedef BOOL (WINAPI *FP_CALLCLASSINSTALLER)(DI_FUNCTION, HDEVINFO, PSP_DEVINFO_DATA);
  607. typedef BOOL (WINAPI *FP_INSTALLCLASS)(HWND, PCWSTR, DWORD, HSPFILEQ);
  608. typedef BOOL (WINAPI *FP_GETSELECTEDDRIVER)(HDEVINFO, PSP_DEVINFO_DATA, PSP_DRVINFO_DATA);
  609. typedef BOOL (WINAPI *FP_GETDRIVERINFODETAIL)(HDEVINFO, PSP_DEVINFO_DATA, PSP_DRVINFO_DATA, PSP_DRVINFO_DETAIL_DATA, DWORD, PDWORD);
  610. typedef BOOL (WINAPI *FP_GETDEVICEINSTALLPARAMS)(HDEVINFO, PSP_DEVINFO_DATA, PSP_DEVINSTALL_PARAMS);
  611. typedef BOOL (WINAPI *FP_SETDEVICEINSTALLPARAMS)(HDEVINFO, PSP_DEVINFO_DATA, PSP_DEVINSTALL_PARAMS);
  612. typedef BOOL (WINAPI *FP_GETDRIVERINSTALLPARAMS)(HDEVINFO, PSP_DEVINFO_DATA, PSP_DRVINFO_DATA, PSP_DRVINSTALL_PARAMS);
  613. typedef BOOL (WINAPI *FP_SETCLASSINSTALLPARAMS)(HDEVINFO, PSP_DEVINFO_DATA, PSP_CLASSINSTALL_HEADER, DWORD);
  614. typedef BOOL (WINAPI *FP_GETCLASSINSTALLPARAMS)(HDEVINFO, PSP_DEVINFO_DATA, PSP_CLASSINSTALL_HEADER, DWORD, PDWORD);
  615. typedef HINF (WINAPI *FP_OPENINFFILE)(PCWSTR, PCWSTR, DWORD, PUINT);
  616. typedef VOID (WINAPI *FP_CLOSEINFFILE)(HINF);
  617. typedef BOOL (WINAPI *FP_FINDFIRSTLINE)(HINF, PCWSTR, PCWSTR, PINFCONTEXT);
  618. typedef BOOL (WINAPI *FP_FINDNEXTMATCHLINE)(PINFCONTEXT, PCWSTR, PINFCONTEXT);
  619. typedef BOOL (WINAPI *FP_GETSTRINGFIELD)(PINFCONTEXT, DWORD, PWSTR, DWORD, PDWORD);
  620. typedef VOID (*FP_SETGLOBALFLAGS)(DWORD);
  621. typedef DWORD (*FP_GETGLOBALFLAGS)(VOID);
  622. typedef PPSP_RUNONCE_NODE (*FP_ACCESSRUNONCENODELIST)(VOID);
  623. typedef VOID (*FP_DESTROYRUNONCENODELIST)(VOID);
  624. HINSTANCE ghDeviceInstallerLib = NULL;
  625. FP_CREATEDEVICEINFOLIST fpCreateDeviceInfoList;
  626. FP_OPENDEVICEINFO fpOpenDeviceInfo;
  627. FP_BUILDDRIVERINFOLIST fpBuildDriverInfoList;
  628. FP_DESTROYDEVICEINFOLIST fpDestroyDeviceInfoList;
  629. FP_CALLCLASSINSTALLER fpCallClassInstaller;
  630. FP_INSTALLCLASS fpInstallClass;
  631. FP_GETSELECTEDDRIVER fpGetSelectedDriver;
  632. FP_GETDRIVERINFODETAIL fpGetDriverInfoDetail;
  633. FP_GETDEVICEINSTALLPARAMS fpGetDeviceInstallParams;
  634. FP_SETDEVICEINSTALLPARAMS fpSetDeviceInstallParams;
  635. FP_GETDRIVERINSTALLPARAMS fpGetDriverInstallParams;
  636. FP_SETCLASSINSTALLPARAMS fpSetClassInstallParams;
  637. FP_GETCLASSINSTALLPARAMS fpGetClassInstallParams;
  638. FP_OPENINFFILE fpOpenInfFile;
  639. FP_CLOSEINFFILE fpCloseInfFile;
  640. FP_FINDFIRSTLINE fpFindFirstLine;
  641. FP_FINDNEXTMATCHLINE fpFindNextMatchLine;
  642. FP_GETSTRINGFIELD fpGetStringField;
  643. FP_SETGLOBALFLAGS fpSetGlobalFlags;
  644. FP_GETGLOBALFLAGS fpGetGlobalFlags;
  645. FP_ACCESSRUNONCENODELIST fpAccessRunOnceNodeList;
  646. FP_DESTROYRUNONCENODELIST fpDestroyRunOnceNodeList;
  647. //
  648. // typdef for comctl32's DestroyPropertySheetPage API, needed in cases where
  649. // class-/co-installers supply wizard pages (that need to be destroyed).
  650. //
  651. typedef BOOL (WINAPI *FP_DESTROYPROPERTYSHEETPAGE)(HPROPSHEETPAGE);
  652. //
  653. // typedefs for ANSI and Unicode variants of rundll32 proc entrypoint.
  654. //
  655. typedef void (WINAPI *RUNDLLPROCA)(HWND hwndStub, HINSTANCE hInstance, LPSTR pszCmdLine, int nCmdShow);
  656. typedef void (WINAPI *RUNDLLPROCW)(HWND hwndStub, HINSTANCE hInstance, LPWSTR pszCmdLine, int nCmdShow);
  657. //
  658. // typedefs for Terminal Services message dispatch routines, in winsta.dll.
  659. //
  660. typedef LONG (*FP_WINSTABROADCASTSYSTEMMESSAGE)(
  661. HANDLE hServer,
  662. BOOL sendToAllWinstations,
  663. ULONG sessionID,
  664. ULONG timeOut,
  665. DWORD dwFlags,
  666. DWORD *lpdwRecipients,
  667. ULONG uiMessage,
  668. WPARAM wParam,
  669. LPARAM lParam,
  670. LONG *pResponse
  671. );
  672. typedef LONG (*FP_WINSTASENDWINDOWMESSAGE)(
  673. HANDLE hServer,
  674. ULONG sessionID,
  675. ULONG timeOut,
  676. ULONG hWnd,
  677. ULONG Msg,
  678. WPARAM wParam,
  679. LPARAM lParam,
  680. LONG *pResponse
  681. );
  682. typedef BOOLEAN (WINAPI * FP_WINSTAQUERYINFORMATIONW)(
  683. HANDLE hServer,
  684. ULONG LogonId,
  685. WINSTATIONINFOCLASS WinStationInformationClass,
  686. PVOID pWinStationInformation,
  687. ULONG WinStationInformationLength,
  688. PULONG pReturnLength
  689. );
  690. HINSTANCE ghWinStaLib = NULL;
  691. FP_WINSTASENDWINDOWMESSAGE fpWinStationSendWindowMessage = NULL;
  692. FP_WINSTABROADCASTSYSTEMMESSAGE fpWinStationBroadcastSystemMessage = NULL;
  693. FP_WINSTAQUERYINFORMATIONW fpWinStationQueryInformationW = NULL;
  694. //
  695. // typedefs for Terminal Services support routines, in wtsapi32.dll.
  696. //
  697. typedef BOOL (*FP_WTSQUERYSESSIONINFORMATION)(
  698. IN HANDLE hServer,
  699. IN DWORD SessionId,
  700. IN WTS_INFO_CLASS WTSInfoClass,
  701. OUT LPWSTR * ppBuffer,
  702. OUT DWORD * pBytesReturned
  703. );
  704. typedef VOID (*FP_WTSFREEMEMORY)(
  705. IN PVOID pMemory
  706. );
  707. HINSTANCE ghWtsApi32Lib = NULL;
  708. FP_WTSQUERYSESSIONINFORMATION fpWTSQuerySessionInformation = NULL;
  709. FP_WTSFREEMEMORY fpWTSFreeMemory = NULL;
  710. //
  711. // Service controller callback routines for authentication and notification to
  712. // services.
  713. //
  714. PSCMCALLBACK_ROUTINE pServiceControlCallback;
  715. PSCMAUTHENTICATION_CALLBACK pSCMAuthenticate;
  716. //
  717. // Device install events
  718. //
  719. #define NUM_INSTALL_EVENTS 2
  720. #define LOGGED_ON_EVENT 0
  721. #define NEEDS_INSTALL_EVENT 1
  722. HANDLE InstallEvents[NUM_INSTALL_EVENTS] = {NULL, NULL};
  723. HANDLE ghNoPendingInstalls = NULL;
  724. //
  725. // Veto definitions
  726. //
  727. #define UnknownVeto(t,n,l) { *(t) = PNP_VetoTypeUnknown; }
  728. #define WinBroadcastVeto(h,t,n,l) { *(t) = PNP_VetoWindowsApp;\
  729. GetWindowsExeFileName(h,n,l); }
  730. #define WindowVeto(e,t,n,l) { *(t) = PNP_VetoWindowsApp;\
  731. GetClientName(e,n,l); }
  732. #define ServiceVeto(e,t,n,l) { *(t) = PNP_VetoWindowsService;\
  733. GetClientName(e,n,l); }
  734. //
  735. // Sentinel for event loop control
  736. //
  737. #define PASS_COMPLETE 0x7fffffff
  738. //---------------------------------------------------------------------------
  739. // Debugging interface - initiate detection through private debug interface
  740. //---------------------------------------------------------------------------
  741. CONFIGRET
  742. PNP_InitDetection(
  743. handle_t hBinding
  744. )
  745. /*++
  746. Routine Description:
  747. This routine is a private debugging interface to initiate device detection.
  748. Arguments:
  749. hBinding - RPC binding handle, not used.
  750. Return Value:
  751. Currently returns CR_SUCCESS.
  752. Notes:
  753. Previously, this routine would kick off the InitializePnPManager thread on
  754. checked builds only.
  755. Presumably, this dates way back to a time when this routine actually sought
  756. out non-configured devices and initiated installation on them (as is
  757. currently done at the start of the ThreadProc_DeviceInstall thread procedure
  758. routine).
  759. Since InitializePnPManager no longer does this, so this behavior has been
  760. removed altogether. It is currently never valid to perform initialization
  761. more than once, however this routine may be used to implement detection of
  762. non-configured devices.
  763. --*/
  764. {
  765. UNREFERENCED_PARAMETER(hBinding);
  766. return CR_SUCCESS;
  767. } // PNP_InitDetection
  768. BOOL
  769. PnpConsoleCtrlHandler(
  770. DWORD dwCtrlType
  771. )
  772. /*++
  773. Routine Description:
  774. This routine handles control signals received by the process for the
  775. session the process is associated with.
  776. Arguments:
  777. dwCtrlType - Indicates the type of control signal received by the handler.
  778. This value is one of the following: CTRL_C_EVENT, CTRL_BREAK_EVENT,
  779. CTRL_CLOSE_EVENT, CTRL_LOGOFF_EVENT, CTRL_SHUTDOWN_EVENT
  780. Return Value:
  781. If the function handles the control signal, it should return TRUE. If it
  782. returns FALSE, the next handler function in the list of handlers for this
  783. process is used.
  784. --*/
  785. {
  786. PINSTALL_CLIENT_ENTRY pDeviceInstallClient;
  787. switch (dwCtrlType) {
  788. case CTRL_LOGOFF_EVENT:
  789. //
  790. // The system sends the logoff event to the registered console ctrl
  791. // handlers for a console process when a user is logging off from the
  792. // session associated with that process. Since UMPNPMGR runs within the
  793. // context of the services.exe process, which always resides in session
  794. // 0, that is the only session for which this handler will receive
  795. // logoff events.
  796. //
  797. KdPrintEx((DPFLTR_PNPMGR_ID,
  798. DBGF_EVENT,
  799. "UMPNPMGR: PnpConsoleCtrlHandler: CTRL_LOGOFF_EVENT: Session %d\n",
  800. MAIN_SESSION));
  801. //
  802. // Close the handle to the user access token for the main session.
  803. //
  804. ASSERT(gTokenLock.LockHandles);
  805. LockPrivateResource(&gTokenLock);
  806. if (ghUserToken) {
  807. CloseHandle(ghUserToken);
  808. ghUserToken = NULL;
  809. }
  810. UnlockPrivateResource(&gTokenLock);
  811. //
  812. // If the main session was the active Console session, (or should be
  813. // treated as the active console session because Fast User Switching is
  814. // disabled) when the user logged off, reset the "logged on" event.
  815. //
  816. if (IsConsoleSession(MAIN_SESSION)) {
  817. if (InstallEvents[LOGGED_ON_EVENT]) {
  818. ResetEvent(InstallEvents[LOGGED_ON_EVENT]);
  819. KdPrintEx((DPFLTR_PNPMGR_ID,
  820. DBGF_INSTALL | DBGF_EVENT,
  821. "UMPNPMGR: PnpConsoleCtrlHandler: CTRL_LOGOFF_EVENT: ResetEvent LOGGED_ON_EVENT\n"));
  822. }
  823. }
  824. //
  825. // If we currently have a device install UI client on this session,
  826. // we should attempt to close it now, before logging off.
  827. //
  828. LockNotifyList(&InstallClientList.Lock);
  829. pDeviceInstallClient = LocateDeviceInstallClient(MAIN_SESSION);
  830. if (pDeviceInstallClient) {
  831. DereferenceDeviceInstallClient(pDeviceInstallClient);
  832. }
  833. UnlockNotifyList(&InstallClientList.Lock);
  834. break;
  835. default:
  836. //
  837. // No special processing for any other events.
  838. //
  839. break;
  840. }
  841. //
  842. // Returning FALSE passes this control to the next registered CtrlHandler in
  843. // the list of handlers for this process (services.exe), so that other
  844. // services will get a chance to look at this.
  845. //
  846. return FALSE;
  847. } // PnpConsoleCtrlHandler
  848. DWORD
  849. InitializePnPManager(
  850. LPDWORD lpParam
  851. )
  852. /*++
  853. Routine Description:
  854. This thread routine is created from srventry.c when services.exe
  855. attempts to start the plug and play service. The init routine in
  856. srventry.c does critical initialize then creates this thread to
  857. do pnp initialization so that it can return back to the service
  858. controller before pnp init completes.
  859. Arguments:
  860. lpParam - Not used.
  861. Return Value:
  862. Currently returns TRUE/FALSE.
  863. --*/
  864. {
  865. DWORD dwStatus = TRUE;
  866. DWORD ThreadID = 0;
  867. HANDLE hThread = NULL, hEventThread = NULL;
  868. HKEY hKey = NULL;
  869. LONG status;
  870. BOOL bGuiModeSetup = FALSE, bFactoryPreInstall = FALSE;
  871. ULONG ulSize, ulValue;
  872. UNREFERENCED_PARAMETER(lpParam);
  873. KdPrintEx((DPFLTR_PNPMGR_ID,
  874. DBGF_EVENT,
  875. "UMPNPMGR: InitializePnPManager\n"));
  876. //
  877. // Initialize events that will control when to install devices later.
  878. //
  879. InstallEvents[LOGGED_ON_EVENT] = CreateEvent(NULL, TRUE, FALSE, NULL);
  880. if (InstallEvents[LOGGED_ON_EVENT] == NULL) {
  881. LogErrorEvent(ERR_CREATING_LOGON_EVENT, GetLastError(), 0);
  882. }
  883. InstallEvents[NEEDS_INSTALL_EVENT] = CreateEvent(NULL, TRUE, FALSE, NULL);
  884. if (InstallEvents[NEEDS_INSTALL_EVENT] == NULL) {
  885. LogErrorEvent(ERR_CREATING_INSTALL_EVENT, GetLastError(), 0);
  886. }
  887. //
  888. // Create the pending install event.
  889. //
  890. if (!CreateNoPendingInstallEvent()) {
  891. LogErrorEvent(ERR_CREATING_PENDING_INSTALL_EVENT, GetLastError(), 0);
  892. }
  893. ASSERT(ghNoPendingInstalls != NULL);
  894. //
  895. // Initialize event to control access to the current session during session
  896. // change events. The event state is initially signalled since this service
  897. // initializes when only session 0 exists (prior to the initialization of
  898. // termsrv, or the creation of any other sessions).
  899. //
  900. ghActiveConsoleSessionEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
  901. if (ghActiveConsoleSessionEvent == NULL) {
  902. KdPrintEx((DPFLTR_PNPMGR_ID,
  903. DBGF_INSTALL | DBGF_ERRORS,
  904. "UMPNPMGR: Failed to initialize ghActiveConsoleSessionEvent!!, error = %d\n",
  905. GetLastError()));
  906. }
  907. //
  908. // Setup a console control handler so that I can keep track of logoffs to
  909. // the main session (SessionId 0). This is still necessary because Terminal
  910. // Services may not always be available. (see PNP_ReportLogOn).
  911. // (I only get logoff notification via this handler so I still
  912. // rely on the kludge in userinit.exe to tell me about logons).
  913. //
  914. if (!SetConsoleCtrlHandler(PnpConsoleCtrlHandler, TRUE)) {
  915. KdPrintEx((DPFLTR_PNPMGR_ID,
  916. DBGF_EVENT | DBGF_ERRORS,
  917. "UMPNPMGR: SetConsoleCtrlHandler failed, error = %d\n",
  918. GetLastError()));
  919. }
  920. //
  921. // acquire a mutex now to make sure I get through this
  922. // initialization task before getting pinged by a logon
  923. //
  924. ghInitMutex = CreateMutex(NULL, TRUE, PNP_INIT_MUTEX);
  925. if (ghInitMutex == NULL) {
  926. ASSERT(0);
  927. return FALSE;
  928. }
  929. try {
  930. //
  931. // Check if we're running during one of the assorted flavors of setup.
  932. //
  933. status = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  934. TEXT("SYSTEM\\Setup"),
  935. 0,
  936. KEY_READ,
  937. &hKey);
  938. if (status == ERROR_SUCCESS) {
  939. //
  940. // Determine if factory pre-install is in progress.
  941. //
  942. ulValue = 0;
  943. ulSize = sizeof(ulValue);
  944. status = RegQueryValueEx(hKey,
  945. TEXT("FactoryPreInstallInProgress"),
  946. NULL,
  947. NULL,
  948. (LPBYTE)&ulValue,
  949. &ulSize);
  950. if ((status == ERROR_SUCCESS) && (ulValue == 1)) {
  951. bFactoryPreInstall = TRUE;
  952. gbSuppressUI = TRUE;
  953. KdPrintEx((DPFLTR_PNPMGR_ID,
  954. DBGF_INSTALL | DBGF_EVENT,
  955. "UMPNPMGR: Will suppress all UI in Factory Mode\n"));
  956. LogWarningEvent(WRN_FACTORY_UI_SUPPRESSED, 0, NULL);
  957. }
  958. if (!bFactoryPreInstall) {
  959. //
  960. // Determine if Gui Mode Setup is in progress (but not mini-setup).
  961. //
  962. ulValue = 0;
  963. ulSize = sizeof(ulValue);
  964. status = RegQueryValueEx(hKey,
  965. TEXT("SystemSetupInProgress"),
  966. NULL,
  967. NULL,
  968. (LPBYTE)&ulValue,
  969. &ulSize);
  970. if (status == ERROR_SUCCESS) {
  971. bGuiModeSetup = (ulValue == 1);
  972. }
  973. if (bGuiModeSetup) {
  974. //
  975. // Well, we're in GUI-mode setup, but we need to make sure
  976. // we're not in mini-setup, or factory pre-install. We
  977. // treat mini-setup like any other boot of the system, and
  978. // factory pre-install is a delayed version of a normal
  979. // boot.
  980. //
  981. ulValue = 0;
  982. ulSize = sizeof(ulValue);
  983. status = RegQueryValueEx(hKey,
  984. TEXT("MiniSetupInProgress"),
  985. NULL,
  986. NULL,
  987. (LPBYTE)&ulValue,
  988. &ulSize);
  989. if ((status == ERROR_SUCCESS) && (ulValue == 1)) {
  990. //
  991. // Well, we're in mini-setup, but we need to make sure
  992. // that he doesn't want us to do PnP re-enumeration.
  993. //
  994. ulValue = 0;
  995. ulSize = sizeof(ulValue);
  996. status = RegQueryValueEx(hKey,
  997. TEXT("MiniSetupDoPnP"),
  998. NULL,
  999. NULL,
  1000. (LPBYTE)&ulValue,
  1001. &ulSize);
  1002. if ((status != ERROR_SUCCESS) || (ulValue == 0)) {
  1003. //
  1004. // Nope. Treat this like any other boot of the
  1005. // system.
  1006. //
  1007. bGuiModeSetup = FALSE;
  1008. }
  1009. }
  1010. }
  1011. }
  1012. //
  1013. // Determine if this is an OOBE boot.
  1014. //
  1015. ulValue = 0;
  1016. ulSize = sizeof(ulValue);
  1017. status = RegQueryValueEx(hKey,
  1018. TEXT("OobeInProgress"),
  1019. NULL,
  1020. NULL,
  1021. (LPBYTE)&ulValue,
  1022. &ulSize);
  1023. if (status == ERROR_SUCCESS) {
  1024. gbOobeInProgress = (ulValue == 1);
  1025. }
  1026. //
  1027. // Close the SYSTEM\Setup key.
  1028. //
  1029. RegCloseKey(hKey);
  1030. hKey = NULL;
  1031. } else {
  1032. KdPrintEx((DPFLTR_PNPMGR_ID,
  1033. DBGF_INSTALL,
  1034. "UMPNPMGR: Failure opening key SYSTEM\\Setup (%d)\n",
  1035. status));
  1036. }
  1037. //
  1038. // If this is EmbeddedNT, check whether PNP should display UI.
  1039. // Note that this is only checked once per system boot, when the
  1040. // service is initialized.
  1041. //
  1042. if (IsEmbeddedNT()) {
  1043. if (RegOpenKeyEx(ghServicesKey,
  1044. pszRegKeyPlugPlayServiceParams,
  1045. 0,
  1046. KEY_READ,
  1047. &hKey) == ERROR_SUCCESS) {
  1048. ulValue = 0;
  1049. ulSize = sizeof(ulValue);
  1050. if ((RegQueryValueEx(hKey,
  1051. TEXT("SuppressUI"),
  1052. NULL,
  1053. NULL,
  1054. (LPBYTE)&ulValue,
  1055. &ulSize) == ERROR_SUCCESS) && (ulValue == 1)) {
  1056. gbSuppressUI = TRUE;
  1057. KdPrintEx((DPFLTR_PNPMGR_ID,
  1058. DBGF_INSTALL | DBGF_EVENT,
  1059. "UMPNPMGR: Will suppress all UI on EmbeddedNT\n"));
  1060. LogWarningEvent(WRN_EMBEDDEDNT_UI_SUPPRESSED, 0, NULL);
  1061. }
  1062. RegCloseKey(hKey);
  1063. }
  1064. }
  1065. //
  1066. // Initialize the interfaces to Hydra, if Hydra is running on this system.
  1067. //
  1068. if (IsTerminalServer()) {
  1069. KdPrintEx((DPFLTR_PNPMGR_ID,
  1070. DBGF_EVENT,
  1071. "UMPNPMGR: Initializing interfaces to Terminal Services.\n"));
  1072. if (!InitializeHydraInterface()) {
  1073. KdPrintEx((DPFLTR_PNPMGR_ID,
  1074. DBGF_WARNINGS | DBGF_EVENT,
  1075. "UMPNPMGR: Failed to initialize interfaces to Terminal Services!\n"));
  1076. }
  1077. }
  1078. //
  1079. // Initialize the global drive letter mask
  1080. //
  1081. gAllDrivesMask = GetAllVolumeMountPoints();
  1082. //
  1083. // Create a thread that monitors device events.
  1084. //
  1085. hEventThread = CreateThread(NULL,
  1086. 0,
  1087. (LPTHREAD_START_ROUTINE)ThreadProc_DeviceEvent,
  1088. NULL,
  1089. 0,
  1090. &ThreadID);
  1091. //
  1092. // Create the appropriate thread to handle the device installation.
  1093. // The two cases are when gui mode setup is in progress and for
  1094. // a normal user boot case.
  1095. //
  1096. if (bFactoryPreInstall) {
  1097. //
  1098. // FactoryPreInstallInProgress
  1099. //
  1100. hThread = CreateThread(NULL,
  1101. 0,
  1102. (LPTHREAD_START_ROUTINE)ThreadProc_FactoryPreinstallDeviceInstall,
  1103. NULL,
  1104. 0,
  1105. &ThreadID);
  1106. } else if (bGuiModeSetup) {
  1107. //
  1108. // SystemSetupInProgress,
  1109. // including MiniSetupInProgress with MiniSetupDoPnP
  1110. //
  1111. hThread = CreateThread(NULL,
  1112. 0,
  1113. (LPTHREAD_START_ROUTINE)ThreadProc_GuiSetupDeviceInstall,
  1114. NULL,
  1115. 0,
  1116. &ThreadID);
  1117. } else {
  1118. //
  1119. // Standard system boot, or
  1120. // SystemSetupInProgress with MiniSetupInProgress (but not MiniSetupDoPnP)
  1121. //
  1122. hThread = CreateThread(NULL,
  1123. 0,
  1124. (LPTHREAD_START_ROUTINE)ThreadProc_DeviceInstall,
  1125. NULL,
  1126. 0,
  1127. &ThreadID);
  1128. }
  1129. } except(EXCEPTION_EXECUTE_HANDLER) {
  1130. KdPrintEx((DPFLTR_PNPMGR_ID,
  1131. DBGF_ERRORS | DBGF_EVENT,
  1132. "UMPNPMGR: Exception in InitializePnPManager!\n"));
  1133. ASSERT(0);
  1134. dwStatus = FALSE;
  1135. //
  1136. // Reference the following variables so the compiler will respect
  1137. // statement ordering w.r.t. their assignment.
  1138. //
  1139. hThread = hThread;
  1140. hEventThread = hEventThread;
  1141. }
  1142. //
  1143. // signal the init mutex so that logon init activity can procede
  1144. //
  1145. ReleaseMutex(ghInitMutex);
  1146. if (hThread != NULL) {
  1147. CloseHandle(hThread);
  1148. }
  1149. if (hEventThread != NULL) {
  1150. CloseHandle(hEventThread);
  1151. }
  1152. return dwStatus;
  1153. } // InitializePnPManager
  1154. //------------------------------------------------------------------------
  1155. // Post Log-On routines
  1156. //------------------------------------------------------------------------
  1157. CONFIGRET
  1158. PNP_ReportLogOn(
  1159. IN handle_t hBinding,
  1160. IN BOOL bAdmin,
  1161. IN DWORD ProcessID
  1162. )
  1163. /*++
  1164. Routine Description:
  1165. This routine is used to report logon events. It is called from the
  1166. userinit.exe process during logon, via CMP_Report_LogOn.
  1167. Arguments:
  1168. hBinding - RPC binding handle.
  1169. bAdmin - Not used.
  1170. ProcessID - Process ID of the userinit.exe process that will be used to
  1171. retrieve the access token for the user associated with this
  1172. logon.
  1173. Return Value:
  1174. Return CR_SUCCESS if the function succeeds, CR_FAILURE otherwise.
  1175. Notes:
  1176. When a user logs on to the console session, we signal the "logged on" event,
  1177. which will wake the device installation thread to perform any pending
  1178. client-side device install events.
  1179. Client-side device installation, requires the user access token to create a
  1180. rundll32 process in the logged on user's security context.
  1181. Although Terminal Services is now always running on all flavors of Whistler,
  1182. it is not started during safe mode. It may also not be started by the time
  1183. session 0 is available for logon as the Console session. For those reasons,
  1184. SessionId 0 is still treated differently from the other sessions.
  1185. Since Terminal Services may not be available during a logon to session 0, we
  1186. cache a handle to the access token associated with the userinit.exe process.
  1187. The handle is closed when we receive a logoff event for our process's
  1188. session (SessionId 0), via PnpConsoleCtrlHandler.
  1189. Handles to user access tokens for all other sessions are retrieved on
  1190. demand, using GetWinStationUserToken, since Terminal Services must
  1191. necessarily be available for the creation of those sessions.
  1192. --*/
  1193. {
  1194. CONFIGRET Status = CR_SUCCESS;
  1195. HANDLE hUserProcess = NULL;
  1196. RPC_STATUS rpcStatus;
  1197. DWORD dwWait;
  1198. ULONG ulSessionId;
  1199. PWSTR MultiSzGuidList = NULL;
  1200. UNREFERENCED_PARAMETER(bAdmin);
  1201. //
  1202. // Wait for the init mutex - this ensures that the pnp init
  1203. // routine (called when the service starts) has had a chance
  1204. // to complete first.
  1205. //
  1206. if (ghInitMutex != NULL) {
  1207. dwWait = WaitForSingleObject(ghInitMutex, 180000); // 3 minutes
  1208. if (dwWait != WAIT_OBJECT_0) {
  1209. //
  1210. // mutex was abandoned or timed out during the wait,
  1211. // don't attempt any further init activity
  1212. //
  1213. return CR_FAILURE;
  1214. }
  1215. }
  1216. try {
  1217. //
  1218. // Make sure that the caller is a member of the interactive group.
  1219. //
  1220. if (!IsClientInteractive(hBinding)) {
  1221. Status = CR_FAILURE;
  1222. goto Clean0;
  1223. }
  1224. //
  1225. // Impersonate the client and retrieve the SessionId.
  1226. //
  1227. rpcStatus = RpcImpersonateClient(hBinding);
  1228. if (rpcStatus != RPC_S_OK) {
  1229. KdPrintEx((DPFLTR_PNPMGR_ID,
  1230. DBGF_ERRORS,
  1231. "UMPNPMGR: PNP_ReportLogOn: RpcImpersonateClient failed, error = %d\n",
  1232. rpcStatus));
  1233. Status = CR_FAILURE;
  1234. goto Clean0;
  1235. }
  1236. //
  1237. // Keep track of the client's session.
  1238. //
  1239. ulSessionId = GetClientLogonId();
  1240. KdPrintEx((DPFLTR_PNPMGR_ID,
  1241. DBGF_EVENT,
  1242. "UMPNPMGR: PNP_ReportLogOn: SessionId %d\n",
  1243. ulSessionId));
  1244. //
  1245. // NTRAID #181685-2000/09/11-jamesca:
  1246. //
  1247. // Currently, terminal services send notification of logons to
  1248. // "remote" sessions before the server's process creation thread is
  1249. // running. If we set the logged on event, and there are devices
  1250. // waiting to be installed, we will immediately call
  1251. // CreateProcessAsUser on that session, which will fail. As a
  1252. // (temporary?) workaround, we'll continue to use PNP_ReportLogOn to
  1253. // receive logon notification from userinit.exe, now for all sessions.
  1254. //
  1255. //
  1256. // If this is a logon to SessionId 0, save a handle to the access token
  1257. // associated with the userinit.exe process. We need this later to
  1258. // create a rundll32 process in the logged on user's security context
  1259. // for client-side device installation and hotplug notifications.
  1260. //
  1261. if (ulSessionId == MAIN_SESSION) {
  1262. ASSERT(gTokenLock.LockHandles);
  1263. LockPrivateResource(&gTokenLock);
  1264. //
  1265. // We should have gotten rid of the cached user token during logoff,
  1266. // so if we still have one, ignore this spurious logon report.
  1267. //
  1268. //ASSERT(ghUserToken == NULL);
  1269. if (ghUserToken == NULL) {
  1270. //
  1271. // While still impersonating the client, open a handle to the user
  1272. // access token of the calling process (userinit.exe).
  1273. //
  1274. hUserProcess = OpenProcess(PROCESS_ALL_ACCESS, TRUE, ProcessID);
  1275. if (hUserProcess) {
  1276. OpenProcessToken(hUserProcess, TOKEN_ALL_ACCESS, &ghUserToken);
  1277. CloseHandle(hUserProcess);
  1278. } else {
  1279. KdPrintEx((DPFLTR_PNPMGR_ID,
  1280. DBGF_ERRORS,
  1281. "UMPNPMGR: PNP_ReportLogOn: OpenProcess failed, error = %d\n",
  1282. rpcStatus));
  1283. ASSERT(0);
  1284. }
  1285. }
  1286. ASSERT(ghUserToken);
  1287. UnlockPrivateResource(&gTokenLock);
  1288. }
  1289. //
  1290. // Stop impersonating.
  1291. //
  1292. rpcStatus = RpcRevertToSelf();
  1293. if (rpcStatus != RPC_S_OK) {
  1294. KdPrintEx((DPFLTR_PNPMGR_ID,
  1295. DBGF_ERRORS,
  1296. "UMPNPMGR: PNP_ReportLogOn: RpcRevertToSelf failed, error = %d\n",
  1297. rpcStatus));
  1298. ASSERT(0);
  1299. }
  1300. //
  1301. // If this is a logon to the "Console" session, signal the event that
  1302. // indicates a Console user is currently logged on.
  1303. //
  1304. if (IsConsoleSession(ulSessionId)) {
  1305. if (InstallEvents[LOGGED_ON_EVENT]) {
  1306. SetEvent(InstallEvents[LOGGED_ON_EVENT]);
  1307. KdPrintEx((DPFLTR_PNPMGR_ID,
  1308. DBGF_EVENT | DBGF_INSTALL,
  1309. "UMPNPMGR: PNP_ReportLogOn: SetEvent LOGGED_ON_EVENT\n"));
  1310. }
  1311. }
  1312. //
  1313. // For every logon to every session, send a generic blocked driver
  1314. // notification if the system has blocked any drivers from loading so
  1315. // far this boot.
  1316. //
  1317. MultiSzGuidList = BuildBlockedDriverList((LPGUID)NULL, 0);
  1318. if (MultiSzGuidList != NULL) {
  1319. SendHotplugNotification((LPGUID)&GUID_DRIVER_BLOCKED,
  1320. NULL,
  1321. MultiSzGuidList,
  1322. &ulSessionId,
  1323. 0);
  1324. HeapFree(ghPnPHeap, 0, MultiSzGuidList);
  1325. MultiSzGuidList = NULL;
  1326. }
  1327. Clean0:
  1328. NOTHING;
  1329. } except(EXCEPTION_EXECUTE_HANDLER) {
  1330. KdPrintEx((DPFLTR_PNPMGR_ID,
  1331. DBGF_ERRORS,
  1332. "UMPNPMGR: Exception in PNP_ReportLogOn\n"));
  1333. ASSERT(0);
  1334. Status = CR_FAILURE;
  1335. //
  1336. // Reference the following variable so the compiler will respect
  1337. // statement ordering w.r.t. its assignment.
  1338. //
  1339. ghInitMutex = ghInitMutex;
  1340. MultiSzGuidList = MultiSzGuidList;
  1341. }
  1342. if (ghInitMutex != NULL) {
  1343. ReleaseMutex(ghInitMutex);
  1344. }
  1345. if (MultiSzGuidList != NULL) {
  1346. HeapFree(ghPnPHeap, 0, MultiSzGuidList);
  1347. }
  1348. return Status;
  1349. } // PNP_ReportLogon
  1350. typedef struct _DEVICE_INSTALL_ENTRY {
  1351. SIZE_T Index;
  1352. ULONG Depth;
  1353. }DEVICE_INSTALL_ENTRY, *PDEVICE_INSTALL_ENTRY;
  1354. int
  1355. __cdecl
  1356. compare_depth(
  1357. const void *a,
  1358. const void *b
  1359. )
  1360. {
  1361. PDEVICE_INSTALL_ENTRY entry1, entry2;
  1362. entry1 = (PDEVICE_INSTALL_ENTRY)a;
  1363. entry2 = (PDEVICE_INSTALL_ENTRY)b;
  1364. if (entry1->Depth > entry2->Depth) {
  1365. return -1;
  1366. } else if (entry1->Depth < entry2->Depth) {
  1367. return 1;
  1368. }
  1369. return 0;
  1370. }
  1371. DWORD
  1372. ThreadProc_GuiSetupDeviceInstall(
  1373. LPDWORD lpThreadParam
  1374. )
  1375. /*++
  1376. Routine Description:
  1377. This routine is a thread procedure. This thread is only active during GUI-mode setup
  1378. and passes device notifications down a pipe to setup.
  1379. There are two passes, which *must* match exactly with the two passes in GUI-setup
  1380. Once the passes are complete, we proceed to Phase-2 of normal serverside install
  1381. Arguments:
  1382. lpThreadParam - Not used.
  1383. Return Value:
  1384. Not used, currently returns result of ThreadProc_DeviceInstall - which will normally not return
  1385. --*/
  1386. {
  1387. CONFIGRET Status = CR_SUCCESS;
  1388. LPWSTR pDeviceList = NULL, pszDevice = NULL;
  1389. ULONG ulSize = 0, ulProblem = 0, ulStatus = 0, ulConfig, Pass, threadID;
  1390. ULONG ulPostSetupSkipPhase1 = TRUE;
  1391. HANDLE hPipeEvent = NULL, hPipe = NULL, hBatchEvent = NULL, hThread = NULL;
  1392. PPNP_INSTALL_ENTRY entry = NULL;
  1393. PDEVICE_INSTALL_ENTRY pSortArray = NULL;
  1394. LONG lCount;
  1395. BOOL needsInstall;
  1396. ULONG ulReenumerationCount;
  1397. UNREFERENCED_PARAMETER(lpThreadParam);
  1398. try {
  1399. //
  1400. // 2 Passes, must match up with the 2 passes in SysSetup.
  1401. // generally, most, if not all devices, will be picked up
  1402. // and installed by syssetup
  1403. //
  1404. for (Pass = 1; Pass <= 2; Pass++) {
  1405. ulReenumerationCount = 0;
  1406. //
  1407. // If Gui mode setup is in progress, we don't need to wait for a logon
  1408. // event. Just wait on the event that indicates when gui mode setup
  1409. // has opened the pipe and is ready to recieve device names. Attempt to
  1410. // create the event first (in case I beat setup to it), if it exists
  1411. // already then just open it by name. This is a manual reset event.
  1412. //
  1413. hPipeEvent = CreateEvent(NULL, TRUE, FALSE, PNP_CREATE_PIPE_EVENT);
  1414. if (!hPipeEvent) {
  1415. if (GetLastError() == ERROR_ALREADY_EXISTS) {
  1416. hPipeEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, PNP_CREATE_PIPE_EVENT);
  1417. if (!hPipeEvent) {
  1418. Status = CR_FAILURE;
  1419. goto Clean0;
  1420. }
  1421. } else {
  1422. Status = CR_FAILURE;
  1423. goto Clean0;
  1424. }
  1425. }
  1426. if (WaitForSingleObject(hPipeEvent, INFINITE) != WAIT_OBJECT_0) {
  1427. Status = CR_FAILURE;
  1428. goto Clean0; // event must have been abandoned
  1429. }
  1430. //
  1431. // Reset the manual-reset event back to the non-signalled state.
  1432. //
  1433. ResetEvent(hPipeEvent);
  1434. hBatchEvent = CreateEvent(NULL, TRUE, FALSE, PNP_BATCH_PROCESSED_EVENT);
  1435. if (!hBatchEvent) {
  1436. if (GetLastError() == ERROR_ALREADY_EXISTS) {
  1437. hBatchEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, PNP_BATCH_PROCESSED_EVENT);
  1438. if (!hBatchEvent) {
  1439. Status = CR_FAILURE;
  1440. goto Clean0;
  1441. }
  1442. } else {
  1443. Status = CR_FAILURE;
  1444. goto Clean0;
  1445. }
  1446. }
  1447. //
  1448. // Open the client side of the named pipe, the server side was opened
  1449. // by gui mode setup.
  1450. //
  1451. if (!WaitNamedPipe(PNP_NEW_HW_PIPE, PNP_PIPE_TIMEOUT)) {
  1452. KdPrintEx((DPFLTR_PNPMGR_ID,
  1453. DBGF_INSTALL | DBGF_ERRORS,
  1454. "UMPNPMGR: ThreadProc_GuiSetupDeviceInstall: WaitNamedPipe failed!\n"));
  1455. Status = CR_FAILURE;
  1456. goto Clean0;
  1457. }
  1458. hPipe = CreateFile(PNP_NEW_HW_PIPE,
  1459. GENERIC_WRITE,
  1460. 0,
  1461. NULL,
  1462. OPEN_EXISTING,
  1463. FILE_ATTRIBUTE_NORMAL,
  1464. NULL);
  1465. if (hPipe == INVALID_HANDLE_VALUE) {
  1466. LogErrorEvent(ERR_CREATING_SETUP_PIPE, GetLastError(), 0);
  1467. Status = CR_FAILURE;
  1468. goto Clean0;
  1469. }
  1470. //
  1471. // Retreive the list of all devices for all enumerators
  1472. // Start out with a reasonably-sized buffer (16K characters) in
  1473. // hopes of avoiding 2 calls to get the device list.
  1474. //
  1475. ulSize = 16384;
  1476. for ( ; ; ) {
  1477. pDeviceList = HeapAlloc(ghPnPHeap, 0, ulSize * sizeof(WCHAR));
  1478. if (pDeviceList == NULL) {
  1479. Status = CR_OUT_OF_MEMORY;
  1480. goto Clean0;
  1481. }
  1482. Status = PNP_GetDeviceList(NULL, NULL, pDeviceList, &ulSize, 0);
  1483. if (Status == CR_SUCCESS) {
  1484. break;
  1485. } else if(Status == CR_BUFFER_SMALL) {
  1486. //
  1487. // Our initial buffer wasn't large enough. Free the current
  1488. // buffer.
  1489. //
  1490. HeapFree(ghPnPHeap, 0, pDeviceList);
  1491. pDeviceList = NULL;
  1492. //
  1493. // Now, go ahead and make the call to retrieve the actual
  1494. // size required.
  1495. //
  1496. Status = PNP_GetDeviceListSize(NULL, NULL, &ulSize, 0);
  1497. if (Status != CR_SUCCESS) {
  1498. goto Clean0;
  1499. }
  1500. } else {
  1501. //
  1502. // We failed for some reason other than buffer-too-small.
  1503. // Bail now.
  1504. //
  1505. goto Clean0;
  1506. }
  1507. }
  1508. //
  1509. // Count the number of devices we are installing.
  1510. //
  1511. for (pszDevice = pDeviceList, lCount = 0;
  1512. *pszDevice;
  1513. pszDevice += lstrlen(pszDevice) + 1, lCount++) {
  1514. }
  1515. pSortArray = HeapAlloc(ghPnPHeap, 0, lCount * sizeof(DEVICE_INSTALL_ENTRY));
  1516. if (pSortArray) {
  1517. NTSTATUS ntStatus;
  1518. PLUGPLAY_CONTROL_DEPTH_DATA depthData;
  1519. LPWSTR pTempList;
  1520. //
  1521. // Initialize all the information we need to sort devices.
  1522. //
  1523. for (pszDevice = pDeviceList, lCount = 0;
  1524. *pszDevice;
  1525. pszDevice += lstrlen(pszDevice) + 1, lCount++) {
  1526. pSortArray[lCount].Index = pszDevice - pDeviceList;
  1527. depthData.DeviceDepth = 0;
  1528. RtlInitUnicodeString(&depthData.DeviceInstance, pszDevice);
  1529. ntStatus = NtPlugPlayControl(PlugPlayControlGetDeviceDepth,
  1530. &depthData,
  1531. sizeof(depthData));
  1532. pSortArray[lCount].Depth = depthData.DeviceDepth;
  1533. }
  1534. //
  1535. // Sort the array so that deeper devices are ahead.
  1536. //
  1537. qsort(pSortArray, lCount, sizeof(DEVICE_INSTALL_ENTRY), compare_depth);
  1538. //
  1539. // Copy the data so that the device instance strings are sorted.
  1540. //
  1541. pTempList = HeapAlloc(ghPnPHeap, 0, ulSize * sizeof(WCHAR));
  1542. if (pTempList) {
  1543. for (pszDevice = pTempList, lCount--; lCount >= 0; lCount--) {
  1544. lstrcpy(pszDevice, &pDeviceList[pSortArray[lCount].Index]);
  1545. pszDevice += lstrlen(pszDevice) + 1;
  1546. }
  1547. *pszDevice = TEXT('\0');
  1548. HeapFree(ghPnPHeap, 0, pDeviceList);
  1549. pDeviceList = pTempList;
  1550. }
  1551. HeapFree(ghPnPHeap, 0, pSortArray);
  1552. }
  1553. //
  1554. // PHASE 1
  1555. //
  1556. // Search the registry for devices to install.
  1557. //
  1558. for (pszDevice = pDeviceList;
  1559. *pszDevice;
  1560. pszDevice += lstrlen(pszDevice) + 1) {
  1561. //
  1562. // Is device present?
  1563. //
  1564. if (IsDeviceIdPresent(pszDevice)) {
  1565. if (Pass == 1) {
  1566. //
  1567. // First time through, pass everything in the registry to
  1568. // guimode setup via the pipe, whether they are marked as
  1569. // needing to be installed or not.
  1570. //
  1571. if (!WriteFile(hPipe,
  1572. pszDevice,
  1573. (lstrlen(pszDevice)+1) * sizeof(WCHAR),
  1574. &ulSize,
  1575. NULL)) {
  1576. LogErrorEvent(ERR_WRITING_SETUP_PIPE, GetLastError(), 0);
  1577. }
  1578. } else {
  1579. //
  1580. // Second time through, only pass along anything
  1581. // that is marked as needing to be installed.
  1582. //
  1583. DevInstNeedsInstall(pszDevice, &needsInstall);
  1584. if (needsInstall) {
  1585. if (!WriteFile(hPipe,
  1586. pszDevice,
  1587. (lstrlen(pszDevice)+1) * sizeof(WCHAR),
  1588. &ulSize,
  1589. NULL)) {
  1590. LogErrorEvent(ERR_WRITING_SETUP_PIPE, GetLastError(), 0);
  1591. }
  1592. }
  1593. }
  1594. } else if (Pass == 1) {
  1595. //
  1596. // device ID is not present
  1597. // we should have marked this as needs re-install
  1598. //
  1599. ulConfig = GetDeviceConfigFlags(pszDevice, NULL);
  1600. if ((ulConfig & CONFIGFLAG_REINSTALL)==0) {
  1601. KdPrintEx((DPFLTR_PNPMGR_ID,
  1602. DBGF_INSTALL | DBGF_WARNINGS,
  1603. "UMPNPMGR: Setup - %ws not present and not marked as needing reinstall - setting CONFIGFLAG_REINSTALL\n",
  1604. pszDevice));
  1605. ulConfig |= CONFIGFLAG_REINSTALL;
  1606. PNP_SetDeviceRegProp(NULL,
  1607. pszDevice,
  1608. CM_DRP_CONFIGFLAGS,
  1609. REG_DWORD,
  1610. (LPBYTE)&ulConfig,
  1611. sizeof(ulConfig),
  1612. 0
  1613. );
  1614. }
  1615. }
  1616. }
  1617. //
  1618. // PHASE 2
  1619. //
  1620. do {
  1621. //
  1622. // Write a NULL ID to indicate end of this batch.
  1623. //
  1624. if (!WriteFile(hPipe,
  1625. TEXT(""),
  1626. sizeof(WCHAR),
  1627. &ulSize,
  1628. NULL)) {
  1629. LogErrorEvent(ERR_WRITING_SETUP_PIPE, GetLastError(), 0);
  1630. }
  1631. //
  1632. // Wait for gui mode setup to complete processing of the last
  1633. // batch.
  1634. //
  1635. if (WaitForSingleObject(hBatchEvent,
  1636. PNP_GUISETUP_INSTALL_TIMEOUT) != WAIT_OBJECT_0) {
  1637. //
  1638. // The event was either abandoned or timed out, give up.
  1639. //
  1640. goto Clean1;
  1641. }
  1642. ResetEvent(hBatchEvent);
  1643. //
  1644. // Reenumerate the tree from the ROOT on a separate thread.
  1645. //
  1646. hThread = CreateThread(NULL,
  1647. 0,
  1648. (LPTHREAD_START_ROUTINE)ThreadProc_ReenumerateDeviceTree,
  1649. (LPVOID)pszRegRootEnumerator,
  1650. 0,
  1651. &threadID);
  1652. if (hThread == NULL) {
  1653. goto Clean1;
  1654. }
  1655. if (WaitForSingleObject(hThread,
  1656. PNP_GUISETUP_INSTALL_TIMEOUT) != WAIT_OBJECT_0) {
  1657. //
  1658. // The event was either abandadoned or timed out, give up.
  1659. //
  1660. goto Clean1;
  1661. }
  1662. //
  1663. // Check if we have reenumerated for too long.
  1664. //
  1665. if (++ulReenumerationCount >= MAX_REENUMERATION_COUNT) {
  1666. //
  1667. // Either something is wrong with one of the enumerators in
  1668. // the system (more likely) or this device tree is
  1669. // unreasonably deep. In the latter case, the remaining
  1670. // devices will get installed post setup.
  1671. //
  1672. KdPrintEx((DPFLTR_PNPMGR_ID,
  1673. DBGF_INSTALL | DBGF_ERRORS,
  1674. "UMPNPMGR: ThreadProc_GuiSetupDeviceInstall: Reenumerated %d times, some enumerator is misbehaving!\n", ulReenumerationCount));
  1675. ASSERT(ulReenumerationCount < MAX_REENUMERATION_COUNT);
  1676. goto Clean1;
  1677. }
  1678. //
  1679. // If we dont have any devices in the install list, we are done.
  1680. //
  1681. if (InstallList.Next == NULL) {
  1682. break;
  1683. }
  1684. //
  1685. // Install any new devices found as a result of setting up the
  1686. // previous batch of devices.
  1687. //
  1688. lCount = 0;
  1689. LockNotifyList(&InstallList.Lock);
  1690. while (InstallList.Next != NULL) {
  1691. //
  1692. // Retrieve and remove the first (oldest) entry in the
  1693. // install device list.
  1694. //
  1695. entry = (PPNP_INSTALL_ENTRY)InstallList.Next;
  1696. InstallList.Next = entry->Next;
  1697. UnlockNotifyList(&InstallList.Lock);
  1698. ASSERT(!(entry->Flags & (PIE_SERVER_SIDE_INSTALL_ATTEMPTED | PIE_DEVICE_INSTALL_REQUIRED_REBOOT)));
  1699. //
  1700. // Should we install this device?
  1701. //
  1702. DevInstNeedsInstall(entry->szDeviceId, &needsInstall);
  1703. if (needsInstall) {
  1704. //
  1705. // Give this device name to gui mode setup via the pipe
  1706. //
  1707. if (!WriteFile(hPipe,
  1708. entry->szDeviceId,
  1709. (lstrlen(entry->szDeviceId)+1) * sizeof(WCHAR),
  1710. &ulSize,
  1711. NULL)) {
  1712. LogErrorEvent(ERR_WRITING_SETUP_PIPE, GetLastError(), 0);
  1713. } else {
  1714. lCount++;
  1715. }
  1716. }
  1717. HeapFree(ghPnPHeap, 0, entry);
  1718. LockNotifyList(&InstallList.Lock);
  1719. }
  1720. UnlockNotifyList(&InstallList.Lock);
  1721. } while (lCount > 0);
  1722. Clean1:
  1723. CloseHandle(hPipe);
  1724. hPipe = INVALID_HANDLE_VALUE;
  1725. CloseHandle(hPipeEvent);
  1726. hPipeEvent = NULL;
  1727. CloseHandle(hBatchEvent);
  1728. hBatchEvent = NULL;
  1729. if (hThread) {
  1730. CloseHandle(hThread);
  1731. hThread = NULL;
  1732. }
  1733. HeapFree(ghPnPHeap, 0, pDeviceList);
  1734. pDeviceList = NULL;
  1735. } // for
  1736. Clean0:
  1737. NOTHING;
  1738. } except(EXCEPTION_EXECUTE_HANDLER) {
  1739. KdPrintEx((DPFLTR_PNPMGR_ID,
  1740. DBGF_ERRORS | DBGF_INSTALL,
  1741. "UMPNPMGR: Exception in ThreadProc_GuiSetupDeviceInstall\n"));
  1742. ASSERT(0);
  1743. Status = CR_FAILURE;
  1744. //
  1745. // Reference the following variables so the compiler will respect
  1746. // statement ordering w.r.t. their assignment.
  1747. //
  1748. hPipe = hPipe;
  1749. hPipeEvent = hPipeEvent;
  1750. hBatchEvent = hBatchEvent;
  1751. hThread = hThread;
  1752. pDeviceList = pDeviceList;
  1753. }
  1754. if (hPipe != INVALID_HANDLE_VALUE) {
  1755. CloseHandle(hPipe);
  1756. }
  1757. if (hPipeEvent != NULL) {
  1758. CloseHandle(hPipeEvent);
  1759. }
  1760. if (hBatchEvent != NULL) {
  1761. CloseHandle(hBatchEvent);
  1762. }
  1763. if (hThread) {
  1764. CloseHandle(hThread);
  1765. }
  1766. if (pDeviceList != NULL) {
  1767. HeapFree(ghPnPHeap, 0, pDeviceList);
  1768. }
  1769. //
  1770. // will typically never return
  1771. //
  1772. return ThreadProc_DeviceInstall(&ulPostSetupSkipPhase1);
  1773. } // ThreadProc_GuiSetupDeviceInstall
  1774. DWORD
  1775. ThreadProc_FactoryPreinstallDeviceInstall(
  1776. LPDWORD lpThreadParam
  1777. )
  1778. /*++
  1779. Routine Description:
  1780. This routine is a thread procedure. This thread is only active during
  1781. GUI-mode setup when we are doing a factory preinstall.
  1782. This function simply creates and event, and then waits before kicking off
  1783. normal pnp device install
  1784. Arguments:
  1785. lpThreadParam - Not used.
  1786. Return Value:
  1787. Not used, currently returns result of ThreadProc_DeviceInstall - which will
  1788. normally not return.
  1789. --*/
  1790. {
  1791. HANDLE hEvent = NULL;
  1792. CONFIGRET Status = CR_SUCCESS;
  1793. UNREFERENCED_PARAMETER(lpThreadParam);
  1794. try {
  1795. hEvent = CreateEvent(NULL, TRUE, FALSE, PNP_CREATE_PIPE_EVENT);
  1796. if (!hEvent) {
  1797. if (GetLastError() == ERROR_ALREADY_EXISTS) {
  1798. hEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, PNP_CREATE_PIPE_EVENT);
  1799. if (!hEvent) {
  1800. Status = CR_FAILURE;
  1801. goto Clean0;
  1802. }
  1803. } else {
  1804. Status = CR_FAILURE;
  1805. goto Clean0;
  1806. }
  1807. }
  1808. if (WaitForSingleObject(hEvent, INFINITE) != WAIT_OBJECT_0) {
  1809. Status = CR_FAILURE;
  1810. goto Clean0; // event must have been abandoned
  1811. }
  1812. Clean0:
  1813. NOTHING;
  1814. } except(EXCEPTION_EXECUTE_HANDLER) {
  1815. KdPrintEx((DPFLTR_PNPMGR_ID,
  1816. DBGF_ERRORS | DBGF_INSTALL,
  1817. "UMPNPMGR: Exception in ThreadProc_FactoryPreinstallDeviceInstall\n"));
  1818. ASSERT(0);
  1819. Status = CR_FAILURE;
  1820. //
  1821. // Reference the following variable so the compiler will respect
  1822. // statement ordering w.r.t. its assignment.
  1823. //
  1824. hEvent = hEvent;
  1825. }
  1826. if (hEvent != NULL) {
  1827. CloseHandle(hEvent);
  1828. }
  1829. //
  1830. // will typically never return
  1831. //
  1832. return ThreadProc_DeviceInstall(NULL);
  1833. } // ThreadProc_FactoryPreinstallDeviceInstall
  1834. //-----------------------------------------------------------------------------
  1835. // Device enumeration thread - created on demand
  1836. //-----------------------------------------------------------------------------
  1837. DWORD
  1838. ThreadProc_ReenumerateDeviceTree(
  1839. LPVOID lpThreadParam
  1840. )
  1841. /*++
  1842. Routine Description:
  1843. This routine is a thread procedure. This thread is created dynamically to
  1844. perform a synchronous device re-enumeration.
  1845. This thread can be waited on and abandoned after a specified timeout, if
  1846. necessary.
  1847. Arguments:
  1848. lpThreadParam - Specifies a pointer to the device instance path that should
  1849. be re-enumerated.
  1850. Return Value:
  1851. Not used, currently returns 0.
  1852. --*/
  1853. {
  1854. PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA controlData;
  1855. //
  1856. // Reenumerate the tree from the root specified.
  1857. //
  1858. memset(&controlData, 0 , sizeof(PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA));
  1859. controlData.Flags = 0;
  1860. RtlInitUnicodeString(&controlData.DeviceInstance, (PCWSTR)lpThreadParam);
  1861. NtPlugPlayControl(PlugPlayControlEnumerateDevice,
  1862. &controlData,
  1863. sizeof(controlData));
  1864. return 0;
  1865. } // ThreadProc_ReenumerateDeviceTree
  1866. //-----------------------------------------------------------------------------
  1867. // Device installation thread
  1868. //-----------------------------------------------------------------------------
  1869. DWORD
  1870. ThreadProc_DeviceInstall(
  1871. LPDWORD lpParam
  1872. )
  1873. /*++
  1874. Routine Description:
  1875. This routine is a thread procedure.
  1876. It is invoked during a normal boot, or after the GUI-setup special case has finished
  1877. During Phase-1, all devices are checked
  1878. During Phase-2, all new devices are checked as they arrive.
  1879. Arguments:
  1880. lpParam - if given and non-zero (currently only when called from ThreadProc_GuiSetupDeviceInstall)
  1881. skips Phase-1, will never prompt for reboot
  1882. Return Value:
  1883. Not used, currently returns Status failure code, should typically not return
  1884. --*/
  1885. {
  1886. CONFIGRET Status = CR_SUCCESS;
  1887. LPWSTR pDeviceList = NULL, pszDevice = NULL;
  1888. ULONG ulSize = 0, ulProblem = 0, ulStatus, ulConfig;
  1889. DWORD InstallDevStatus, WaitResult;
  1890. PPNP_INSTALL_ENTRY InstallEntry = NULL;
  1891. PPNP_INSTALL_ENTRY current, TempInstallList, CurDupeNode, PrevDupeNode;
  1892. BOOL InstallListLocked = FALSE;
  1893. BOOL RebootRequired, needsInstall;
  1894. BOOL DeviceHasProblem = FALSE, SingleDeviceHasProblem;
  1895. BOOL bStillInGuiModeSetup = lpParam ? (BOOL)lpParam[0] : FALSE;
  1896. ULONG ulClientSessionId = INVALID_SESSION;
  1897. ULONG ulFlags = 0;
  1898. HANDLE hAutoStartEvent;
  1899. if (!bStillInGuiModeSetup) {
  1900. //
  1901. // If the OOBE is not running, wait until the service control manager
  1902. // has begun starting autostart services before we attempt to install
  1903. // any devices. When the OOBE is running, we don't wait for anything
  1904. // because the OOBE waits on us (via CMP_WaitNoPendingInstallEvents) to
  1905. // finish server-side installing any devices that we can before it lets
  1906. // the SCM autostart services and set this event.
  1907. //
  1908. if (!gbOobeInProgress) {
  1909. hAutoStartEvent = OpenEvent(SYNCHRONIZE,
  1910. FALSE,
  1911. SC_AUTOSTART_EVENT_NAME);
  1912. if (hAutoStartEvent) {
  1913. //
  1914. // Wait until the service controller allows other services to
  1915. // start before we try to install any devices in phases 1 and 2,
  1916. // below.
  1917. //
  1918. WaitResult = WaitForSingleObject(hAutoStartEvent, INFINITE);
  1919. ASSERT(WaitResult == WAIT_OBJECT_0);
  1920. CloseHandle(hAutoStartEvent);
  1921. } else {
  1922. //
  1923. // The service controller always creates this event, so it must
  1924. // exist by the time our service is started.
  1925. //
  1926. KdPrintEx((DPFLTR_PNPMGR_ID,
  1927. DBGF_INSTALL | DBGF_ERRORS,
  1928. "UMPNPMGR: Failed to open %ws event, error = %d\n",
  1929. SC_AUTOSTART_EVENT_NAME,
  1930. GetLastError()));
  1931. ASSERT(0);
  1932. }
  1933. }
  1934. try {
  1935. //
  1936. // Phase 1:
  1937. //
  1938. // Check the enum branch in the registry and attempt to install, one
  1939. // right after the other, any devices that need to be installed right
  1940. // now. Typically these devices showed up during boot.
  1941. //
  1942. // Retrieve the list of devices that currently need to be installed.
  1943. // Start out with a reasonably-sized buffer (16K characters) in hopes
  1944. // of avoiding 2 calls to get the device list.
  1945. //
  1946. // this phase is skipped during GUI-mode setup, and is handled by ThreadProc_GuiSetupDeviceInstall
  1947. //
  1948. ulSize = 16384;
  1949. for ( ; ; ) {
  1950. pDeviceList = HeapAlloc(ghPnPHeap, 0, ulSize * sizeof(WCHAR));
  1951. if (pDeviceList == NULL) {
  1952. Status = CR_OUT_OF_MEMORY;
  1953. goto Clean1;
  1954. }
  1955. Status = PNP_GetDeviceList(NULL, NULL, pDeviceList, &ulSize, 0);
  1956. if (Status == CR_SUCCESS) {
  1957. break;
  1958. } else if(Status == CR_BUFFER_SMALL) {
  1959. //
  1960. // Our initial buffer wasn't large enough. Free the current
  1961. // buffer.
  1962. //
  1963. HeapFree(ghPnPHeap, 0, pDeviceList);
  1964. pDeviceList = NULL;
  1965. //
  1966. // Now, go ahead and make the call to retrieve the actual size
  1967. // required.
  1968. //
  1969. Status = PNP_GetDeviceListSize(NULL, NULL, &ulSize, 0);
  1970. if (Status != CR_SUCCESS) {
  1971. goto Clean1;
  1972. }
  1973. } else {
  1974. //
  1975. // We failed for some reason other than buffer-too-small. Bail
  1976. // now.
  1977. //
  1978. goto Clean1;
  1979. }
  1980. }
  1981. //
  1982. // Make sure we have the device installer APIs at our disposal
  1983. // before starting server-side install.
  1984. //
  1985. InstallDevStatus = LoadDeviceInstaller();
  1986. ASSERT(InstallDevStatus == NO_ERROR);
  1987. if (InstallDevStatus != NO_ERROR) {
  1988. goto Clean1;
  1989. }
  1990. //
  1991. // Get the config flag for each device, and install any that need to be
  1992. // installed.
  1993. //
  1994. for (pszDevice = pDeviceList;
  1995. *pszDevice;
  1996. pszDevice += lstrlen(pszDevice) + 1) {
  1997. //
  1998. // Should the device be installed?
  1999. //
  2000. if (DevInstNeedsInstall(pszDevice, &needsInstall) == CR_SUCCESS) {
  2001. if (needsInstall) {
  2002. KdPrintEx((DPFLTR_PNPMGR_ID,
  2003. DBGF_INSTALL,
  2004. "UMPNPMGR: Installing device (%ws) server-side\n",
  2005. pszDevice));
  2006. RebootRequired = FALSE;
  2007. //
  2008. // Make sure we have the device installer APIs at our disposal
  2009. // before starting server-side install.
  2010. //
  2011. InstallDevStatus = LoadDeviceInstaller();
  2012. ASSERT(InstallDevStatus == NO_ERROR);
  2013. if (InstallDevStatus == NO_ERROR) {
  2014. if (IsFastUserSwitchingEnabled()) {
  2015. ulFlags = DEVICE_INSTALL_DISPLAY_ON_CONSOLE;
  2016. } else {
  2017. ulClientSessionId = MAIN_SESSION;
  2018. ulFlags = 0;
  2019. }
  2020. //
  2021. // Attempt server-side installation of this device.
  2022. //
  2023. InstallDevStatus = InstallDeviceServerSide(pszDevice,
  2024. &RebootRequired,
  2025. &SingleDeviceHasProblem,
  2026. &ulClientSessionId,
  2027. ulFlags);
  2028. }
  2029. if(InstallDevStatus == NO_ERROR) {
  2030. KdPrintEx((DPFLTR_PNPMGR_ID,
  2031. DBGF_INSTALL,
  2032. "UMPNPMGR: Installing device (%ws), Server-side installation succeeded!\n",
  2033. pszDevice));
  2034. if (SingleDeviceHasProblem) {
  2035. DeviceHasProblem = TRUE;
  2036. }
  2037. } else {
  2038. KdPrintEx((DPFLTR_PNPMGR_ID,
  2039. DBGF_INSTALL,
  2040. "UMPNPMGR: Installing device (%ws), Server-side installation failed (Status = 0x%08X)\n",
  2041. pszDevice,
  2042. InstallDevStatus));
  2043. }
  2044. if((InstallDevStatus != NO_ERROR) || RebootRequired) {
  2045. //
  2046. // Allocate and initialize a new device install entry
  2047. // block.
  2048. //
  2049. InstallEntry = HeapAlloc(ghPnPHeap, 0, sizeof(PNP_INSTALL_ENTRY));
  2050. if(!InstallEntry) {
  2051. Status = CR_OUT_OF_MEMORY;
  2052. goto Clean1;
  2053. }
  2054. InstallEntry->Next = NULL;
  2055. InstallEntry->Flags = PIE_SERVER_SIDE_INSTALL_ATTEMPTED;
  2056. if(InstallDevStatus == NO_ERROR) {
  2057. //
  2058. // We didn't get here because the install failed,
  2059. // so it must've been because the installation
  2060. // requires a reboot.
  2061. //
  2062. ASSERT(RebootRequired);
  2063. InstallEntry->Flags |= PIE_DEVICE_INSTALL_REQUIRED_REBOOT;
  2064. //
  2065. // Set the global server side device install
  2066. // reboot needed bool to TRUE.
  2067. //
  2068. gServerSideDeviceInstallRebootNeeded = TRUE;
  2069. }
  2070. lstrcpy(InstallEntry->szDeviceId, pszDevice);
  2071. //
  2072. // Insert this entry in the device install list.
  2073. //
  2074. LockNotifyList(&InstallList.Lock);
  2075. InstallListLocked = TRUE;
  2076. current = (PPNP_INSTALL_ENTRY)InstallList.Next;
  2077. if(!current) {
  2078. InstallList.Next = InstallEntry;
  2079. } else {
  2080. while((PPNP_INSTALL_ENTRY)current->Next) {
  2081. current = (PPNP_INSTALL_ENTRY)current->Next;
  2082. }
  2083. current->Next = InstallEntry;
  2084. }
  2085. //
  2086. // Newly-allocated entry now added to the list--NULL
  2087. // out the pointer so we won't try to free it if we
  2088. // happen to encounter an exception later.
  2089. //
  2090. InstallEntry = NULL;
  2091. UnlockNotifyList(&InstallList.Lock);
  2092. InstallListLocked = FALSE;
  2093. SetEvent(InstallEvents[NEEDS_INSTALL_EVENT]);
  2094. }
  2095. }
  2096. } else {
  2097. KdPrintEx((DPFLTR_PNPMGR_ID,
  2098. DBGF_INSTALL,
  2099. "UMPNPMGR: Ignoring not present device (%ws)\n",
  2100. pszDevice));
  2101. }
  2102. }
  2103. Clean1:
  2104. //
  2105. // Up to this point, we have only attempted server-side installation
  2106. // of devices, so any device install clients we might have launched
  2107. // would have been for UI only. Since we are done installing
  2108. // devices for the time being, we should unload the device installer
  2109. // APIs, and get rid of any device install clients that currently
  2110. // exist.
  2111. //
  2112. UnloadDeviceInstaller();
  2113. } except(EXCEPTION_EXECUTE_HANDLER) {
  2114. KdPrintEx((DPFLTR_PNPMGR_ID,
  2115. DBGF_ERRORS | DBGF_INSTALL,
  2116. "UMPNPMGR: Exception in ThreadProc_DeviceInstall\n"));
  2117. ASSERT(0);
  2118. Status = CR_FAILURE;
  2119. //
  2120. // Reference the following variables so the compiler will respect
  2121. // statement ordering w.r.t. assignment.
  2122. //
  2123. pDeviceList = pDeviceList;
  2124. InstallListLocked = InstallListLocked;
  2125. InstallEntry = InstallEntry;
  2126. }
  2127. if(InstallEntry) {
  2128. HeapFree(ghPnPHeap, 0, InstallEntry);
  2129. InstallEntry = NULL;
  2130. }
  2131. if(InstallListLocked) {
  2132. UnlockNotifyList(&InstallList.Lock);
  2133. InstallListLocked = FALSE;
  2134. }
  2135. if(pDeviceList != NULL) {
  2136. HeapFree(ghPnPHeap, 0, pDeviceList);
  2137. }
  2138. }
  2139. //
  2140. // NOTE: We should remove this line if we ever hook up the 'finished
  2141. // installing hardware' balloon so that it comes up if we install devices
  2142. // before a user logs on.
  2143. //
  2144. DeviceHasProblem = FALSE;
  2145. //
  2146. // Maintain a temporary list of PNP_INSTALL_ENTRY nodes that we needed to
  2147. // initiate client-side installation for, but couldn't these nodes get
  2148. // re-added to the master InstallList once all entries have been processed.
  2149. // Also, keep a pointer to the end of the list for efficient appending of
  2150. // nodes to the queue.
  2151. //
  2152. current = TempInstallList = NULL;
  2153. try {
  2154. //
  2155. // Phase 2: Hang around and be prepared to install any devices
  2156. // that come on line for the first time while we're
  2157. // running.
  2158. // we may come into Phase 2 (skipping Phase 1) in GUI-Setup
  2159. //
  2160. for ( ; ; ) {
  2161. //
  2162. // Before starting an indefinite wait, test the event state and set
  2163. // the ghNoPendingInstalls event accordingly. This event is just a
  2164. // backdoor way for device manager (and others) to see if we're
  2165. // still installing things.
  2166. //
  2167. if(WaitForSingleObject(InstallEvents[NEEDS_INSTALL_EVENT], 0) != WAIT_OBJECT_0) {
  2168. //
  2169. // There's nothing waiting to be installed--set the event.
  2170. //
  2171. SetEvent(ghNoPendingInstalls);
  2172. }
  2173. //
  2174. // Wait until the device event thread tells us we need to
  2175. // dynamically install a new device (or until somebody logs on).
  2176. //
  2177. WaitForMultipleObjects(NUM_INSTALL_EVENTS,
  2178. InstallEvents,
  2179. FALSE, // wake up on either event
  2180. INFINITE // I can wait all day
  2181. );
  2182. //
  2183. // After I empty the list, this thread can sleep until another new
  2184. // device needs to be installed...
  2185. //
  2186. ResetEvent(InstallEvents[NEEDS_INSTALL_EVENT]);
  2187. //
  2188. // ...or until a user logs in (note that we only want to awake once
  2189. // per log-in.
  2190. //
  2191. ResetEvent(InstallEvents[LOGGED_ON_EVENT]);
  2192. //
  2193. // We now have something to do, so reset the event that lets folks
  2194. // like DevMgr know when we're idle.
  2195. //
  2196. ResetEvent(ghNoPendingInstalls);
  2197. #if DBG
  2198. RtlValidateHeap(ghPnPHeap,0,NULL);
  2199. #endif
  2200. //
  2201. // Process each device that needs to be installed.
  2202. //
  2203. while (InstallList.Next != NULL) {
  2204. //
  2205. // Retrieve and remove the first (oldest) entry in the
  2206. // install device list.
  2207. //
  2208. LockNotifyList(&InstallList.Lock);
  2209. InstallListLocked = TRUE;
  2210. InstallEntry = (PPNP_INSTALL_ENTRY)InstallList.Next;
  2211. InstallList.Next = InstallEntry->Next;
  2212. //
  2213. // Now, scan the rest of the list looking for additional nodes
  2214. // related to this same device. If we find any, OR their flags
  2215. // into our 'master' node, and remove the duplicated nodes from
  2216. // the list. We can get duplicates due to the fact that both
  2217. // the event thread and this thread can be placing items in the
  2218. // list. We don't want to be attempting (failing) server-side
  2219. // installations multiple times.
  2220. //
  2221. CurDupeNode = (PPNP_INSTALL_ENTRY)InstallList.Next;
  2222. PrevDupeNode = NULL;
  2223. while(CurDupeNode) {
  2224. if(!lstrcmpi(InstallEntry->szDeviceId, CurDupeNode->szDeviceId)) {
  2225. //
  2226. // We have a duplicate! OR the flags into those of
  2227. // the install entry we retrieved from the head of
  2228. // the list.
  2229. //
  2230. InstallEntry->Flags |= CurDupeNode->Flags;
  2231. //
  2232. // Now remove this duplicate node from the list.
  2233. //
  2234. if(PrevDupeNode) {
  2235. PrevDupeNode->Next = CurDupeNode->Next;
  2236. } else {
  2237. InstallList.Next = CurDupeNode->Next;
  2238. }
  2239. HeapFree(ghPnPHeap, 0, CurDupeNode);
  2240. if(PrevDupeNode) {
  2241. CurDupeNode = (PPNP_INSTALL_ENTRY)PrevDupeNode->Next;
  2242. } else {
  2243. CurDupeNode = (PPNP_INSTALL_ENTRY)InstallList.Next;
  2244. }
  2245. } else {
  2246. PrevDupeNode = CurDupeNode;
  2247. CurDupeNode = (PPNP_INSTALL_ENTRY)CurDupeNode->Next;
  2248. }
  2249. }
  2250. UnlockNotifyList(&InstallList.Lock);
  2251. InstallListLocked = FALSE;
  2252. if(InstallEntry->Flags & PIE_DEVICE_INSTALL_REQUIRED_REBOOT) {
  2253. //
  2254. // We've already performed a (successful) server-side
  2255. // installation on this device. Remember the fact that a
  2256. // reboot is needed, so we'll prompt after processing this
  2257. // batch of new hardware.
  2258. //
  2259. // This will be our last chance to prompt for reboot on this
  2260. // node, because the next thing we're going to do is free
  2261. // this install entry!
  2262. //
  2263. gServerSideDeviceInstallRebootNeeded = TRUE;
  2264. } else {
  2265. //
  2266. // Verify that device really needs to be installed
  2267. //
  2268. ulConfig = GetDeviceConfigFlags(InstallEntry->szDeviceId, NULL);
  2269. Status = GetDeviceStatus(InstallEntry->szDeviceId, &ulStatus, &ulProblem);
  2270. if (Status == CR_SUCCESS) {
  2271. //
  2272. // Note that we must explicitly check below for the
  2273. // presence of the CONFIGFLAG_REINSTALL config flag. We
  2274. // can't simply rely on the CM_PROB_REINSTALL problem
  2275. // being set, because we may have encountered a device
  2276. // during our phase 1 processing whose installation was
  2277. // deferred because it provided finish-install wizard
  2278. // pages. Since we only discover that this is the case
  2279. // _after_ successful completion of DIF_INSTALLDEVICE,
  2280. // it's too late to set the problem (kernel-mode PnP
  2281. // manager only allows us to set a problem of needs-
  2282. // reboot for a running devnode).
  2283. //
  2284. if((ulConfig & CONFIGFLAG_FINISH_INSTALL) ||
  2285. (ulConfig & CONFIGFLAG_REINSTALL) ||
  2286. ((ulStatus & DN_HAS_PROBLEM) &&
  2287. ((ulProblem == CM_PROB_REINSTALL) || (ulProblem == CM_PROB_NOT_CONFIGURED)))) {
  2288. if(!(InstallEntry->Flags & PIE_SERVER_SIDE_INSTALL_ATTEMPTED)) {
  2289. //
  2290. // We haven't tried to install this device
  2291. // server-side yet, so try that now.
  2292. //
  2293. KdPrintEx((DPFLTR_PNPMGR_ID,
  2294. DBGF_INSTALL,
  2295. "UMPNPMGR: Installing device (%ws) server-side\n\t Status = 0x%08X\n\t Problem = %d\n\t ConfigFlags = 0x%08X\n",
  2296. InstallEntry->szDeviceId,
  2297. ulStatus,
  2298. ulProblem,
  2299. ulConfig));
  2300. //
  2301. // Make sure we have the device installer APIs at our disposal
  2302. // before starting server-side install.
  2303. //
  2304. InstallDevStatus = LoadDeviceInstaller();
  2305. ASSERT(InstallDevStatus == NO_ERROR);
  2306. if (InstallDevStatus == NO_ERROR) {
  2307. if (IsFastUserSwitchingEnabled()) {
  2308. ulFlags = DEVICE_INSTALL_DISPLAY_ON_CONSOLE;
  2309. } else {
  2310. ulClientSessionId = MAIN_SESSION;
  2311. ulFlags = 0;
  2312. }
  2313. InstallDevStatus = InstallDeviceServerSide(
  2314. InstallEntry->szDeviceId,
  2315. &gServerSideDeviceInstallRebootNeeded,
  2316. &SingleDeviceHasProblem,
  2317. &ulClientSessionId,
  2318. ulFlags);
  2319. }
  2320. if(InstallDevStatus == NO_ERROR) {
  2321. KdPrintEx((DPFLTR_PNPMGR_ID,
  2322. DBGF_INSTALL,
  2323. "UMPNPMGR: Installing device (%ws), Server-side installation succeeded!\n",
  2324. InstallEntry->szDeviceId));
  2325. if (SingleDeviceHasProblem) {
  2326. DeviceHasProblem = TRUE;
  2327. }
  2328. } else {
  2329. KdPrintEx((DPFLTR_PNPMGR_ID,
  2330. DBGF_INSTALL,
  2331. "UMPNPMGR: Installing device (%ws), Server-side installation failed (Status = 0x%08X)\n",
  2332. InstallEntry->szDeviceId,
  2333. InstallDevStatus));
  2334. InstallEntry->Flags |= PIE_SERVER_SIDE_INSTALL_ATTEMPTED;
  2335. }
  2336. } else {
  2337. //
  2338. // Set some bogus error so we'll drop into the
  2339. // non-server install codepath below.
  2340. //
  2341. InstallDevStatus = ERROR_INVALID_DATA;
  2342. }
  2343. if(InstallDevStatus != NO_ERROR) {
  2344. KdPrintEx((DPFLTR_PNPMGR_ID,
  2345. DBGF_INSTALL,
  2346. "UMPNPMGR: Installing device (%ws) client-side\n\t Status = 0x%08X\n\t Problem = %d\n\t ConfigFlags = 0x%08X\n",
  2347. InstallEntry->szDeviceId,
  2348. ulStatus,
  2349. ulProblem,
  2350. ulConfig));
  2351. if (IsFastUserSwitchingEnabled()) {
  2352. ulFlags = DEVICE_INSTALL_DISPLAY_ON_CONSOLE;
  2353. } else {
  2354. ulClientSessionId = MAIN_SESSION;
  2355. ulFlags = 0;
  2356. }
  2357. if (!InstallDevice(InstallEntry->szDeviceId,
  2358. &ulClientSessionId,
  2359. ulFlags)) {
  2360. //
  2361. // We weren't able to kick off a device
  2362. // install on the client side (probably
  2363. // because no one was logged in). Stick
  2364. // this PNP_INSTALL_ENTRY node into a
  2365. // temporary list that we'll re-add into
  2366. // the InstallList queue once we've emptied
  2367. // it.
  2368. //
  2369. if(current) {
  2370. current->Next = InstallEntry;
  2371. current = InstallEntry;
  2372. } else {
  2373. ASSERT(!TempInstallList);
  2374. TempInstallList = current = InstallEntry;
  2375. }
  2376. //
  2377. // NULL out the InstallEntry pointer so we
  2378. // don't try to free it later.
  2379. //
  2380. InstallEntry = NULL;
  2381. }
  2382. }
  2383. } else if((ulStatus & DN_HAS_PROBLEM) &&
  2384. (ulProblem == CM_PROB_NEED_RESTART)) {
  2385. //
  2386. // This device was percolated up from kernel-mode
  2387. // for the sole purpose of requesting a reboot.
  2388. // This presently only happens when we encounter a
  2389. // duplicate devnode, and we then "unique-ify" it
  2390. // to keep from bugchecking. We don't want the
  2391. // unique-ified devnode to actually be installed/
  2392. // used. Instead, we just want to give the user a
  2393. // prompt to reboot, and after they reboot, all
  2394. // should be well. The scenario where this has
  2395. // arisen is in relation to a USB printer (with a
  2396. // serial number) that is moved from one port to
  2397. // another during a suspend. When we resume, we
  2398. // have both an arrival and a removal to process,
  2399. // and if we process the arrival first, we think
  2400. // we've found a dupe.
  2401. //
  2402. KdPrintEx((DPFLTR_PNPMGR_ID,
  2403. DBGF_INSTALL,
  2404. "UMPNPMGR: Duplicate device detected (%ws), need to prompt user to reboot!\n",
  2405. InstallEntry->szDeviceId));
  2406. //
  2407. // Stick this entry into our temporary list to deal
  2408. // with later...
  2409. //
  2410. if(current) {
  2411. current->Next = InstallEntry;
  2412. current = InstallEntry;
  2413. } else {
  2414. ASSERT(!TempInstallList);
  2415. TempInstallList = current = InstallEntry;
  2416. }
  2417. //
  2418. // If possible, we want to prompt for reboot right
  2419. // away (that is, after all install events are
  2420. // drained)...
  2421. //
  2422. gServerSideDeviceInstallRebootNeeded = TRUE;
  2423. //
  2424. // If no user is logged in yet, flag this install
  2425. // entry so we'll try to prompt for reboot the next
  2426. // time we're awakened (which hopefully will be due
  2427. // to a user logging in).
  2428. //
  2429. InstallEntry->Flags |= PIE_DEVICE_INSTALL_REQUIRED_REBOOT;
  2430. //
  2431. // NULL out the InstallEntry pointer so we
  2432. // don't try to free it later.
  2433. //
  2434. InstallEntry = NULL;
  2435. }
  2436. }
  2437. }
  2438. if(InstallEntry) {
  2439. HeapFree(ghPnPHeap, 0, InstallEntry);
  2440. InstallEntry = NULL;
  2441. }
  2442. }
  2443. //
  2444. // We've processed all device install events known to us at this
  2445. // time. If we encountered any device whose installation requires
  2446. // a reboot, prompt the logged-in user (if any) to reboot now.
  2447. //
  2448. if (gServerSideDeviceInstallRebootNeeded) {
  2449. ulFlags = DEVICE_INSTALL_FINISHED_REBOOT;
  2450. if (IsFastUserSwitchingEnabled()) {
  2451. ulFlags |= DEVICE_INSTALL_DISPLAY_ON_CONSOLE;
  2452. } else {
  2453. ulClientSessionId = MAIN_SESSION;
  2454. }
  2455. if (bStillInGuiModeSetup) {
  2456. //
  2457. // if we're still in GUI setup, we're going to suppress
  2458. // any reboot prompts
  2459. //
  2460. gServerSideDeviceInstallRebootNeeded = FALSE;
  2461. } else if (PromptUser(&ulClientSessionId,
  2462. ulFlags)) {
  2463. //
  2464. // We successfully delivered the reboot prompt, so if the
  2465. // user chose to ignore it, we don't want to prompt again
  2466. // for reboot the next time new hardware shows up (unless
  2467. // that hardware also requires a reboot).
  2468. //
  2469. gServerSideDeviceInstallRebootNeeded = FALSE;
  2470. }
  2471. }
  2472. if(TempInstallList) {
  2473. //
  2474. // Add our temporary list of PNP_INSTALL_ENTRY nodes back into
  2475. // the InstallList queue. We _do not_ set the event that says
  2476. // there's more to do, so these nodes will be seen again only
  2477. // if (a) somebody logs in or (b) more new hardware shows up.
  2478. //
  2479. // Note: we cannot assume that the list is empty, because there
  2480. // may have been an insertion after the last time we checked it
  2481. // above. We want to add our stuff to the beginning of the
  2482. // InstallList queue, since the items we just finished
  2483. // processing appeared before any new entries that might be
  2484. // there now.
  2485. //
  2486. LockNotifyList(&InstallList.Lock);
  2487. InstallListLocked = TRUE;
  2488. ASSERT(current);
  2489. current->Next = InstallList.Next;
  2490. InstallList.Next = TempInstallList;
  2491. //
  2492. // Null out our temporary install list pointers to indicate
  2493. // that the list is now empty.
  2494. //
  2495. current = TempInstallList = NULL;
  2496. UnlockNotifyList(&InstallList.Lock);
  2497. InstallListLocked = FALSE;
  2498. }
  2499. //
  2500. // Before starting an indefinite wait, test the InstallEvents to see
  2501. // if there any new devices to install, or there are still devices
  2502. // to be installed in the InstallList. If neither of these is the
  2503. // case after waiting a few seconds, we'll notify the user that
  2504. // we're done installing devices for now, unload setupapi, and close
  2505. // all device install clients.
  2506. //
  2507. WaitResult = WaitForMultipleObjects(NUM_INSTALL_EVENTS,
  2508. InstallEvents,
  2509. FALSE,
  2510. DEVICE_INSTALL_COMPLETE_WAIT_TIME);
  2511. if ((WaitResult != (WAIT_OBJECT_0 + NEEDS_INSTALL_EVENT)) &&
  2512. (InstallList.Next == NULL)) {
  2513. KdPrintEx((DPFLTR_PNPMGR_ID,
  2514. DBGF_INSTALL,
  2515. "UMPNPMGR: ThreadProc_DeviceInstall: no more devices to install.\n"));
  2516. //
  2517. // There's nothing waiting to be installed--set the event.
  2518. //
  2519. SetEvent(ghNoPendingInstalls);
  2520. //
  2521. // Notify the user (if any), that we think we're done installing
  2522. // devices for now. Note that if we never used a device install
  2523. // client at any time in this pass (all server-side or silent
  2524. // installs), then we won't prompt about this.
  2525. //
  2526. ulFlags = DEVICE_INSTALL_BATCH_COMPLETE;
  2527. if (DeviceHasProblem) {
  2528. ulFlags |= DEVICE_INSTALL_PROBLEM;
  2529. }
  2530. if (IsFastUserSwitchingEnabled()) {
  2531. ulFlags |= DEVICE_INSTALL_DISPLAY_ON_CONSOLE;
  2532. } else {
  2533. ulClientSessionId = MAIN_SESSION;
  2534. }
  2535. PromptUser(&ulClientSessionId,
  2536. ulFlags);
  2537. //
  2538. // Clear the DeviceHasProblem boolean since we just notified the
  2539. // user.
  2540. //
  2541. DeviceHasProblem = FALSE;
  2542. //
  2543. // We notified the user, now wait around for 10 more seconds
  2544. // from the time of prompting before closing the client to make
  2545. // sure that some new device doesn't arrive, in which case we
  2546. // would just immediately load the installer again.
  2547. //
  2548. WaitResult = WaitForMultipleObjects(NUM_INSTALL_EVENTS,
  2549. InstallEvents,
  2550. FALSE,
  2551. DEVICE_INSTALL_COMPLETE_DISPLAY_TIME);
  2552. if ((WaitResult != (WAIT_OBJECT_0 + NEEDS_INSTALL_EVENT)) &&
  2553. (InstallList.Next == NULL)) {
  2554. //
  2555. // Unload the device installer, and get rid of any device
  2556. // install clients that currently exist on any sessions.
  2557. // Note that closing the device install client will make the
  2558. // above prompt go away.
  2559. //
  2560. UnloadDeviceInstaller();
  2561. }
  2562. }
  2563. }
  2564. } except(EXCEPTION_EXECUTE_HANDLER) {
  2565. KdPrintEx((DPFLTR_PNPMGR_ID,
  2566. DBGF_ERRORS | DBGF_INSTALL,
  2567. "UMPNPMGR: Exception in ThreadProc_DeviceInstall\n"));
  2568. ASSERT(0);
  2569. Status = CR_FAILURE;
  2570. //
  2571. // Reference the following variables so the compiler will respect
  2572. // statement ordering w.r.t. their assignment.
  2573. //
  2574. InstallListLocked = InstallListLocked;
  2575. InstallEntry = InstallEntry;
  2576. TempInstallList = TempInstallList;
  2577. }
  2578. if(InstallListLocked) {
  2579. UnlockNotifyList(&InstallList.Lock);
  2580. }
  2581. if(InstallEntry) {
  2582. HeapFree(ghPnPHeap, 0, InstallEntry);
  2583. }
  2584. while(TempInstallList) {
  2585. current = (PPNP_INSTALL_ENTRY)(TempInstallList->Next);
  2586. HeapFree(ghPnPHeap, 0, TempInstallList);
  2587. TempInstallList = current;
  2588. }
  2589. //
  2590. // meaningless return value, since this thread should never exit.
  2591. //
  2592. return (DWORD)Status;
  2593. } // ThreadProc_DeviceInstall
  2594. BOOL
  2595. InstallDevice(
  2596. IN LPWSTR pszDeviceId,
  2597. IN OUT PULONG SessionId,
  2598. IN ULONG Flags
  2599. )
  2600. /*++
  2601. Routine Description:
  2602. This routine initiates a device installation with the device install client
  2603. (newdev.dll) on the current active console session, creating one if
  2604. necessary. This routine waits for the client to signal completion, the
  2605. process to signal that it has terminated, or this service to signal that we
  2606. have disconnected ourselves from the client.
  2607. Arguments:
  2608. pszDeviceId - device instance ID of the devnode to be installed.
  2609. SessionId - Supplies the address of a variable containing the SessionId on
  2610. which the device install client is to be displayed. If successful, the
  2611. SessionId will contain the session on which the device install client
  2612. process was launched. Otherwise, will contain an invalid SessionId,
  2613. INVALID_SESSION (0xFFFFFFFF).
  2614. Flags - Specifies flags describing the behavior of the device install client.
  2615. The following flags are currently defined:
  2616. DEVICE_INSTALL_DISPLAY_ON_CONSOLE - if specified, the value in the
  2617. SessionId variable will be ignored, and the device installclient will
  2618. always be displayed on the current active console session. The
  2619. SessionId of the current active console session will be returned in
  2620. the SessionId.
  2621. Return Value:
  2622. Returns TRUE is the device installation was completed by the device install
  2623. client, FALSE otherwise.
  2624. --*/
  2625. {
  2626. BOOL b;
  2627. HANDLE hFinishEvents[3] = { NULL, NULL, NULL };
  2628. DWORD dwWait;
  2629. PINSTALL_CLIENT_ENTRY pDeviceInstallClient = NULL;
  2630. //
  2631. // Assume failure
  2632. //
  2633. b = FALSE;
  2634. //
  2635. // Validate parameters
  2636. //
  2637. ASSERT(SessionId);
  2638. if (SessionId == NULL) {
  2639. return FALSE;
  2640. }
  2641. try {
  2642. //
  2643. // Calling DoDeviceInstallClient will create the newdev.dll process and open a named
  2644. // pipe if it isn't already been done earlier. It will then send the Device Id to
  2645. // newdev.dll over the pipe.
  2646. //
  2647. if (DoDeviceInstallClient(pszDeviceId,
  2648. SessionId,
  2649. Flags,
  2650. &pDeviceInstallClient)) {
  2651. ASSERT(pDeviceInstallClient);
  2652. ASSERT(pDeviceInstallClient->ulSessionId == *SessionId);
  2653. //
  2654. // Keep track of the device id last sent to this client before we
  2655. // disconnected from it. This will avoid duplicate popups if we
  2656. // reconnect to this session again, and attempt to client-side
  2657. // install the same device.
  2658. //
  2659. lstrcpy(pDeviceInstallClient->LastDeviceId, pszDeviceId);
  2660. //
  2661. // Wait for the device install to be signaled from newdev.dll
  2662. // to let us know that it has completed the installation.
  2663. //
  2664. // Wait on the client's process as well, to catch the case
  2665. // where the process crashes (or goes away) without signaling the
  2666. // device install event.
  2667. //
  2668. // Also wait on the disconnect event in case we have explicitly
  2669. // disconnected from the client while switching sessions.
  2670. //
  2671. hFinishEvents[0] = pDeviceInstallClient->hProcess;
  2672. hFinishEvents[1] = pDeviceInstallClient->hEvent;
  2673. hFinishEvents[2] = pDeviceInstallClient->hDisconnectEvent;
  2674. dwWait = WaitForMultipleObjects(3, hFinishEvents, FALSE, INFINITE);
  2675. if (dwWait == WAIT_OBJECT_0) {
  2676. //
  2677. // If the return is WAIT_OBJECT_0 then the newdev.dll
  2678. // process has gone away.
  2679. //
  2680. KdPrintEx((DPFLTR_PNPMGR_ID,
  2681. DBGF_INSTALL,
  2682. "UMPNPMGR: InstallDevice: process signalled, closing device install client!\n"));
  2683. } else if (dwWait == (WAIT_OBJECT_0 + 1)) {
  2684. //
  2685. // If the return is WAIT_OBJECT_0 + 1 then the device
  2686. // installer successfully received the request.
  2687. //
  2688. KdPrintEx((DPFLTR_PNPMGR_ID,
  2689. DBGF_INSTALL,
  2690. "UMPNPMGR: InstallDevice: device install succeeded\n"));
  2691. b = TRUE;
  2692. //
  2693. // This device install client is no longer processing any
  2694. // devices, so clear the device id.
  2695. //
  2696. *pDeviceInstallClient->LastDeviceId = L'\0';
  2697. } else if (dwWait == (WAIT_OBJECT_0 + 2)) {
  2698. //
  2699. // If the return is WAIT_OBJECT_0 + 2 then we were explicitly
  2700. // disconnected from the device install client. Consider the
  2701. // device install unsuccessful so that this device remains in
  2702. // the install list.
  2703. //
  2704. KdPrintEx((DPFLTR_PNPMGR_ID,
  2705. DBGF_INSTALL,
  2706. "UMPNPMGR: InstallDevice: device install client disconnected\n"));
  2707. } else {
  2708. //
  2709. // The wait was satisfied for some reason other than the
  2710. // specified objects. This should never happen, but just in
  2711. // case, we'll close the client.
  2712. //
  2713. KdPrintEx((DPFLTR_PNPMGR_ID,
  2714. DBGF_INSTALL | DBGF_ERRORS,
  2715. "UMPNPMGR: InstallDevice: wait completed unexpectedly!\n"));
  2716. }
  2717. //
  2718. // Remove the reference placed on the client while it was in use.
  2719. //
  2720. LockNotifyList(&InstallClientList.Lock);
  2721. DereferenceDeviceInstallClient(pDeviceInstallClient);
  2722. if ((dwWait != (WAIT_OBJECT_0 + 1)) &&
  2723. (dwWait != (WAIT_OBJECT_0 + 2))) {
  2724. //
  2725. // Unless the client signalled successful receipt of the
  2726. // request, or the client's session was disconnected from the
  2727. // console, the attempt to use this client was unsuccessful.
  2728. // Remove the initial reference so all associated handles will
  2729. // be closed and the entry will be freed when it is no longer in
  2730. // use.
  2731. //
  2732. //
  2733. // Note that if we were unsuccessful because of a
  2734. // logoff, we would have already dereferenced the
  2735. // client then, in which case the above dereference
  2736. // was the final one, and pDeviceInstallClient would
  2737. // be invalid. Instead, attempt to re-locate the
  2738. // client by the session id.
  2739. //
  2740. pDeviceInstallClient = LocateDeviceInstallClient(*SessionId);
  2741. if (pDeviceInstallClient) {
  2742. ASSERT(pDeviceInstallClient->RefCount == 1);
  2743. DereferenceDeviceInstallClient(pDeviceInstallClient);
  2744. }
  2745. }
  2746. UnlockNotifyList(&InstallClientList.Lock);
  2747. }
  2748. } except (EXCEPTION_EXECUTE_HANDLER) {
  2749. KdPrintEx((DPFLTR_PNPMGR_ID,
  2750. DBGF_ERRORS | DBGF_INSTALL,
  2751. "UMPNPMGR: Exception in InstallDevice!\n"));
  2752. ASSERT(0);
  2753. b = FALSE;
  2754. }
  2755. return b;
  2756. } // InstallDevice
  2757. //-----------------------------------------------------------------------------
  2758. // Device event server-side rpc routines
  2759. //-----------------------------------------------------------------------------
  2760. CONFIGRET
  2761. PNP_RegisterNotification(
  2762. IN handle_t hBinding,
  2763. IN ULONG_PTR hRecipient,
  2764. IN LPWSTR ServiceName,
  2765. IN LPBYTE NotificationFilter,
  2766. IN ULONG ulSize,
  2767. IN DWORD Flags,
  2768. OUT PNP_NOTIFICATION_CONTEXT *Context,
  2769. IN ULONG ProcessId,
  2770. IN ULONG64 *ClientContext
  2771. )
  2772. /*++
  2773. Routine Description:
  2774. This routine is the rpc server-side of the CMP_RegisterNotification routine.
  2775. It performs the remaining parameter validation and actually registers the
  2776. notification request appropriately.
  2777. Arguments:
  2778. hBinding - RPC binding handle.
  2779. hRecipient - The Flags value specifies what type of handle this is,
  2780. currently it's either a window handle or a service handle.
  2781. NotificationFilter - Specifies a pointer to one of the DEV_BROADCAST_XXX
  2782. structures.
  2783. ulSize - Specifies the size of the NotificationFilter structure.
  2784. Flags - Specifies additional paramters used to describe the client or
  2785. the supplied parameters. The Flags parameter is subdivided
  2786. into multiple fields that are interpreted separately, as
  2787. described below.
  2788. ** The Flags parameter contains a field that describes the type
  2789. of the hRecipient handle passed in. This field should be
  2790. interpreted as an enum, and can be extracted from the Flags
  2791. parameter using the following mask:
  2792. DEVICE_NOTIFY_HANDLE_MASK
  2793. Currently one of the following values must be specified by
  2794. this field:
  2795. DEVICE_NOTIFY_WINDOW_HANDLE - hRecipient is a window handle
  2796. (HWND) for a window whose WNDPROC will be registered to
  2797. receive WM_DEVICECHANGE window messages for the filtered
  2798. events specified by the supplied NotificationFilter.
  2799. DEVICE_NOTIFY_SERVICE_HANDLE - hRecipient is a service status
  2800. handle (SERVICE_STATUS_HANDLE) for a service whose
  2801. HandlerEx routine will be registered to receive
  2802. SERVICE_CONTROL_DEVICEEVENT service controls for the
  2803. filtered events specified by the supplied
  2804. NotificationFilter.
  2805. NOTE: in reality - hRecipient is just the name of the
  2806. service, as resolved by the cfgmgr32 client. the SCM will
  2807. actually resolve this name for us to the true
  2808. SERVICE_STATUS_HANDLE for this service.
  2809. DEVICE_NOTIFY_COMPLETION_HANDLE - not currently implemented.
  2810. ** The Flags parameter contains a field that described additional
  2811. properties for the notification. This field should be
  2812. interpreted as a bitmask, and can be extracted from the Flags
  2813. parameter using the following mask:
  2814. DEVICE_NOTIFY_PROPERTY_MASK
  2815. Currently, the following flags are defined for this field:
  2816. DEVICE_NOTIFY_ALL_INTERFACE_CLASSES - This flag is only valid
  2817. when a DBT_DEVTYP_DEVICEINTERFACE type notification filter
  2818. is supplied. This flag specifies that the caller wishes
  2819. to receive notification of events for device interfaces of
  2820. all classes. If this flag is specified, the
  2821. dbcc_classguid member of the NotificationFilter structure
  2822. is ignored.
  2823. ** The Flags parameter also contains a "Reserved" field, that is
  2824. reserved for use by the cfgmgr32 client to this interface
  2825. only. This field should be interpreted as a bitmask, and can
  2826. be extracted from the Flags parameter using the following
  2827. mask:
  2828. DEVICE_NOTIFY_RESERVED_MASK
  2829. Currently, the following flags are defined for this field:
  2830. DEVICE_NOTIFY_WOW64_CLIENT - Specifies to a 64-bit server
  2831. caller is a 32-bit process running on WOW64. The 64-bit
  2832. server uses this information to construct 32-bit
  2833. compatible notification filters for the client.
  2834. Context - On return, this value returns the server notification context
  2835. to the client, that is supplied when unregistering this
  2836. notification request.
  2837. hProcess - Process Id of the calling application.
  2838. ClientContext - Specifies a pointer to a 64-bit value that contains the
  2839. client-context pointer. This value is the HDEVNOTIFY
  2840. notification handle returned to caller upon successful
  2841. registration. It is actually a pointer to the client memory
  2842. that will reference the returned server-notification context
  2843. pointer - but is never used as a pointer here on the
  2844. server-side. It is only used by the server to be specified as
  2845. the dbch_hdevnotify member of the DEV_BROADCAST_HANDLE
  2846. notification structure, supplied to the caller on
  2847. DBT_DEVTYP_HANDLE notification events.
  2848. NOTE: This value is truncated to 32-bits on 32-bit platforms,
  2849. but is always transmitted as a 64-bit value by the RPC
  2850. interface - for consistent marshalling of the data by RPC for
  2851. all 32-bit / 64-bit client / server combinations.
  2852. Return Value:
  2853. Return CR_SUCCESS if the function succeeds, otherwise it returns one
  2854. of the CR_* errors.
  2855. Notes:
  2856. This RPC server interface is used by local RPC clients only; it is never
  2857. called remotely.
  2858. --*/
  2859. {
  2860. CONFIGRET Status = CR_SUCCESS;
  2861. RPC_STATUS rpcStatus;
  2862. DEV_BROADCAST_HDR UNALIGNED *p;
  2863. PPNP_NOTIFY_ENTRY entry = NULL;
  2864. ULONG hashValue, ulSessionId;
  2865. HANDLE hProcess = NULL, localHandle = NULL;
  2866. PPNP_NOTIFY_LIST notifyList;
  2867. BOOLEAN bLocked = FALSE, bCritSecHeld = FALSE;
  2868. try {
  2869. //
  2870. // Validate parameters.
  2871. //
  2872. if (!ARGUMENT_PRESENT(Context)) {
  2873. return CR_INVALID_POINTER;
  2874. }
  2875. *Context = NULL;
  2876. if ((!ARGUMENT_PRESENT(NotificationFilter)) ||
  2877. (!ARGUMENT_PRESENT(ClientContext)) ||
  2878. (*ClientContext == 0)) {
  2879. return CR_INVALID_POINTER;
  2880. }
  2881. //
  2882. // DEVICE_NOTIFY_BITS is a private mask, defined specifically for
  2883. // validation by the client and server. It contains the bitmask for all
  2884. // handle types (DEVICE_NOTIFY_COMPLETION_HANDLE specifically excluded
  2885. // below), and all other flags that are currently defined - both public
  2886. // and reserved.
  2887. //
  2888. if (INVALID_FLAGS(Flags, DEVICE_NOTIFY_BITS)) {
  2889. Status = CR_INVALID_FLAG;
  2890. goto Clean0;
  2891. }
  2892. //
  2893. // Completion handles are not currently implemented.
  2894. // DEVICE_NOTIFY_COMPLETION_HANDLE defined privately in winuserp.h,
  2895. // reserved for future use (??).
  2896. //
  2897. if ((Flags & DEVICE_NOTIFY_HANDLE_MASK) ==
  2898. DEVICE_NOTIFY_COMPLETION_HANDLE) {
  2899. return CR_INVALID_FLAG;
  2900. }
  2901. //
  2902. // Make sure the Notification filter is a valid size.
  2903. //
  2904. if ((ulSize < sizeof(DEV_BROADCAST_HDR)) ||
  2905. (((PDEV_BROADCAST_HDR)NotificationFilter)->dbch_size < sizeof(DEV_BROADCAST_HDR))) {
  2906. return CR_BUFFER_SMALL;
  2907. }
  2908. ASSERT(ulSize == ((PDEV_BROADCAST_HDR)NotificationFilter)->dbch_size);
  2909. //
  2910. // Impersonate the client and retrieve the SessionId.
  2911. //
  2912. rpcStatus = RpcImpersonateClient(hBinding);
  2913. if (rpcStatus != RPC_S_OK) {
  2914. KdPrintEx((DPFLTR_PNPMGR_ID,
  2915. DBGF_ERRORS,
  2916. "UMPNPMGR: RpcImpersonateClient failed, error = %d\n",
  2917. rpcStatus));
  2918. return CR_FAILURE;
  2919. }
  2920. ulSessionId = GetClientLogonId();
  2921. rpcStatus = RpcRevertToSelf();
  2922. if (rpcStatus != RPC_S_OK) {
  2923. KdPrintEx((DPFLTR_PNPMGR_ID,
  2924. DBGF_ERRORS,
  2925. "UMPNPMGR: RpcRevertToSelf failed, error = %d\n",
  2926. rpcStatus));
  2927. ASSERT(0);
  2928. }
  2929. //
  2930. // Handle the different types of notification filters.
  2931. //
  2932. p = (PDEV_BROADCAST_HDR)NotificationFilter;
  2933. switch (p->dbch_devicetype) {
  2934. case DBT_DEVTYP_OEM:
  2935. case DBT_DEVTYP_VOLUME:
  2936. case DBT_DEVTYP_PORT:
  2937. case DBT_DEVTYP_NET:
  2938. //
  2939. // These structures are either obsolete, or used for broadcast-only
  2940. // notifications.
  2941. //
  2942. Status = CR_INVALID_DATA;
  2943. break;
  2944. case DBT_DEVTYP_HANDLE: {
  2945. //
  2946. // DEV_BROADCAST_HANDLE based notification.
  2947. //
  2948. DEV_BROADCAST_HANDLE UNALIGNED *filter = (PDEV_BROADCAST_HANDLE)NotificationFilter;
  2949. PLUGPLAY_CONTROL_TARGET_RELATION_DATA controlData;
  2950. NTSTATUS ntStatus;
  2951. #ifdef _WIN64
  2952. DEV_BROADCAST_HANDLE64 UNALIGNED filter64;
  2953. //
  2954. // Check if the client is running on WOW64.
  2955. //
  2956. if (Flags & DEVICE_NOTIFY_WOW64_CLIENT) {
  2957. //
  2958. // Convert the 32-bit DEV_BROADCAST_HANDLE notification filter
  2959. // to 64-bit.
  2960. //
  2961. DEV_BROADCAST_HANDLE32 UNALIGNED *filter32 = (PDEV_BROADCAST_HANDLE32)NotificationFilter;
  2962. //
  2963. // Validate the 32-bit input filter data
  2964. //
  2965. ASSERT(filter32->dbch_size >= sizeof(DEV_BROADCAST_HANDLE32));
  2966. if (filter32->dbch_size < sizeof(DEV_BROADCAST_HANDLE32) ||
  2967. ulSize < sizeof(DEV_BROADCAST_HANDLE32)) {
  2968. Status = CR_INVALID_DATA;
  2969. goto Clean0;
  2970. }
  2971. memset(&filter64, 0, sizeof(DEV_BROADCAST_HANDLE64));
  2972. filter64.dbch_size = sizeof(DEV_BROADCAST_HANDLE64);
  2973. filter64.dbch_devicetype = DBT_DEVTYP_HANDLE;
  2974. filter64.dbch_handle = (ULONG64)filter32->dbch_handle;
  2975. //
  2976. // use the converted 64-bit filter and size from now on, instead
  2977. // of the caller supplied 32-bit filter.
  2978. //
  2979. filter = (PDEV_BROADCAST_HANDLE)&filter64;
  2980. ulSize = sizeof(DEV_BROADCAST_HANDLE64);
  2981. }
  2982. #endif // _WIN64
  2983. //
  2984. // Validate the input filter data
  2985. //
  2986. if (filter->dbch_size < sizeof(DEV_BROADCAST_HANDLE) ||
  2987. ulSize < sizeof(DEV_BROADCAST_HANDLE)) {
  2988. Status = CR_INVALID_DATA;
  2989. goto Clean0;
  2990. }
  2991. //
  2992. // The DEVICE_NOTIFY_INCLUDE_ALL_INTERFACE_CLASSES flag is only
  2993. // valid for the DBT_DEVTYP_DEVICEINTERFACE notification filter
  2994. // type.
  2995. //
  2996. if ((Flags & DEVICE_NOTIFY_PROPERTY_MASK) &
  2997. DEVICE_NOTIFY_ALL_INTERFACE_CLASSES) {
  2998. Status = CR_INVALID_FLAG;
  2999. goto Clean0;
  3000. }
  3001. entry = HeapAlloc(ghPnPHeap, 0, sizeof(PNP_NOTIFY_ENTRY));
  3002. if (entry == NULL) {
  3003. Status = CR_OUT_OF_MEMORY;
  3004. goto Clean0;
  3005. }
  3006. //
  3007. // Find the device id that corresponds to this file handle.
  3008. // In this case, use a duplicated instance of the file handle
  3009. // for this process, not the caller's process.
  3010. //
  3011. rpcStatus = RpcImpersonateClient(hBinding);
  3012. if (rpcStatus != RPC_S_OK) {
  3013. KdPrintEx((DPFLTR_PNPMGR_ID,
  3014. DBGF_ERRORS,
  3015. "UMPNPMGR: RpcImpersonateClient failed, error = %d\n",
  3016. rpcStatus));
  3017. Status = CR_FAILURE;
  3018. HeapFree(ghPnPHeap, 0, entry);
  3019. goto Clean0;
  3020. }
  3021. hProcess = OpenProcess(PROCESS_DUP_HANDLE, FALSE, ProcessId);
  3022. if (hProcess == NULL) {
  3023. //
  3024. // Last error set by OpenProcess routine
  3025. //
  3026. Status = CR_FAILURE;
  3027. HeapFree(ghPnPHeap, 0, entry);
  3028. rpcStatus = RpcRevertToSelf();
  3029. if (rpcStatus != RPC_S_OK) {
  3030. KdPrintEx((DPFLTR_PNPMGR_ID,
  3031. DBGF_ERRORS,
  3032. "UMPNPMGR: RpcRevertToSelf failed, error = %d\n",
  3033. rpcStatus));
  3034. ASSERT(0);
  3035. }
  3036. goto Clean0;
  3037. }
  3038. if (!DuplicateHandle(hProcess,
  3039. (HANDLE)filter->dbch_handle,
  3040. GetCurrentProcess(),
  3041. &localHandle,
  3042. 0,
  3043. FALSE,
  3044. DUPLICATE_SAME_ACCESS)) {
  3045. //
  3046. // Last error set by DuplicateHandle routine
  3047. //
  3048. Status = CR_FAILURE;
  3049. HeapFree(ghPnPHeap, 0, entry);
  3050. CloseHandle(hProcess);
  3051. rpcStatus = RpcRevertToSelf();
  3052. if (rpcStatus != RPC_S_OK) {
  3053. KdPrintEx((DPFLTR_PNPMGR_ID,
  3054. DBGF_ERRORS,
  3055. "UMPNPMGR: RpcRevertToSelf failed, error = %d\n",
  3056. rpcStatus));
  3057. ASSERT(0);
  3058. }
  3059. goto Clean0;
  3060. }
  3061. rpcStatus = RpcRevertToSelf();
  3062. if (rpcStatus != RPC_S_OK) {
  3063. KdPrintEx((DPFLTR_PNPMGR_ID,
  3064. DBGF_ERRORS,
  3065. "UMPNPMGR: RpcRevertToSelf failed, error = %d\n",
  3066. rpcStatus));
  3067. ASSERT(0);
  3068. }
  3069. memset(&controlData, 0 , sizeof(PLUGPLAY_CONTROL_TARGET_RELATION_DATA));
  3070. controlData.UserFileHandle = localHandle;
  3071. controlData.DeviceInstance = entry->u.Target.DeviceId;
  3072. controlData.DeviceInstanceLen = sizeof(entry->u.Target.DeviceId);
  3073. ntStatus = NtPlugPlayControl(PlugPlayControlTargetDeviceRelation,
  3074. &controlData,
  3075. sizeof(controlData));
  3076. CloseHandle(localHandle);
  3077. CloseHandle(hProcess);
  3078. if (!NT_SUCCESS(ntStatus)) {
  3079. Status = MapNtStatusToCmError(ntStatus);
  3080. HeapFree(ghPnPHeap, 0, entry);
  3081. goto Clean0;
  3082. }
  3083. //
  3084. // Sanitize the device id
  3085. //
  3086. FixUpDeviceId(entry->u.Target.DeviceId);
  3087. //
  3088. // Copy the client name for the window or service, supplied by
  3089. // ServiceName.
  3090. //
  3091. if (ServiceName) {
  3092. KdPrintEx((DPFLTR_PNPMGR_ID,
  3093. DBGF_EVENT,
  3094. "UMPNPMGR: PNP_RegisterNotification: Registering [%ws]\n",
  3095. ServiceName));
  3096. entry->ClientName = HeapAlloc(ghPnPHeap,
  3097. 0,
  3098. (lstrlen(ServiceName)+1)*sizeof (WCHAR));
  3099. if (!entry->ClientName) {
  3100. KdPrintEx((DPFLTR_PNPMGR_ID,
  3101. DBGF_WARNINGS,
  3102. "UMPNPMGR: PNP_RegisterNotification failed to allocate memory for ClientName!\n"));
  3103. Status = CR_OUT_OF_MEMORY;
  3104. HeapFree (ghPnPHeap,0,entry);
  3105. goto Clean0;
  3106. }
  3107. lstrcpy (entry->ClientName, ServiceName);
  3108. } else {
  3109. entry->ClientName = NULL;
  3110. }
  3111. //
  3112. // Resolve the service status handle from the supplied service name.
  3113. //
  3114. if ((Flags & DEVICE_NOTIFY_HANDLE_MASK) == DEVICE_NOTIFY_SERVICE_HANDLE) {
  3115. hRecipient = (ULONG_PTR)NULL;
  3116. if (pSCMAuthenticate && ServiceName) {
  3117. SERVICE_STATUS_HANDLE serviceHandle;
  3118. if (pSCMAuthenticate(ServiceName, &serviceHandle) == NO_ERROR) {
  3119. hRecipient = (ULONG_PTR)serviceHandle;
  3120. }
  3121. }
  3122. if (!hRecipient) {
  3123. Status = CR_INVALID_DATA;
  3124. if (entry->ClientName) {
  3125. HeapFree(ghPnPHeap, 0, entry->ClientName);
  3126. }
  3127. HeapFree(ghPnPHeap, 0, entry);
  3128. *Context = NULL;
  3129. goto Clean0;
  3130. }
  3131. }
  3132. //
  3133. // Add this entry to the target list
  3134. //
  3135. entry->Signature = TARGET_ENTRY_SIGNATURE;
  3136. entry->Handle = (HANDLE)hRecipient;
  3137. entry->Flags = Flags;
  3138. entry->Unregistered = FALSE;
  3139. entry->Freed = 0;
  3140. entry->SessionId = ulSessionId;
  3141. //
  3142. // Save the caller's file handle (to pass back to caller
  3143. // during notification).
  3144. //
  3145. entry->u.Target.FileHandle = filter->dbch_handle;
  3146. EnterCriticalSection(&RegistrationCS);
  3147. bCritSecHeld = TRUE;
  3148. if (gNotificationInProg != 0) {
  3149. //
  3150. // If a notification is happening, add this entry to the list of
  3151. // deferred registrations.
  3152. //
  3153. PPNP_DEFERRED_LIST regNode;
  3154. regNode = (PPNP_DEFERRED_LIST)
  3155. HeapAlloc(ghPnPHeap,
  3156. 0,
  3157. sizeof (PNP_DEFERRED_LIST));
  3158. if (!regNode) {
  3159. KdPrintEx((DPFLTR_PNPMGR_ID,
  3160. DBGF_ERRORS | DBGF_WARNINGS,
  3161. "UMPNPMGR: Error allocating deferred list entry during registration!\n"));
  3162. Status = CR_OUT_OF_MEMORY;
  3163. if (entry->ClientName) {
  3164. HeapFree(ghPnPHeap, 0, entry->ClientName);
  3165. }
  3166. HeapFree(ghPnPHeap, 0, entry);
  3167. LeaveCriticalSection(&RegistrationCS);
  3168. bCritSecHeld = FALSE;
  3169. goto Clean0;
  3170. }
  3171. //
  3172. // Do not notify this entry until after the current
  3173. // notification is finished.
  3174. //
  3175. entry->Unregistered = TRUE;
  3176. regNode->hBinding = 0;
  3177. regNode->Entry = entry;
  3178. regNode->Next = RegisterList;
  3179. RegisterList = regNode;
  3180. }
  3181. hashValue = HashString(entry->u.Target.DeviceId, TARGET_HASH_BUCKETS);
  3182. notifyList = &TargetList[hashValue];
  3183. MarkEntryWithList(entry,hashValue);
  3184. LockNotifyList(&notifyList->Lock);
  3185. bLocked = TRUE;
  3186. AddNotifyEntry(&TargetList[hashValue], entry);
  3187. entry->ClientCtxPtr = (ULONG64)*ClientContext;
  3188. *Context = entry;
  3189. UnlockNotifyList(&notifyList->Lock);
  3190. bLocked = FALSE;
  3191. LeaveCriticalSection(&RegistrationCS);
  3192. bCritSecHeld = FALSE;
  3193. break;
  3194. }
  3195. case DBT_DEVTYP_DEVICEINTERFACE: {
  3196. DEV_BROADCAST_DEVICEINTERFACE UNALIGNED *filter = (PDEV_BROADCAST_DEVICEINTERFACE)NotificationFilter;
  3197. //
  3198. // Validate the input filter data
  3199. //
  3200. if (filter->dbcc_size < sizeof(DEV_BROADCAST_DEVICEINTERFACE) ||
  3201. ulSize < sizeof (DEV_BROADCAST_DEVICEINTERFACE) ) {
  3202. Status = CR_INVALID_DATA;
  3203. goto Clean0;
  3204. }
  3205. //
  3206. // We no longer support the private GUID_DEVNODE_CHANGE interface so return
  3207. // CR_INVALID_DATA if this GUID is passed in.
  3208. //
  3209. if (GuidEqual(&GUID_DEVNODE_CHANGE, &filter->dbcc_classguid)) {
  3210. KdPrintEx((DPFLTR_PNPMGR_ID,
  3211. DBGF_WARNINGS,
  3212. "UMPNPMGR: RegisterDeviceNotification using GUID_DEVNODE_CHANGE is not supported!\n"));
  3213. Status = CR_INVALID_DATA;
  3214. goto Clean0;
  3215. }
  3216. //
  3217. // The GUID_DEVINTERFACE_INCLUDE_ALL_INTERFACE_CLASSES interface is
  3218. // not supported directly. It is for internal use only. Return
  3219. // CR_INVALID_DATA if this GUID is passed in.
  3220. //
  3221. if (GuidEqual(&GUID_DEVINTERFACE_INCLUDE_ALL_INTERFACE_CLASSES,
  3222. &filter->dbcc_classguid)) {
  3223. KdPrintEx((DPFLTR_PNPMGR_ID,
  3224. DBGF_WARNINGS,
  3225. "UMPNPMGR: RegisterDeviceNotification using this class GUID is not supported!\n"));
  3226. Status = CR_INVALID_DATA;
  3227. goto Clean0;
  3228. }
  3229. entry = HeapAlloc(ghPnPHeap, 0, sizeof(PNP_NOTIFY_ENTRY));
  3230. if (entry == NULL) {
  3231. Status = CR_OUT_OF_MEMORY;
  3232. goto Clean0;
  3233. }
  3234. //
  3235. // Copy the client name for the window or service, supplied by
  3236. // ServiceName.
  3237. //
  3238. if (ServiceName) {
  3239. entry->ClientName = HeapAlloc(ghPnPHeap,
  3240. 0,
  3241. (lstrlen(ServiceName)+1)*sizeof (WCHAR));
  3242. if (!entry->ClientName) {
  3243. KdPrintEx((DPFLTR_PNPMGR_ID,
  3244. DBGF_WARNINGS,
  3245. "UMPNPMGR: PNP_RegisterNotification failed to allocate memory for ClientName!\n"));
  3246. Status = CR_OUT_OF_MEMORY;
  3247. HeapFree (ghPnPHeap,0,entry);
  3248. goto Clean0;
  3249. }
  3250. lstrcpy (entry->ClientName, ServiceName);
  3251. } else {
  3252. entry->ClientName = NULL;
  3253. }
  3254. //
  3255. // Resolve the service status handle from the supplied service name.
  3256. //
  3257. if ((Flags & DEVICE_NOTIFY_HANDLE_MASK) == DEVICE_NOTIFY_SERVICE_HANDLE) {
  3258. hRecipient = (ULONG_PTR)NULL;
  3259. if (pSCMAuthenticate && ServiceName) {
  3260. SERVICE_STATUS_HANDLE serviceHandle;
  3261. if (pSCMAuthenticate(ServiceName, &serviceHandle) == NO_ERROR) {
  3262. hRecipient = (ULONG_PTR)serviceHandle;
  3263. }
  3264. }
  3265. if (!hRecipient) {
  3266. Status = CR_INVALID_DATA;
  3267. if (entry->ClientName) {
  3268. HeapFree(ghPnPHeap, 0, entry->ClientName);
  3269. }
  3270. HeapFree(ghPnPHeap, 0, entry);
  3271. *Context = NULL;
  3272. goto Clean0;
  3273. }
  3274. }
  3275. //
  3276. // Add this entry to the class list
  3277. //
  3278. entry->Signature = CLASS_ENTRY_SIGNATURE;
  3279. entry->Handle = (HANDLE)hRecipient;
  3280. entry->Flags = Flags;
  3281. entry->Unregistered = FALSE;
  3282. entry->Freed = 0;
  3283. entry->SessionId = ulSessionId;
  3284. //
  3285. // If the caller is registering for all interface class events,
  3286. // ignore the caller supplied class GUID and use a private GUID.
  3287. // Otherwise, copy the caller supplied GUID to the notification list
  3288. // entry.
  3289. //
  3290. if ((Flags & DEVICE_NOTIFY_PROPERTY_MASK) &
  3291. DEVICE_NOTIFY_ALL_INTERFACE_CLASSES) {
  3292. memcpy(&entry->u.Class.ClassGuid,
  3293. &GUID_DEVINTERFACE_INCLUDE_ALL_INTERFACE_CLASSES,
  3294. sizeof(GUID));
  3295. } else {
  3296. memcpy(&entry->u.Class.ClassGuid,
  3297. &filter->dbcc_classguid,
  3298. sizeof(GUID));
  3299. }
  3300. EnterCriticalSection(&RegistrationCS);
  3301. bCritSecHeld = TRUE;
  3302. if (gNotificationInProg != 0) {
  3303. //
  3304. // If a notification is happening, add this entry to the list of
  3305. // deferred registrations.
  3306. //
  3307. PPNP_DEFERRED_LIST regNode;
  3308. regNode = (PPNP_DEFERRED_LIST)
  3309. HeapAlloc(ghPnPHeap,
  3310. 0,
  3311. sizeof (PNP_DEFERRED_LIST));
  3312. if (!regNode) {
  3313. KdPrintEx((DPFLTR_PNPMGR_ID,
  3314. DBGF_ERRORS | DBGF_WARNINGS,
  3315. "UMPNPMGR: Error allocating deferred list entry during registration!\n"));
  3316. Status = CR_OUT_OF_MEMORY;
  3317. if (entry->ClientName) {
  3318. HeapFree(ghPnPHeap, 0, entry->ClientName);
  3319. }
  3320. HeapFree(ghPnPHeap, 0, entry);
  3321. LeaveCriticalSection(&RegistrationCS);
  3322. bCritSecHeld = FALSE;
  3323. goto Clean0;
  3324. }
  3325. //
  3326. // Do not notify this entry until after the current
  3327. // notification is finished.
  3328. //
  3329. entry->Unregistered = TRUE;
  3330. regNode->hBinding = 0;
  3331. regNode->Entry = entry;
  3332. regNode->Next = RegisterList;
  3333. RegisterList = regNode;
  3334. }
  3335. hashValue = HashClassGuid(&entry->u.Class.ClassGuid);
  3336. notifyList = &ClassList[hashValue];
  3337. MarkEntryWithList(entry,hashValue);
  3338. LockNotifyList(&notifyList->Lock);
  3339. bLocked = TRUE;
  3340. AddNotifyEntry(&ClassList[hashValue],entry);
  3341. entry->ClientCtxPtr = (ULONG64)*ClientContext;
  3342. *Context = entry;
  3343. UnlockNotifyList(&notifyList->Lock);
  3344. bLocked = FALSE;
  3345. LeaveCriticalSection(&RegistrationCS);
  3346. bCritSecHeld = FALSE;
  3347. break;
  3348. }
  3349. default:
  3350. Status = CR_INVALID_DATA;
  3351. goto Clean0;
  3352. }
  3353. Clean0:
  3354. NOTHING;
  3355. } except(EXCEPTION_EXECUTE_HANDLER) {
  3356. KdPrintEx((DPFLTR_PNPMGR_ID,
  3357. DBGF_ERRORS | DBGF_EVENT,
  3358. "UMPNPMGR: Exception in PNP_RegisterNotification\n"));
  3359. ASSERT(0);
  3360. Status = CR_FAILURE;
  3361. if (bLocked) {
  3362. UnlockNotifyList(&notifyList->Lock);
  3363. }
  3364. if (bCritSecHeld) {
  3365. LeaveCriticalSection(&RegistrationCS);
  3366. }
  3367. }
  3368. return Status;
  3369. } // PNP_RegisterNotification
  3370. CONFIGRET
  3371. PNP_UnregisterNotification(
  3372. IN handle_t hBinding,
  3373. IN PPNP_NOTIFICATION_CONTEXT Context
  3374. )
  3375. /*++
  3376. Routine Description:
  3377. This routine is the rpc server-side of the CMP_UnregisterNotification routine.
  3378. It performs the remaining parameter validation and unregisters the
  3379. corresponding notification entry.
  3380. Arguments:
  3381. hBinding - RPC binding handle (not used).
  3382. Context - Contains the address of a HDEVNOTIFY notification handle that
  3383. was supplied when this notification request was registered.
  3384. Return Value:
  3385. Return CR_SUCCESS if the function succeeds, otherwise it returns one
  3386. of the CR_* errors.
  3387. Notes:
  3388. Note that the Context comes in as a PNP_NOTIFICATION_CONTEXT pointer
  3389. It is NOT one of those. The case is correct. This is to work around
  3390. RPC and user.
  3391. This RPC server interface is used by local RPC clients only; it is never
  3392. called remotely.
  3393. --*/
  3394. {
  3395. CONFIGRET Status = CR_SUCCESS;
  3396. ULONG hashValue = 0;
  3397. PPNP_DEFERRED_LIST unregNode;
  3398. PPNP_NOTIFY_LIST notifyList;
  3399. BOOLEAN bLocked = FALSE;
  3400. UNREFERENCED_PARAMETER(hBinding);
  3401. try {
  3402. //
  3403. // validate notification handle
  3404. //
  3405. PPNP_NOTIFY_ENTRY entry = (PPNP_NOTIFY_ENTRY)*Context;
  3406. EnterCriticalSection (&RegistrationCS);
  3407. if (entry == NULL) {
  3408. Status = CR_INVALID_DATA;
  3409. goto Clean0;
  3410. }
  3411. if (gNotificationInProg != 0) {
  3412. if (RegisterList) {
  3413. //
  3414. // Check to see if this entry is in the deferred RegisterList.
  3415. //
  3416. PPNP_DEFERRED_LIST currReg,prevReg;
  3417. currReg = RegisterList;
  3418. prevReg = NULL;
  3419. while (currReg) {
  3420. //
  3421. // Entries in the deferred RegisterList are to be skipped
  3422. // during notification.
  3423. //
  3424. ASSERT(currReg->Entry->Unregistered);
  3425. if (currReg->Entry == entry) {
  3426. //
  3427. // Remove this entry from the deferred RegisterList.
  3428. //
  3429. if (prevReg) {
  3430. prevReg->Next = currReg->Next;
  3431. } else {
  3432. RegisterList = currReg->Next;
  3433. }
  3434. HeapFree(ghPnPHeap, 0, currReg);
  3435. if (prevReg) {
  3436. currReg = prevReg->Next;
  3437. } else {
  3438. currReg = RegisterList;
  3439. }
  3440. } else {
  3441. prevReg = currReg;
  3442. currReg = currReg->Next;
  3443. }
  3444. }
  3445. }
  3446. switch (entry->Signature & LIST_ENTRY_SIGNATURE_MASK) {
  3447. case CLASS_ENTRY_SIGNATURE:
  3448. case TARGET_ENTRY_SIGNATURE: {
  3449. unregNode = (PPNP_DEFERRED_LIST)
  3450. HeapAlloc(ghPnPHeap,
  3451. 0,
  3452. sizeof (PNP_DEFERRED_LIST));
  3453. if (!unregNode) {
  3454. KdPrintEx((DPFLTR_PNPMGR_ID,
  3455. DBGF_ERRORS | DBGF_WARNINGS,
  3456. "UMPNPMGR: Error allocating deferred list entry during unregistration!\n"));
  3457. Status = CR_OUT_OF_MEMORY;
  3458. goto Clean0;
  3459. }
  3460. //
  3461. // This param is not used, if this changes, change this line too.
  3462. //
  3463. unregNode->hBinding= 0;
  3464. notifyList = GetNotifyListForEntry(entry);
  3465. if (notifyList) {
  3466. //
  3467. // The entry is part of a notification list, so make
  3468. // sure not to notify on it.
  3469. //
  3470. LockNotifyList(&notifyList->Lock);
  3471. bLocked = TRUE;
  3472. entry->Unregistered = TRUE;
  3473. UnlockNotifyList(&notifyList->Lock);
  3474. bLocked = FALSE;
  3475. }
  3476. unregNode->Entry = entry;
  3477. unregNode->Next = UnregisterList;
  3478. UnregisterList = unregNode;
  3479. *Context = NULL;
  3480. break;
  3481. }
  3482. default:
  3483. Status = CR_INVALID_DATA;
  3484. KdPrintEx((DPFLTR_PNPMGR_ID,
  3485. DBGF_WARNINGS | DBGF_ERRORS,
  3486. "UMPNPMGR: PNP_UnregisterNotification: invalid signature on entry at %x\n",
  3487. entry));
  3488. break;
  3489. }
  3490. goto Clean0;
  3491. }
  3492. //
  3493. // Free the notification entry from the appropriate list.
  3494. //
  3495. switch (entry->Signature & LIST_ENTRY_SIGNATURE_MASK) {
  3496. case CLASS_ENTRY_SIGNATURE:
  3497. hashValue = HashClassGuid(&entry->u.Class.ClassGuid);
  3498. notifyList = &ClassList[hashValue];
  3499. LockNotifyList(&notifyList->Lock);
  3500. bLocked = TRUE;
  3501. entry->Freed |= (PNP_UNREG_FREE|PNP_UNREG_CLASS);
  3502. DeleteNotifyEntry(entry,TRUE);
  3503. UnlockNotifyList(&notifyList->Lock);
  3504. bLocked = FALSE;
  3505. *Context = NULL;
  3506. break;
  3507. case TARGET_ENTRY_SIGNATURE:
  3508. hashValue = HashString(entry->u.Target.DeviceId, TARGET_HASH_BUCKETS);
  3509. notifyList = &TargetList[hashValue];
  3510. LockNotifyList(&notifyList->Lock);
  3511. bLocked = TRUE;
  3512. entry->Freed |= (PNP_UNREG_FREE|PNP_UNREG_TARGET);
  3513. DeleteNotifyEntry(entry,TRUE);
  3514. UnlockNotifyList(&notifyList->Lock);
  3515. bLocked = FALSE;
  3516. *Context = NULL;
  3517. break;
  3518. default:
  3519. Status = CR_INVALID_DATA;
  3520. KdPrintEx((DPFLTR_PNPMGR_ID,
  3521. DBGF_WARNINGS | DBGF_ERRORS,
  3522. "UMPNPMGR: PNP_UnregisterNotification: invalid signature on entry at %x\n",
  3523. entry));
  3524. }
  3525. Clean0:
  3526. LeaveCriticalSection(&RegistrationCS);
  3527. } except(EXCEPTION_EXECUTE_HANDLER) {
  3528. KdPrintEx((DPFLTR_PNPMGR_ID,
  3529. DBGF_ERRORS | DBGF_EVENT,
  3530. "UMPNPMGR: PNP_UnregisterNotification caused an exception!\n"));
  3531. ASSERT(0);
  3532. SetLastError(ERROR_EXCEPTION_IN_SERVICE);
  3533. Status = CR_FAILURE;
  3534. if (bLocked) {
  3535. UnlockNotifyList(&notifyList->Lock);
  3536. }
  3537. LeaveCriticalSection(&RegistrationCS);
  3538. }
  3539. return Status;
  3540. } // PNP_UnregisterNotification
  3541. //-----------------------------------------------------------------------------
  3542. // Dynamic Event Notification Support
  3543. //-----------------------------------------------------------------------------
  3544. DWORD
  3545. ThreadProc_DeviceEvent(
  3546. LPDWORD lpParam
  3547. )
  3548. /*++
  3549. Routine Description:
  3550. This routine is a thread procedure. This thread handles all device event
  3551. notification from kernel-mode.
  3552. Arguments:
  3553. lpParam - Not used.
  3554. Return Value:
  3555. Currently returns TRUE/FALSE.
  3556. --*/
  3557. {
  3558. DWORD status = TRUE, result = 0;
  3559. NTSTATUS ntStatus = STATUS_SUCCESS;
  3560. PPLUGPLAY_EVENT_BLOCK eventBlock = NULL;
  3561. ULONG totalSize, variableSize;
  3562. BOOL notDone = TRUE;
  3563. PVOID p = NULL;
  3564. PNP_VETO_TYPE vetoType;
  3565. WCHAR vetoName[MAX_VETO_NAME_LENGTH];
  3566. ULONG vetoNameLength;
  3567. PLUGPLAY_CONTROL_USER_RESPONSE_DATA userResponse;
  3568. PPNP_NOTIFY_LIST notifyList;
  3569. PPNP_DEFERRED_LIST reg,regFree,unreg,unregFree,rundown,rundownFree;
  3570. UNREFERENCED_PARAMETER(lpParam);
  3571. try {
  3572. //
  3573. // Initialize event buffer used to pass info back from kernel-mode in.
  3574. //
  3575. variableSize = 4096 - sizeof(PLUGPLAY_EVENT_BLOCK);
  3576. totalSize = sizeof(PLUGPLAY_EVENT_BLOCK) + variableSize;
  3577. eventBlock = (PPLUGPLAY_EVENT_BLOCK)HeapAlloc(ghPnPHeap, 0, totalSize);
  3578. if (eventBlock == NULL) {
  3579. LogErrorEvent(ERR_ALLOCATING_EVENT_BLOCK, ERROR_NOT_ENOUGH_MEMORY, 0);
  3580. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  3581. status = FALSE;
  3582. ASSERT(0);
  3583. goto Clean0;
  3584. }
  3585. //
  3586. // Retrieve device events synchronously (this is more efficient
  3587. // than using apcs).
  3588. //
  3589. while (notDone) {
  3590. ntStatus = NtGetPlugPlayEvent(NULL,
  3591. NULL, // Context
  3592. eventBlock,
  3593. totalSize);
  3594. if (ntStatus == STATUS_BUFFER_TOO_SMALL) {
  3595. //
  3596. // Kernel-mode side couldn't transfer the event because
  3597. // my buffer is too small, realloc and attempt to retrieve
  3598. // the event again.
  3599. //
  3600. variableSize += 1024;
  3601. totalSize = variableSize + sizeof(PLUGPLAY_EVENT_BLOCK);
  3602. p = HeapReAlloc(ghPnPHeap, 0, eventBlock, totalSize);
  3603. if (p == NULL) {
  3604. KdPrintEx((DPFLTR_PNPMGR_ID,
  3605. DBGF_ERRORS,
  3606. "UMPNPMGR: Couldn't reallocate event block to size %d\n",
  3607. totalSize));
  3608. LogErrorEvent(ERR_ALLOCATING_EVENT_BLOCK, ERROR_NOT_ENOUGH_MEMORY, 0);
  3609. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  3610. status = FALSE;
  3611. ASSERT(0);
  3612. goto Clean0;
  3613. }
  3614. eventBlock = (PPLUGPLAY_EVENT_BLOCK)p;
  3615. }
  3616. if (ntStatus == STATUS_SUCCESS) {
  3617. //
  3618. // An event was retrieved, process it.
  3619. //
  3620. gNotificationInProg = 1;
  3621. vetoType = PNP_VetoTypeUnknown;
  3622. vetoName[0] = L'\0';
  3623. vetoNameLength = MAX_VETO_NAME_LENGTH;
  3624. try {
  3625. //
  3626. // Process the device event.
  3627. //
  3628. result = ProcessDeviceEvent(eventBlock,
  3629. totalSize,
  3630. &vetoType,
  3631. vetoName,
  3632. &vetoNameLength);
  3633. } except(EXCEPTION_EXECUTE_HANDLER) {
  3634. KdPrintEx((DPFLTR_PNPMGR_ID,
  3635. DBGF_ERRORS | DBGF_EVENT,
  3636. "UMPNPMGR: Exception in ProcessDeviceEvent!\n"));
  3637. ASSERT(0);
  3638. //
  3639. // An exception while processing the event should not be
  3640. // considered a failure of the event itself.
  3641. //
  3642. result = TRUE;
  3643. vetoType = PNP_VetoTypeUnknown;
  3644. vetoName[0] = L'\0';
  3645. vetoNameLength = 0;
  3646. }
  3647. ASSERT(vetoNameLength < MAX_VETO_NAME_LENGTH &&
  3648. vetoName[vetoNameLength] == L'\0');
  3649. //
  3650. // Notify kernel-mode of the user-mode result.
  3651. //
  3652. userResponse.Response = result;
  3653. userResponse.VetoType = vetoType;
  3654. userResponse.VetoName = vetoName;
  3655. userResponse.VetoNameLength = vetoNameLength;
  3656. NtPlugPlayControl(PlugPlayControlUserResponse,
  3657. &userResponse,
  3658. sizeof(userResponse));
  3659. EnterCriticalSection (&RegistrationCS);
  3660. if (RegisterList != NULL) {
  3661. //
  3662. // Complete Registrations requested during notification.
  3663. //
  3664. reg = RegisterList;
  3665. RegisterList=NULL;
  3666. } else {
  3667. reg = NULL;
  3668. }
  3669. if (UnregisterList != NULL) {
  3670. //
  3671. // Complete Unregistrations requested during notification.
  3672. //
  3673. unreg = UnregisterList;
  3674. UnregisterList = NULL;
  3675. } else {
  3676. unreg = NULL;
  3677. }
  3678. if (RundownList != NULL) {
  3679. //
  3680. // Complete Unregistrations requested during notification.
  3681. //
  3682. rundown = RundownList;
  3683. RundownList = NULL;
  3684. } else {
  3685. rundown = NULL;
  3686. }
  3687. gNotificationInProg = 0;
  3688. while (reg) {
  3689. //
  3690. // This entry has already been added to the appropriate
  3691. // notification list. Allow this entry to receive
  3692. // notifications.
  3693. //
  3694. notifyList = GetNotifyListForEntry(reg->Entry);
  3695. ASSERT(notifyList);
  3696. if (notifyList) {
  3697. LockNotifyList(&notifyList->Lock);
  3698. }
  3699. reg->Entry->Unregistered = FALSE;
  3700. if (notifyList) {
  3701. UnlockNotifyList(&notifyList->Lock);
  3702. }
  3703. //
  3704. // Remove the entry from the deferred registration list.
  3705. //
  3706. regFree = reg;
  3707. reg = reg->Next;
  3708. HeapFree(ghPnPHeap, 0, regFree);
  3709. }
  3710. while (unreg) {
  3711. PNP_UnregisterNotification(unreg->hBinding,&unreg->Entry);
  3712. //
  3713. // Remove the entry from the deferred unregistration list.
  3714. //
  3715. unregFree = unreg;
  3716. unreg = unreg->Next;
  3717. HeapFree(ghPnPHeap, 0, unregFree);
  3718. }
  3719. while (rundown) {
  3720. PNP_NOTIFICATION_CONTEXT_rundown(rundown->Entry);
  3721. //
  3722. // Remove the entry from the deferred rundown list.
  3723. //
  3724. rundownFree = rundown;
  3725. rundown = rundown->Next;
  3726. HeapFree(ghPnPHeap, 0, rundownFree);
  3727. }
  3728. LeaveCriticalSection(&RegistrationCS);
  3729. }
  3730. if (ntStatus == STATUS_NOT_IMPLEMENTED) {
  3731. KdPrintEx((DPFLTR_PNPMGR_ID,
  3732. DBGF_ERRORS,
  3733. "UMPNPMGR: NtGetPlugPlayEvent returned STATUS_NOT_IMPLEMENTED\n"));
  3734. ASSERT(FALSE);
  3735. }
  3736. if (ntStatus == STATUS_USER_APC) {
  3737. KdPrintEx((DPFLTR_PNPMGR_ID,
  3738. DBGF_ERRORS,
  3739. "UMPNPMGR: ThreadProc_DeviceEvent exiting on STATUS_USER_APC\n"));
  3740. ASSERT(FALSE);
  3741. }
  3742. }
  3743. Clean0:
  3744. NOTHING;
  3745. } except(EXCEPTION_EXECUTE_HANDLER) {
  3746. KdPrintEx((DPFLTR_PNPMGR_ID,
  3747. DBGF_ERRORS | DBGF_EVENT,
  3748. "UMPNPMGR: Exception in ThreadProc_DeviceEvent!\n"));
  3749. ASSERT(0);
  3750. status = FALSE;
  3751. //
  3752. // Reference the following variable so the compiler will respect
  3753. // statement ordering w.r.t. its assignment.
  3754. //
  3755. eventBlock = eventBlock;
  3756. }
  3757. KdPrintEx((DPFLTR_PNPMGR_ID,
  3758. DBGF_ERRORS | DBGF_EVENT,
  3759. "UMPNPMGR: Exiting ThreadProc_DeviceEvent!!!!\n"));
  3760. TermNotification();
  3761. if (eventBlock != NULL) {
  3762. HeapFree(ghPnPHeap, 0, eventBlock);
  3763. }
  3764. return status;
  3765. } // ThreadProc_DeviceEvent
  3766. BOOL
  3767. InitNotification(
  3768. VOID
  3769. )
  3770. /*++
  3771. Routine Description:
  3772. This routine allocates and initializes notification lists, etc.
  3773. Arguments:
  3774. Not used.
  3775. Return Value:
  3776. Currently returns TRUE/FALSE.
  3777. --*/
  3778. {
  3779. ULONG i;
  3780. //
  3781. // Initialize the interface device (class) list
  3782. //
  3783. memset(ClassList, 0, sizeof(PNP_NOTIFY_LIST) * CLASS_GUID_HASH_BUCKETS);
  3784. for (i = 0; i < CLASS_GUID_HASH_BUCKETS; i++) {
  3785. ClassList[i].Next = NULL;
  3786. ClassList[i].Previous = NULL;
  3787. InitPrivateResource(&ClassList[i].Lock);
  3788. }
  3789. //
  3790. // Initialize the target device list
  3791. //
  3792. memset(TargetList, 0, sizeof(PNP_NOTIFY_LIST) * TARGET_HASH_BUCKETS);
  3793. for (i = 0; i < TARGET_HASH_BUCKETS; i++) {
  3794. TargetList[i].Next = NULL;
  3795. TargetList[i].Previous = NULL;
  3796. InitPrivateResource(&TargetList[i].Lock);
  3797. }
  3798. //
  3799. // Initialize the install list
  3800. //
  3801. InstallList.Next = NULL;
  3802. InitPrivateResource(&InstallList.Lock);
  3803. //
  3804. // Initialize the install client list
  3805. //
  3806. InstallClientList.Next = NULL;
  3807. InitPrivateResource(&InstallClientList.Lock);
  3808. //
  3809. // Initialize the lock for user token access
  3810. //
  3811. InitPrivateResource(&gTokenLock);
  3812. //
  3813. // Initialize the service handle list
  3814. //
  3815. memset(ServiceList, 0, sizeof(PNP_NOTIFY_LIST) * SERVICE_NUM_CONTROLS);
  3816. for (i = 0; i < SERVICE_NUM_CONTROLS; i++) {
  3817. ServiceList[i].Next = NULL;
  3818. ServiceList[i].Previous = NULL;
  3819. InitPrivateResource(&ServiceList[i].Lock);
  3820. }
  3821. //
  3822. // Initialize Registration/Unregistration CS.
  3823. //
  3824. try {
  3825. InitializeCriticalSection(&RegistrationCS);
  3826. } except(EXCEPTION_EXECUTE_HANDLER) {
  3827. return FALSE;
  3828. }
  3829. //
  3830. // Initialize deferred Registration/Unregistration lists.
  3831. //
  3832. RegisterList = NULL;
  3833. UnregisterList = NULL;
  3834. RundownList = NULL;
  3835. //
  3836. // Initialize gNotificationInProg flag.
  3837. //
  3838. gNotificationInProg = 0;
  3839. return TRUE;
  3840. } // InitNotification
  3841. VOID
  3842. TermNotification(
  3843. VOID
  3844. )
  3845. /*++
  3846. Routine Description:
  3847. This routine frees notification resources.
  3848. Arguments:
  3849. Not used.
  3850. Return Value:
  3851. No return.
  3852. --*/
  3853. {
  3854. ULONG i;
  3855. //
  3856. // Free the interface device (class) list locks
  3857. //
  3858. for (i = 0; i < CLASS_GUID_HASH_BUCKETS; i++) {
  3859. if (LockNotifyList(&ClassList[i].Lock)) {
  3860. DestroyPrivateResource(&ClassList[i].Lock);
  3861. }
  3862. }
  3863. //
  3864. // Free the target device list locks
  3865. //
  3866. for (i = 0; i < TARGET_HASH_BUCKETS; i++) {
  3867. if (LockNotifyList(&TargetList[i].Lock)) {
  3868. DestroyPrivateResource(&TargetList[i].Lock);
  3869. }
  3870. }
  3871. //
  3872. // Free the service notification list locks
  3873. //
  3874. for (i = 0; i < SERVICE_NUM_CONTROLS; i++) {
  3875. if (LockNotifyList(&ServiceList[i].Lock)) {
  3876. DestroyPrivateResource(&ServiceList[i].Lock);
  3877. }
  3878. }
  3879. //
  3880. // Free the install list lock
  3881. //
  3882. if (LockNotifyList(&InstallList.Lock)) {
  3883. DestroyPrivateResource(&InstallList.Lock);
  3884. }
  3885. //
  3886. // Free the lock for user token access
  3887. //
  3888. if (LockNotifyList(&gTokenLock)) {
  3889. DestroyPrivateResource(&gTokenLock);
  3890. }
  3891. //
  3892. // Free the install client list lock
  3893. //
  3894. if (LockNotifyList(&InstallClientList.Lock)) {
  3895. DestroyPrivateResource(&InstallClientList.Lock);
  3896. }
  3897. //
  3898. // Close the handle to winsta.dll
  3899. //
  3900. if (ghWinStaLib) {
  3901. fpWinStationSendWindowMessage = NULL;
  3902. fpWinStationBroadcastSystemMessage = NULL;
  3903. FreeLibrary(ghWinStaLib);
  3904. ghWinStaLib = NULL;
  3905. }
  3906. //
  3907. // Close the handle to wtsapi32.dll
  3908. //
  3909. if (ghWtsApi32Lib) {
  3910. fpWTSQuerySessionInformation = NULL;
  3911. fpWTSFreeMemory = NULL;
  3912. FreeLibrary(ghWtsApi32Lib);
  3913. ghWtsApi32Lib = NULL;
  3914. }
  3915. return;
  3916. } // TermNotification
  3917. ULONG
  3918. ProcessDeviceEvent(
  3919. IN PPLUGPLAY_EVENT_BLOCK EventBlock,
  3920. IN DWORD EventBufferSize,
  3921. OUT PPNP_VETO_TYPE VetoType,
  3922. OUT LPWSTR VetoName,
  3923. IN OUT PULONG VetoNameLength
  3924. )
  3925. /*++
  3926. Routine Description:
  3927. This routine processes device events recieved from the kernel-mode pnp
  3928. manager.
  3929. Arguments:
  3930. EventBlock - contains the event data.
  3931. EventBlockSize - specifies the size (in bytes) of EventBlock.
  3932. Return Value:
  3933. Returns FALSE if unsuccessful, or in the case of a vetoed query event.
  3934. Returns TRUE otherwise.
  3935. Notes:
  3936. This routine takes part in translating kernel mode PnP events into user mode
  3937. notifications. Currently, the notification code is dispersed and duplicated
  3938. throughout several routines. All notifications can be said to have the
  3939. following form though:
  3940. result = DeliverMessage(
  3941. MessageFlags, // [MSG_POST, MSG_SEND, MSG_QUERY] |
  3942. // [MSG_WALK_LIST_FORWARD, MSG_WALK_LIST_BACKWARDS]
  3943. Target, // A local window handle, hydra window handle (with
  3944. // session ID), service handle, or "broadcast".
  3945. // Better yet, it could take lists...
  3946. wParam, // DBT_* (or corresponding SERVICE_CONTROL_* message)
  3947. lParam, // Appropriate data (note: user has hardcoded knowledge
  3948. // about these via DBT_ type).
  3949. queueTimeout, // Exceeded if there exists messages in the queue but
  3950. // no message has been drained in the given time. Note
  3951. // that this means a message can fail immediately.
  3952. responseTimeout, // Exceeded if *this* message has not been processed in
  3953. // the elasped time.
  3954. VetoName, // For queries, the name of the vetoer.
  3955. VetoType // Type of vetoer component (window, service, ...)
  3956. );
  3957. DeviceEventWorker implements targeted sends and posts (normal exported Win32
  3958. API cannot be used as they won't reach other desktops). Currently User32
  3959. does not allow posts of DBT_* messages with lParam data, mainly because
  3960. a caller might send the message to itself, in which case no copy is made.
  3961. This in theory presents the caller with no opportunity to free that data
  3962. (note that this scenario would never occur with UmPnpMgr however, as we
  3963. have no WndProc). User implements this function with a fixed
  3964. responseTimeout of thirty seconds. This API can but should not be used for
  3965. broadcasts.
  3966. WinStationSendWindowMessage sends messages to windows within Hydra clients
  3967. on a machine. There is no corresponding WinStationPostWindowMessage. All
  3968. the code in this component passes a ResponseTimeout of five seconds. There
  3969. is no queueTimeout.
  3970. BroadcastSystemMessage implements broadcasts to all applications and desktops
  3971. in the non-console (ie non-Hydra) session. As with DeviceEventWorker,
  3972. User32 does not allow posts of DBT_* messages with lParam data (regardless
  3973. of whether you pass in BSF_IGNORECURRENTTASK). All code in this component
  3974. passes a ResponseTimeout of thirty seconds. QueueTimeout is optional,
  3975. fixed five seconds. ResponseTimeout cannot be specified, but the maximum
  3976. value would be five seconds per top level window. There is no information
  3977. returned on which window vetoed a query.
  3978. WinStationBroadcastSystemMessage broadcasts to all applications and desktops
  3979. on a given machine's Hydra sessions. No posts of any kind may be done
  3980. through this API. All code in this component passes a ResponseTimeout of
  3981. five seconds. QueueTimeout is an optional, fixed five seconds. There is no
  3982. information on which window vetoed a query.
  3983. ServiceControlCallback sends messages to registered services. There is no
  3984. posting or timeout facilities of any kind.
  3985. Actually, each queued registration entry should be queued with a callback.
  3986. We implement the callback, and there it hides the underlying complexities.
  3987. --*/
  3988. {
  3989. DWORD eventId, serviceControl, flags, status = TRUE;
  3990. LPWSTR p = NULL;
  3991. ULONG vetoNameSize;
  3992. DWORD dwLengthIDs;
  3993. ULONG ulLength, ulCustomDataLength, ulClientSessionId;
  3994. UNREFERENCED_PARAMETER(EventBufferSize);
  3995. ASSERT(EventBlock->TotalSize >= sizeof(PLUGPLAY_EVENT_BLOCK));
  3996. //
  3997. // Convert the event guid into a dbt style event id.
  3998. //
  3999. if (!EventIdFromEventGuid(&EventBlock->EventGuid,
  4000. &eventId,
  4001. &flags,
  4002. &serviceControl)) {
  4003. if (VetoNameLength != NULL) {
  4004. *VetoNameLength = 0;
  4005. }
  4006. return FALSE;
  4007. }
  4008. if (VetoNameLength != NULL &&
  4009. !((EventBlock->EventCategory == TargetDeviceChangeEvent) ||
  4010. (EventBlock->EventCategory == CustomDeviceEvent) ||
  4011. (EventBlock->EventCategory == HardwareProfileChangeEvent) ||
  4012. (EventBlock->EventCategory == PowerEvent) ) ){
  4013. *VetoNameLength = 0;
  4014. }
  4015. vetoNameSize = *VetoNameLength;
  4016. //
  4017. // Notify registered callers first (class changes will also send generic
  4018. // broadcast if the type is volume or port).
  4019. //
  4020. switch (EventBlock->EventCategory) {
  4021. case CustomDeviceEvent: {
  4022. //
  4023. // Convert the pnp event block into a dbt style structure.
  4024. //
  4025. PDEV_BROADCAST_HANDLE pNotify;
  4026. PLUGPLAY_CUSTOM_NOTIFICATION *pTarget;
  4027. if (*EventBlock->u.CustomNotification.DeviceIds == L'\0') {
  4028. //
  4029. // There are no device IDs, can't do notification in this case
  4030. // just return
  4031. //
  4032. if (VetoNameLength != NULL) {
  4033. *VetoNameLength = 0;
  4034. }
  4035. KdPrintEx((DPFLTR_PNPMGR_ID,
  4036. DBGF_ERRORS,
  4037. "UMPNPMGR: Ignoring CustomDeviceEvent with no Device IDs\n"));
  4038. return FALSE;
  4039. }
  4040. //
  4041. // Custom events should always be this GUID, and that guid should always
  4042. // be converted into the below eventId.
  4043. //
  4044. ASSERT(GuidEqual(&EventBlock->EventGuid, &GUID_PNP_CUSTOM_NOTIFICATION));
  4045. ASSERT(eventId == DBT_CUSTOMEVENT);
  4046. //
  4047. // Handle and Marshall the custom notification.
  4048. //
  4049. //
  4050. // The amount of space allocated for the EventBlock + IDs is always a
  4051. // multiple of sizeof(PVOID) in order to keep the notification structure
  4052. // aligned.
  4053. //
  4054. ulLength = sizeof(PLUGPLAY_EVENT_BLOCK) + (lstrlen(EventBlock->u.CustomNotification.DeviceIds) + 1) * sizeof(WCHAR);
  4055. ulLength += sizeof(PVOID) - 1;
  4056. ulLength &= ~(sizeof(PVOID) - 1);
  4057. //
  4058. // The notification structure follows the Event Block and IDs
  4059. //
  4060. pTarget = (PPLUGPLAY_CUSTOM_NOTIFICATION)((PUCHAR)EventBlock + ulLength);
  4061. ulCustomDataLength = pTarget->HeaderInfo.Size - FIELD_OFFSET(PLUGPLAY_CUSTOM_NOTIFICATION,CustomDataBuffer);
  4062. pNotify = HeapAlloc(ghPnPHeap, 0, sizeof(DEV_BROADCAST_HANDLE) + ulCustomDataLength);
  4063. if (pNotify == NULL) {
  4064. LogErrorEvent(ERR_ALLOCATING_NOTIFICATION_STRUCTURE, ERROR_NOT_ENOUGH_MEMORY, 0);
  4065. status = FALSE;
  4066. break;
  4067. }
  4068. memset(pNotify, 0, sizeof(DEV_BROADCAST_HANDLE) + ulCustomDataLength);
  4069. pNotify->dbch_size = sizeof(DEV_BROADCAST_HANDLE) + ulCustomDataLength;
  4070. pNotify->dbch_devicetype = DBT_DEVTYP_HANDLE;
  4071. pNotify->dbch_nameoffset = pTarget->NameBufferOffset;
  4072. pNotify->dbch_eventguid = pTarget->HeaderInfo.Event;
  4073. memcpy( pNotify->dbch_data, pTarget->CustomDataBuffer, ulCustomDataLength);
  4074. *VetoNameLength = vetoNameSize;
  4075. status = NotifyTargetDeviceChange( serviceControl,
  4076. eventId,
  4077. flags,
  4078. pNotify,
  4079. EventBlock->u.CustomNotification.DeviceIds,
  4080. VetoType,
  4081. VetoName,
  4082. VetoNameLength);
  4083. if (GuidEqual(&pNotify->dbch_eventguid, (LPGUID)&GUID_IO_VOLUME_NAME_CHANGE)) {
  4084. //
  4085. // Broadcast compatible volume removal and arrival notifications
  4086. // (if any) after the custom name change event has been sent to
  4087. // all recipients.
  4088. //
  4089. BroadcastVolumeNameChange();
  4090. }
  4091. HeapFree(ghPnPHeap, 0, pNotify);
  4092. break;
  4093. }
  4094. case TargetDeviceChangeEvent: {
  4095. //
  4096. // Convert the pnp event block into a dbt style structure.
  4097. //
  4098. PDEV_BROADCAST_HANDLE pNotify;
  4099. if (*EventBlock->u.TargetDevice.DeviceIds == L'\0') {
  4100. //
  4101. // There are no device IDs, can't do notification in this case
  4102. // just return
  4103. //
  4104. if (VetoNameLength != NULL) {
  4105. *VetoNameLength = 0;
  4106. }
  4107. KdPrintEx((DPFLTR_PNPMGR_ID,
  4108. DBGF_ERRORS,
  4109. "UMPNPMGR: Ignoring TargetDeviceChangeEvent with no Device IDs\n"));
  4110. return FALSE;
  4111. }
  4112. //
  4113. // If this is a surprise removal event then call HOTPLUG.DLL to display
  4114. // a warning to the user before sending this event to other apps.
  4115. //
  4116. if (GuidEqual(&EventBlock->EventGuid,&GUID_DEVICE_SAFE_REMOVAL)) {
  4117. SendHotplugNotification(
  4118. &EventBlock->EventGuid,
  4119. NULL,
  4120. EventBlock->u.TargetDevice.DeviceIds,
  4121. &ulClientSessionId,
  4122. HOTPLUG_DISPLAY_ON_CONSOLE
  4123. );
  4124. } else if (GuidEqual(&EventBlock->EventGuid, &GUID_DEVICE_KERNEL_INITIATED_EJECT)) {
  4125. *VetoNameLength = vetoNameSize;
  4126. status = CheckEjectPermissions(
  4127. EventBlock->u.TargetDevice.DeviceIds,
  4128. VetoType,
  4129. VetoName,
  4130. VetoNameLength
  4131. );
  4132. } else if (GuidEqual(&EventBlock->EventGuid,&GUID_DEVICE_SURPRISE_REMOVAL)) {
  4133. LogSurpriseRemovalEvent(EventBlock->u.TargetDevice.DeviceIds);
  4134. #if 0 // We don't display surpise-removal bubbles anymore...
  4135. SendHotplugNotification(
  4136. &EventBlock->EventGuid,
  4137. NULL,
  4138. EventBlock->u.TargetDevice.DeviceIds,
  4139. &ulClientSessionId,
  4140. HOTPLUG_DISPLAY_ON_CONSOLE
  4141. );
  4142. #endif
  4143. }
  4144. if (eventId == 0) {
  4145. //
  4146. // Internal event, no broadcasting should be done.
  4147. //
  4148. if (VetoNameLength != NULL) {
  4149. *VetoNameLength = 0;
  4150. }
  4151. break;
  4152. }
  4153. pNotify = HeapAlloc(ghPnPHeap, 0, sizeof(DEV_BROADCAST_HANDLE));
  4154. if (pNotify == NULL) {
  4155. LogErrorEvent(ERR_ALLOCATING_BROADCAST_HANDLE, ERROR_NOT_ENOUGH_MEMORY, 0);
  4156. status = FALSE;
  4157. if (VetoNameLength != NULL) {
  4158. *VetoNameLength = 0;
  4159. }
  4160. break;
  4161. }
  4162. memset(pNotify, 0, sizeof(DEV_BROADCAST_HANDLE));
  4163. pNotify->dbch_nameoffset = -1; // empty except for custom events
  4164. pNotify->dbch_size = sizeof(DEV_BROADCAST_HANDLE);
  4165. pNotify->dbch_devicetype = DBT_DEVTYP_HANDLE;
  4166. for (p = EventBlock->u.TargetDevice.DeviceIds;
  4167. *p;
  4168. p += lstrlen(p) + 1) {
  4169. *VetoNameLength = vetoNameSize;
  4170. KdPrintEx((DPFLTR_PNPMGR_ID,
  4171. DBGF_EVENT,
  4172. "UMPNPMGR: Processing TargetDeviceChangeEvent (0x%lx) for %ws\n",
  4173. eventId, p));
  4174. status = NotifyTargetDeviceChange(serviceControl,
  4175. eventId,
  4176. flags,
  4177. pNotify,
  4178. p,
  4179. VetoType,
  4180. VetoName,
  4181. VetoNameLength);
  4182. if (!status && (flags & BSF_QUERY)) {
  4183. LPWSTR pFail = p;
  4184. DWORD dwCancelEventId;
  4185. //
  4186. // Use the appropriate cancel device event id that corresponds to the
  4187. // original query device event id.
  4188. //
  4189. dwCancelEventId = MapQueryEventToCancelEvent(eventId);
  4190. for (p = EventBlock->u.TargetDevice.DeviceIds;
  4191. *p && p != pFail;
  4192. p += lstrlen(p) + 1) {
  4193. KdPrintEx((DPFLTR_PNPMGR_ID,
  4194. DBGF_EVENT,
  4195. "UMPNPMGR: Processing TargetDeviceChangeEvent (0x%lx) for %ws\n",
  4196. dwCancelEventId, p));
  4197. NotifyTargetDeviceChange( serviceControl,
  4198. dwCancelEventId,
  4199. BSF_NOHANG,
  4200. pNotify,
  4201. p,
  4202. NULL,
  4203. NULL,
  4204. NULL);
  4205. }
  4206. break;
  4207. }
  4208. }
  4209. HeapFree(ghPnPHeap, 0, pNotify);
  4210. break;
  4211. }
  4212. case DeviceClassChangeEvent: {
  4213. //
  4214. // Convert the pnp event block into a dbt style structure.
  4215. //
  4216. PDEV_BROADCAST_DEVICEINTERFACE pNotify;
  4217. ULONG ulSize = sizeof(DEV_BROADCAST_DEVICEINTERFACE) +
  4218. lstrlen(EventBlock->u.DeviceClass.SymbolicLinkName) * sizeof(WCHAR);
  4219. KdPrintEx((DPFLTR_PNPMGR_ID,
  4220. DBGF_EVENT,
  4221. "UMPNPMGR: Processing DeviceClassChangeEvent (0x%lx) for %ws\n",
  4222. eventId, EventBlock->u.DeviceClass.SymbolicLinkName));
  4223. pNotify = HeapAlloc(ghPnPHeap, 0, ulSize);
  4224. if (pNotify == NULL) {
  4225. LogErrorEvent(ERR_ALLOCATING_BROADCAST_INTERFACE, ERROR_NOT_ENOUGH_MEMORY, 0);
  4226. status = FALSE;
  4227. break;
  4228. }
  4229. pNotify->dbcc_size = ulSize;
  4230. pNotify->dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
  4231. pNotify->dbcc_reserved = 0;
  4232. memcpy(&pNotify->dbcc_classguid, &EventBlock->u.DeviceClass.ClassGuid, sizeof(GUID));
  4233. lstrcpy(pNotify->dbcc_name, EventBlock->u.DeviceClass.SymbolicLinkName);
  4234. //
  4235. // Note: the symbolic link name is passed in kernel-mode format (\??\),
  4236. // convert to user-mode format (\\?\) before sending notification.
  4237. // Note that the only difference is the second character.
  4238. //
  4239. pNotify->dbcc_name[1] = L'\\';
  4240. status = NotifyInterfaceClassChange(serviceControl,
  4241. eventId,
  4242. flags,
  4243. pNotify);
  4244. break;
  4245. }
  4246. case HardwareProfileChangeEvent:
  4247. KdPrintEx((DPFLTR_PNPMGR_ID,
  4248. DBGF_EVENT,
  4249. "UMPNPMGR: Processing HardwareProfileChangeEvent (0x%lx)\n",
  4250. eventId));
  4251. *VetoNameLength = vetoNameSize;
  4252. status = NotifyHardwareProfileChange(serviceControl,
  4253. eventId,
  4254. flags,
  4255. VetoType,
  4256. VetoName,
  4257. VetoNameLength);
  4258. break;
  4259. case PowerEvent:
  4260. *VetoNameLength = vetoNameSize;
  4261. //
  4262. // Since all power events arrive under a single event GUID,
  4263. // EventIdFromEventGuid cannot correctly determine the event id or query
  4264. // flags from it. Instead, we get the event id directly from the device
  4265. // event block, and add the BSF_QUERY flag here, if appropriate.
  4266. //
  4267. eventId = EventBlock->u.PowerNotification.NotificationCode;
  4268. KdPrintEx((DPFLTR_PNPMGR_ID,
  4269. DBGF_EVENT,
  4270. "UMPNPMGR: Processing PowerEvent (0x%lx)\n",
  4271. eventId));
  4272. if ((eventId == PBT_APMQUERYSUSPEND) ||
  4273. (eventId == PBT_APMQUERYSTANDBY)) {
  4274. flags |= BSF_QUERY;
  4275. } else {
  4276. flags &= ~BSF_QUERY;
  4277. }
  4278. status = NotifyPower(serviceControl,
  4279. eventId,
  4280. EventBlock->u.PowerNotification.NotificationData,
  4281. flags,
  4282. VetoType,
  4283. VetoName,
  4284. VetoNameLength);
  4285. break;
  4286. case VetoEvent:
  4287. KdPrintEx((DPFLTR_PNPMGR_ID,
  4288. DBGF_EVENT,
  4289. "UMPNPMGR: Processing VetoEvent\n"));
  4290. status = SendHotplugNotification(
  4291. &EventBlock->EventGuid,
  4292. &EventBlock->u.VetoNotification.VetoType,
  4293. EventBlock->u.VetoNotification.DeviceIdVetoNameBuffer,
  4294. &ulClientSessionId,
  4295. HOTPLUG_DISPLAY_ON_CONSOLE
  4296. );
  4297. break;
  4298. case DeviceInstallEvent: {
  4299. //
  4300. // Initiate installation; we can't wait around here for a user, but
  4301. // after installation is complete, kernel-mode will be notified
  4302. // that they can attempt to start the device now.
  4303. //
  4304. PPNP_INSTALL_ENTRY entry = NULL, current = NULL;
  4305. KdPrintEx((DPFLTR_PNPMGR_ID,
  4306. DBGF_EVENT,
  4307. "UMPNPMGR: Processing DeviceInstallEvent for %ws\n",
  4308. EventBlock->u.InstallDevice.DeviceId));
  4309. //
  4310. // Device install events should always be this GUID, and that guid
  4311. // should always be converted into the below eventId, serviceControl and
  4312. // flags.
  4313. //
  4314. ASSERT(GuidEqual(&EventBlock->EventGuid, &GUID_DEVICE_ENUMERATED));
  4315. ASSERT((eventId == DBT_DEVICEARRIVAL) && (serviceControl == 0) && (flags == 0));
  4316. //
  4317. // Allocate and initialize a new device install entry block.
  4318. //
  4319. entry = HeapAlloc(ghPnPHeap, 0, sizeof(PNP_INSTALL_ENTRY));
  4320. if (!entry) {
  4321. break;
  4322. }
  4323. entry->Next = NULL;
  4324. entry->Flags = 0;
  4325. lstrcpy(entry->szDeviceId, EventBlock->u.InstallDevice.DeviceId);
  4326. //
  4327. // Insert this entry in the device install list.
  4328. //
  4329. LockNotifyList(&InstallList.Lock);
  4330. current = (PPNP_INSTALL_ENTRY)InstallList.Next;
  4331. if (current == NULL) {
  4332. InstallList.Next = entry;
  4333. } else {
  4334. while ((PPNP_INSTALL_ENTRY)current->Next != NULL) {
  4335. current = (PPNP_INSTALL_ENTRY)current->Next;
  4336. }
  4337. current->Next = entry;
  4338. }
  4339. UnlockNotifyList(&InstallList.Lock);
  4340. SetEvent(InstallEvents[NEEDS_INSTALL_EVENT]);
  4341. //
  4342. // Generate a devnode changed message
  4343. //
  4344. NotifyTargetDeviceChange(serviceControl,
  4345. eventId,
  4346. flags,
  4347. NULL,
  4348. EventBlock->u.InstallDevice.DeviceId,
  4349. NULL,
  4350. NULL,
  4351. NULL);
  4352. break;
  4353. }
  4354. case BlockedDriverEvent: {
  4355. LPGUID BlockedDriverGuid;
  4356. PWSTR MultiSzGuidList = NULL;
  4357. //
  4358. // Display notification to the Console session that the system just
  4359. // blocked a driver from loading on the system.
  4360. //
  4361. ASSERT(GuidEqual(&EventBlock->EventGuid, &GUID_DRIVER_BLOCKED));
  4362. //
  4363. // We currently only ever have one blocked driver GUID per event,
  4364. // but SendHotplugNotification and hotplug.dll are setup to deal
  4365. // with multi-sz lists, so we'll just construct one for them. This
  4366. // keeps hotplug.dll extensible, should we decide in the future to
  4367. // have the kernel-mode pnpmgr "batch" blocked drivers per devnode.
  4368. //
  4369. BlockedDriverGuid = (LPGUID)&EventBlock->u.BlockedDriverNotification.BlockedDriverGuid;
  4370. KdPrintEx((DPFLTR_PNPMGR_ID,
  4371. DBGF_EVENT,
  4372. "UMPNPMGR: Processing BlockedDriverEvent for GUID = "
  4373. "{%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n",
  4374. BlockedDriverGuid->Data1,
  4375. BlockedDriverGuid->Data2,
  4376. BlockedDriverGuid->Data3,
  4377. BlockedDriverGuid->Data4[0],
  4378. BlockedDriverGuid->Data4[1],
  4379. BlockedDriverGuid->Data4[2],
  4380. BlockedDriverGuid->Data4[3],
  4381. BlockedDriverGuid->Data4[4],
  4382. BlockedDriverGuid->Data4[5],
  4383. BlockedDriverGuid->Data4[6],
  4384. BlockedDriverGuid->Data4[7]));
  4385. MultiSzGuidList = BuildBlockedDriverList(BlockedDriverGuid, 1);
  4386. if (MultiSzGuidList != NULL) {
  4387. SendHotplugNotification((LPGUID)&GUID_DRIVER_BLOCKED,
  4388. NULL,
  4389. MultiSzGuidList,
  4390. &ulClientSessionId,
  4391. HOTPLUG_DISPLAY_ON_CONSOLE);
  4392. HeapFree(ghPnPHeap, 0, MultiSzGuidList);
  4393. MultiSzGuidList = NULL;
  4394. }
  4395. break;
  4396. }
  4397. default:
  4398. break;
  4399. }
  4400. return status;
  4401. } // ProcessDeviceEvent
  4402. ULONG
  4403. NotifyInterfaceClassChange(
  4404. IN DWORD ServiceControl,
  4405. IN DWORD EventId,
  4406. IN DWORD Flags,
  4407. IN PDEV_BROADCAST_DEVICEINTERFACE ClassData
  4408. )
  4409. /*++
  4410. Routine Description:
  4411. This routine notifies registered services and windows of device interface
  4412. change events.
  4413. Arguments:
  4414. ServiceControl - Specifies class of service event (power, device, hwprofile
  4415. change).
  4416. EventId - Specifies the DBT style event id for the device event.
  4417. (see sdk\inc\dbt.h for defined device events)
  4418. Flags - Unused (Specifies BroadcastSystemMessage BSF_ flags.)
  4419. ClassData - Pointer to a PDEV_BROADCAST_DEVICEINTERFACE structure that
  4420. is already filled out with the pertinent data for this
  4421. event.
  4422. Return Value:
  4423. Returns TRUE.
  4424. --*/
  4425. {
  4426. NTSTATUS ntStatus = STATUS_SUCCESS;
  4427. DWORD result;
  4428. ULONG hashValue, pass, i;
  4429. PPNP_NOTIFY_ENTRY classEntry = NULL, nextEntry = NULL;
  4430. PPNP_NOTIFY_LIST notifyList;
  4431. LPGUID entryGuid[3];
  4432. UNREFERENCED_PARAMETER(Flags);
  4433. //
  4434. // Search the notification lists twice - once to notify entries registered
  4435. // on the device interface class for this device interface, and again to
  4436. // notify entries registered for all device interfaces.
  4437. //
  4438. entryGuid[0] = (LPGUID)&ClassData->dbcc_classguid;
  4439. entryGuid[1] = (LPGUID)&GUID_DEVINTERFACE_INCLUDE_ALL_INTERFACE_CLASSES;
  4440. entryGuid[2] = (LPGUID)NULL;
  4441. for (i = 0; entryGuid[i] != NULL; i++) {
  4442. //
  4443. // The list of registered callers is hashed for quicker access and
  4444. // comparison. Walk the list of registered callers and notify anyone
  4445. // that registered an interest in this device interface class guid.
  4446. //
  4447. hashValue = HashClassGuid(entryGuid[i]);
  4448. notifyList = &ClassList[hashValue];
  4449. LockNotifyList(&notifyList->Lock);
  4450. classEntry = GetFirstNotifyEntry(&ClassList[hashValue], 0);
  4451. pass = GetFirstPass(FALSE);
  4452. while (pass != PASS_COMPLETE) {
  4453. while (classEntry) {
  4454. nextEntry = GetNextNotifyEntry(classEntry, 0);
  4455. if (classEntry->Unregistered) {
  4456. classEntry = nextEntry;
  4457. continue;
  4458. }
  4459. if (GuidEqual(entryGuid[i], &classEntry->u.Class.ClassGuid)) {
  4460. if (GuidEqual(&classEntry->u.Class.ClassGuid,
  4461. &GUID_DEVINTERFACE_INCLUDE_ALL_INTERFACE_CLASSES)) {
  4462. //
  4463. // If the entry is marked with our special GUID, make
  4464. // sure it is because it was registered with the
  4465. // appropriate flag.
  4466. //
  4467. ASSERT((classEntry->Flags & DEVICE_NOTIFY_PROPERTY_MASK) &
  4468. DEVICE_NOTIFY_ALL_INTERFACE_CLASSES);
  4469. }
  4470. if ((pass == DEVICE_NOTIFY_WINDOW_HANDLE) &&
  4471. (GetPassFromEntry(classEntry) == DEVICE_NOTIFY_WINDOW_HANDLE)) {
  4472. //
  4473. // Note, class changes currently only support non-query type
  4474. // messages so special processing is not required (PostMessage
  4475. // only). Unfortunately, the PostMessage call currently fails
  4476. // if the high bit of the wParam value is set (which it is in
  4477. // this case), so we are forced to Send the message (rather than
  4478. // Post it). USER group implemented it this way because the original
  4479. // Win95 spec doesn't call for the recipient to free the message
  4480. // so we have to free it and we have no idea when it's safe
  4481. // with a PostMessage call.
  4482. //
  4483. UnlockNotifyList(&notifyList->Lock);
  4484. if (classEntry->SessionId == MAIN_SESSION) {
  4485. ntStatus = DeviceEventWorker(classEntry->Handle,
  4486. EventId,
  4487. (LPARAM)ClassData,
  4488. TRUE,
  4489. &result);
  4490. } else {
  4491. if (fpWinStationSendWindowMessage) {
  4492. try {
  4493. if (fpWinStationSendWindowMessage(SERVERNAME_CURRENT,
  4494. classEntry->SessionId,
  4495. DEFAULT_SEND_TIME_OUT,
  4496. HandleToUlong(classEntry->Handle),
  4497. WM_DEVICECHANGE,
  4498. (WPARAM)EventId,
  4499. (LPARAM)ClassData,
  4500. &result)) {
  4501. ntStatus = STATUS_SUCCESS;
  4502. } else {
  4503. ntStatus = STATUS_UNSUCCESSFUL;
  4504. }
  4505. } except (EXCEPTION_EXECUTE_HANDLER) {
  4506. KdPrintEx((DPFLTR_PNPMGR_ID,
  4507. DBGF_ERRORS,
  4508. "UMPNPMGR: Exception calling WinStationSendWindowMessage!\n"));
  4509. ASSERT(0);
  4510. ntStatus = STATUS_SUCCESS;
  4511. }
  4512. }
  4513. }
  4514. LockNotifyList(&notifyList->Lock);
  4515. if (!NT_SUCCESS(ntStatus)) {
  4516. if (ntStatus == STATUS_INVALID_HANDLE) {
  4517. //
  4518. // window handle no longer exists, cleanup this entry
  4519. //
  4520. KdPrintEx((DPFLTR_PNPMGR_ID,
  4521. DBGF_WARNINGS | DBGF_ERRORS,
  4522. "UMPNPMGR: Invalid window handle for '%ws' during DeviceClassChangeEvent, removing entry.\n",
  4523. classEntry->ClientName));
  4524. classEntry->Freed |= (PNP_UNREG_FREE|PNP_UNREG_DEFER|PNP_UNREG_WIN);
  4525. DeleteNotifyEntry(classEntry,FALSE);
  4526. } else if (ntStatus == STATUS_UNSUCCESSFUL) {
  4527. KdPrintEx((DPFLTR_PNPMGR_ID,
  4528. DBGF_WARNINGS | DBGF_ERRORS,
  4529. "UMPNPMGR: Window '%ws' timed out on DeviceClassChangeEvent\n",
  4530. classEntry->ClientName));
  4531. LogWarningEvent(WRN_INTERFACE_CHANGE_TIMED_OUT, 1, classEntry->ClientName);
  4532. }
  4533. }
  4534. } else if ((pass == DEVICE_NOTIFY_SERVICE_HANDLE) &&
  4535. (GetPassFromEntry(classEntry) == DEVICE_NOTIFY_SERVICE_HANDLE)) {
  4536. //
  4537. // Call the services handler routine...
  4538. //
  4539. if (pServiceControlCallback) {
  4540. UnlockNotifyList(&notifyList->Lock);
  4541. try {
  4542. (pServiceControlCallback)((SERVICE_STATUS_HANDLE)classEntry->Handle,
  4543. ServiceControl,
  4544. EventId,
  4545. (LPARAM)ClassData,
  4546. &result);
  4547. } except (EXCEPTION_EXECUTE_HANDLER) {
  4548. KdPrintEx((DPFLTR_PNPMGR_ID,
  4549. DBGF_ERRORS,
  4550. "UMPNPMGR: Exception calling Service Control Manager!\n"));
  4551. ASSERT(0);
  4552. }
  4553. LockNotifyList(&notifyList->Lock);
  4554. }
  4555. } else if ((pass == DEVICE_NOTIFY_COMPLETION_HANDLE) &&
  4556. (GetPassFromEntry(classEntry) == DEVICE_NOTIFY_COMPLETION_HANDLE)) {
  4557. //
  4558. // Complete the notification handle.
  4559. // NOTE: Notification completion handles not implemented.
  4560. //
  4561. ;
  4562. }
  4563. }
  4564. classEntry = nextEntry;
  4565. }
  4566. pass=GetNextPass(pass,FALSE);
  4567. classEntry = GetFirstNotifyEntry (&ClassList[hashValue],0);
  4568. }
  4569. UnlockNotifyList(&notifyList->Lock);
  4570. }
  4571. //
  4572. // Perform Win9x compatible device interface arrival and removal notification.
  4573. //
  4574. BroadcastCompatibleDeviceMsg(EventId, ClassData);
  4575. HeapFree(ghPnPHeap, 0, ClassData);
  4576. //
  4577. // For device interface notification, there are no query type events, by
  4578. // definition, so we always return TRUE from this routine (no veto).
  4579. //
  4580. return TRUE;
  4581. } // NotifyInterfaceClassChange
  4582. ULONG
  4583. NotifyTargetDeviceChange(
  4584. IN DWORD ServiceControl,
  4585. IN DWORD EventId,
  4586. IN DWORD Flags,
  4587. IN PDEV_BROADCAST_HANDLE HandleData,
  4588. IN LPWSTR DeviceId,
  4589. OUT PPNP_VETO_TYPE VetoType OPTIONAL,
  4590. OUT LPWSTR VetoName OPTIONAL,
  4591. IN OUT PULONG VetoNameLength OPTIONAL
  4592. )
  4593. /*++
  4594. Routine Description:
  4595. This routine notifies registered services and windows of target device
  4596. change events.
  4597. Arguments:
  4598. ServiceControl - Specifies class of service event (power, device, hwprofile
  4599. change).
  4600. EventId - Specifies the DBT style event id for the device event.
  4601. (see sdk\inc\dbt.h for defined device events)
  4602. Flags - Specifies BroadcastSystemMessage BSF_ flags.
  4603. Note that BroadcastSystemMessage is not actually used for
  4604. target device events, but the specified BSF_ flags are used
  4605. to determine query and cancel event notification ordering.
  4606. HandleData - Pointer to a PDEV_BROADCAST_HANDLE structure that is
  4607. already filled out with most of the pertinent data for this
  4608. event.
  4609. DeviceId - Supplies the device instance id of the target device for
  4610. this event.
  4611. VetoType - For query-type events, supplies the address of a variable to
  4612. receive, upon failure, the type of the component responsible
  4613. for vetoing the request.
  4614. VetoName - For query-type events, supplies the address of a variable to
  4615. receive, upon failure, the name of the component
  4616. responsible for vetoing the request.
  4617. VetoNameLength - For query-type events, supplies the address of a variable
  4618. specifying the size of the of buffer specified by the
  4619. VetoName parameter. Upon failure, this address will specify
  4620. the length of the string stored in that buffer by this
  4621. routine.
  4622. Return Value:
  4623. Returns FALSE in the case of a vetoed query event, TRUE otherwise.
  4624. Note:
  4625. For DBT_DEVICEARRIVAL, DBT_DEVICEREMOVEPENDING, and DBT_DEVICEREMOVECOMPLETE
  4626. events this routine also broadcasts a WM_DEVICECHANGE / DBT_DEVNODES_CHANGED
  4627. message to all windows. There is no additional device-specific data for
  4628. this message; it is only used by components like device manager to refresh
  4629. the list of devices in the system.
  4630. Also note that the DBT_DEVNODES_CHANGED message is the only notification
  4631. sent for DBT_DEVICEARRIVAL (kernel GUID_DEVICE_ARRIVAL) events.
  4632. --*/
  4633. {
  4634. NTSTATUS ntStatus = STATUS_SUCCESS;
  4635. LONG result = 0;
  4636. ULONG hashValue, pass;
  4637. PPNP_NOTIFY_ENTRY targetEntry, nextEntry;
  4638. PPNP_NOTIFY_LIST notifyList;
  4639. BOOL bLocked = FALSE;
  4640. DWORD err;
  4641. BOOL serviceVetoedQuery;
  4642. DWORD recipients = BSM_ALLDESKTOPS | BSM_APPLICATIONS;
  4643. LONG response;
  4644. #ifdef _WIN64
  4645. DEV_BROADCAST_HANDLE32 UNALIGNED *HandleData32 = NULL;
  4646. ULONG ulHandleDataSize;
  4647. #endif // _WIN64
  4648. PVOID pHandleData;
  4649. serviceVetoedQuery = FALSE;
  4650. //
  4651. // If we're doing a query, then VetoType, VetoName, and VetoNameLength must
  4652. // all be specified.
  4653. //
  4654. ASSERT(!(Flags & BSF_QUERY) || (VetoType && VetoName && VetoNameLength));
  4655. if (!(Flags & BSF_QUERY) && (VetoNameLength != NULL)) {
  4656. //
  4657. // Not vetoable.
  4658. //
  4659. *VetoNameLength = 0;
  4660. }
  4661. //
  4662. // Broadcast the DBT_DEVNODES_CHANGED message before any other notification
  4663. // events, so components listening for those can update themselves in a
  4664. // timely manner, and not be delayed by apps/services hung on their
  4665. // notification event. This broadcasts is a post, so it will return
  4666. // immediately, and complete asynchronously.
  4667. //
  4668. if ((EventId == DBT_DEVICEARRIVAL) ||
  4669. (EventId == DBT_DEVICEREMOVEPENDING) ||
  4670. (EventId == DBT_DEVICEREMOVECOMPLETE)) {
  4671. BroadcastSystemMessage(BSF_POSTMESSAGE,
  4672. &recipients,
  4673. WM_DEVICECHANGE,
  4674. DBT_DEVNODES_CHANGED,
  4675. (LPARAM)NULL);
  4676. if (fpWinStationBroadcastSystemMessage) {
  4677. try {
  4678. fpWinStationBroadcastSystemMessage(SERVERNAME_CURRENT,
  4679. TRUE,
  4680. 0,
  4681. DEFAULT_BROADCAST_TIME_OUT,
  4682. BSF_NOHANG | BSF_POSTMESSAGE,
  4683. &recipients,
  4684. WM_DEVICECHANGE,
  4685. (WPARAM)DBT_DEVNODES_CHANGED,
  4686. (LPARAM)NULL,
  4687. &response);
  4688. } except (EXCEPTION_EXECUTE_HANDLER) {
  4689. KdPrintEx((DPFLTR_PNPMGR_ID,
  4690. DBGF_ERRORS,
  4691. "UMPNPMGR: Exception calling WinStationBroadcastSystemMessage!\n"));
  4692. ASSERT(0);
  4693. }
  4694. }
  4695. }
  4696. //
  4697. // For target device arrival events, no additional notification is
  4698. // performed.
  4699. //
  4700. if (EventId == DBT_DEVICEARRIVAL) {
  4701. goto Clean0;
  4702. }
  4703. #ifdef _WIN64
  4704. //
  4705. // Prepare a 32-bit notification structure, which we'll need to send to any
  4706. // WOW64 clients that are registered.
  4707. //
  4708. ASSERT(sizeof(DEV_BROADCAST_HANDLE) == sizeof(DEV_BROADCAST_HANDLE64));
  4709. ASSERT(HandleData->dbch_size >= sizeof(DEV_BROADCAST_HANDLE64));
  4710. ulHandleDataSize = HandleData->dbch_size -
  4711. sizeof(DEV_BROADCAST_HANDLE64) +
  4712. sizeof(DEV_BROADCAST_HANDLE32);
  4713. ASSERT(ulHandleDataSize >= sizeof(DEV_BROADCAST_HANDLE32));
  4714. HandleData32 = HeapAlloc(ghPnPHeap, 0, ulHandleDataSize);
  4715. if (HandleData32 == NULL) {
  4716. goto Clean0;
  4717. }
  4718. memset(HandleData32, 0, ulHandleDataSize);
  4719. HandleData32->dbch_size = ulHandleDataSize;
  4720. HandleData32->dbch_devicetype = DBT_DEVTYP_HANDLE;
  4721. HandleData32->dbch_nameoffset = HandleData->dbch_nameoffset;
  4722. memcpy(&HandleData32->dbch_eventguid,
  4723. &HandleData->dbch_eventguid,
  4724. sizeof(GUID));
  4725. memcpy(&HandleData32->dbch_data,
  4726. &HandleData->dbch_data,
  4727. (HandleData->dbch_size - FIELD_OFFSET(DEV_BROADCAST_HANDLE, dbch_data)));
  4728. #endif // _WIN64
  4729. //
  4730. // Sanitize the device id
  4731. //
  4732. FixUpDeviceId(DeviceId);
  4733. //
  4734. // The list of registered callers is hashed for quicker access and
  4735. // comparison. Walk the list of registered callers and notify anyone
  4736. // that registered an interest in this device instance.
  4737. //
  4738. hashValue = HashString(DeviceId, TARGET_HASH_BUCKETS);
  4739. notifyList = &TargetList[hashValue];
  4740. LockNotifyList(&notifyList->Lock);
  4741. bLocked = TRUE;
  4742. pass = GetFirstPass(Flags & BSF_QUERY);
  4743. do {
  4744. targetEntry = GetFirstNotifyEntry (notifyList,Flags);
  4745. while (targetEntry) {
  4746. nextEntry = GetNextNotifyEntry(targetEntry,Flags);
  4747. if (targetEntry->Unregistered) {
  4748. targetEntry = nextEntry;
  4749. continue;
  4750. }
  4751. if (lstrcmpi(DeviceId, targetEntry->u.Target.DeviceId) == 0) {
  4752. if ((pass == DEVICE_NOTIFY_WINDOW_HANDLE) &&
  4753. (GetPassFromEntry(targetEntry) == DEVICE_NOTIFY_WINDOW_HANDLE)) {
  4754. //
  4755. // Note: we could get away with only doing a send message
  4756. // if the Flags has BSF_QUERY set and do a post message in
  4757. // all other cases. Unfortunately, the PostMessage call currently
  4758. // fails if the high bit of the wParam value is set (which it is in
  4759. // this case), so we are forced to Send the message (rather than
  4760. // Post it). USER group implemented it this way because the original
  4761. // Win95 spec doesn't call for the recipient to free the message
  4762. // so we have to free it and we have no idea when it's safe
  4763. // with a PostMessage call.
  4764. //
  4765. HandleData->dbch_handle = targetEntry->u.Target.FileHandle;
  4766. HandleData->dbch_hdevnotify = (HDEVNOTIFY)targetEntry->ClientCtxPtr;
  4767. UnlockNotifyList(&notifyList->Lock);
  4768. bLocked = FALSE;
  4769. //
  4770. // Always send the native DEV_BROADCAST_HANDLE structure to
  4771. // windows. If any 64-bit/32-bit conversion needs to be
  4772. // done for this client, ntuser will do it for us.
  4773. //
  4774. pHandleData = HandleData;
  4775. if (targetEntry->SessionId == MAIN_SESSION ) {
  4776. ntStatus = DeviceEventWorker(targetEntry->Handle,
  4777. EventId,
  4778. (LPARAM)pHandleData,
  4779. TRUE,
  4780. &result);
  4781. } else {
  4782. if (fpWinStationSendWindowMessage) {
  4783. try {
  4784. if (fpWinStationSendWindowMessage(SERVERNAME_CURRENT,
  4785. targetEntry->SessionId,
  4786. DEFAULT_SEND_TIME_OUT,
  4787. HandleToUlong(targetEntry->Handle),
  4788. WM_DEVICECHANGE,
  4789. (WPARAM)EventId,
  4790. (LPARAM)pHandleData,
  4791. &result)) {
  4792. ntStatus = STATUS_SUCCESS;
  4793. } else {
  4794. ntStatus = STATUS_UNSUCCESSFUL;
  4795. }
  4796. } except (EXCEPTION_EXECUTE_HANDLER) {
  4797. KdPrintEx((DPFLTR_PNPMGR_ID,
  4798. DBGF_ERRORS,
  4799. "UMPNPMGR: Exception calling WinStationSendWindowMessage!\n"));
  4800. ASSERT(0);
  4801. ntStatus = STATUS_SUCCESS;
  4802. }
  4803. }
  4804. }
  4805. LockNotifyList(&notifyList->Lock);
  4806. bLocked = TRUE;
  4807. if (NT_SUCCESS(ntStatus)) {
  4808. //
  4809. // This call succeeded, if it's a query type call, check
  4810. // the result returned.
  4811. //
  4812. if ((Flags & BSF_QUERY) && (result == BROADCAST_QUERY_DENY)) {
  4813. KdPrintEx((DPFLTR_PNPMGR_ID,
  4814. DBGF_EVENT,
  4815. "UMPNPMGR: Window '%ws' vetoed TargetDeviceChangeEvent\n",
  4816. targetEntry->ClientName));
  4817. WindowVeto(targetEntry, VetoType, VetoName, VetoNameLength);
  4818. //
  4819. // Haven't told the services yet. Note that we
  4820. // always call this routine with the native
  4821. // DEV_BROADCAST_HANDLE structure, since it walks
  4822. // the entire list itself. It will do the
  4823. // conversion again, if necessary.
  4824. //
  4825. SendCancelNotification(targetEntry,
  4826. ServiceControl,
  4827. EventId,
  4828. BSF_QUERY,
  4829. (PDEV_BROADCAST_HDR)HandleData,
  4830. DeviceId);
  4831. goto Clean0;
  4832. }
  4833. } else if (ntStatus == STATUS_INVALID_HANDLE) {
  4834. //
  4835. // window handle no longer exists, cleanup this entry
  4836. //
  4837. KdPrintEx((DPFLTR_PNPMGR_ID,
  4838. DBGF_ERRORS | DBGF_WARNINGS,
  4839. "UMPNPMGR: Invalid window handle for '%ws' during TargetDeviceChangeEvent, removing entry.\n",
  4840. targetEntry->ClientName));
  4841. targetEntry->Freed |= (PNP_UNREG_FREE|PNP_UNREG_DEFER|PNP_UNREG_WIN);
  4842. DeleteNotifyEntry(targetEntry,FALSE);
  4843. } else if (ntStatus == STATUS_UNSUCCESSFUL) {
  4844. KdPrintEx((DPFLTR_PNPMGR_ID,
  4845. DBGF_ERRORS | DBGF_WARNINGS,
  4846. "UMPNPMGR: Window '%ws' timed out on TargetDeviceChangeEvent\n",
  4847. targetEntry->ClientName));
  4848. LogWarningEvent(WRN_TARGET_DEVICE_CHANGE_TIMED_OUT, 1, targetEntry->ClientName);
  4849. }
  4850. } else if ((pass == DEVICE_NOTIFY_SERVICE_HANDLE) &&
  4851. (GetPassFromEntry(targetEntry) == DEVICE_NOTIFY_SERVICE_HANDLE)) {
  4852. if (pServiceControlCallback) {
  4853. //
  4854. // Call the services handler routine...
  4855. //
  4856. HandleData->dbch_handle = targetEntry->u.Target.FileHandle;
  4857. HandleData->dbch_hdevnotify = (HDEVNOTIFY)targetEntry->ClientCtxPtr;
  4858. //
  4859. // Assume we're sending the native DEV_BROADCAST_HANDLE
  4860. // structure.
  4861. //
  4862. pHandleData = HandleData;
  4863. #if _WIN64
  4864. //
  4865. // If the client is running on WOW64, send it the 32-bit
  4866. // DEV_BROADCAST_HANDLE structure we created instead.
  4867. //
  4868. if (targetEntry->Flags & DEVICE_NOTIFY_WOW64_CLIENT) {
  4869. HandleData32->dbch_handle = (ULONG32)PtrToUlong(targetEntry->u.Target.FileHandle);
  4870. HandleData32->dbch_hdevnotify = (ULONG32)PtrToUlong((HDEVNOTIFY)targetEntry->ClientCtxPtr);
  4871. pHandleData = HandleData32;
  4872. }
  4873. #endif // _WIN64
  4874. try {
  4875. UnlockNotifyList(&notifyList->Lock);
  4876. bLocked = FALSE;
  4877. try {
  4878. (pServiceControlCallback)((SERVICE_STATUS_HANDLE)targetEntry->Handle,
  4879. ServiceControl,
  4880. EventId,
  4881. (LPARAM)pHandleData,
  4882. &err);
  4883. } except (EXCEPTION_EXECUTE_HANDLER) {
  4884. KdPrintEx((DPFLTR_PNPMGR_ID,
  4885. DBGF_ERRORS,
  4886. "UMPNPMGR: Exception calling Service Control Manager!\n"));
  4887. ASSERT(0);
  4888. err = NO_ERROR;
  4889. }
  4890. LockNotifyList(&notifyList->Lock);
  4891. bLocked = TRUE;
  4892. //
  4893. // convert Win32 error into window message-style
  4894. // return value
  4895. //
  4896. if (err == NO_ERROR) {
  4897. result = TRUE;
  4898. } else {
  4899. KdPrintEx((DPFLTR_PNPMGR_ID,
  4900. DBGF_EVENT,
  4901. "UMPNPMGR: Service %ws responded to TargetDeviceChangeEvent with status=0x%08lx\n",
  4902. targetEntry->ClientName,
  4903. err));
  4904. //
  4905. // This service specifically requested to receive this
  4906. // notification - it should know how to handle it.
  4907. //
  4908. ASSERT(err != ERROR_CALL_NOT_IMPLEMENTED);
  4909. //
  4910. // Log the error the service used to veto.
  4911. //
  4912. LogWarningEvent(WRN_TARGET_DEVICE_CHANGE_SERVICE_VETO,
  4913. 1,
  4914. targetEntry->ClientName);
  4915. result = BROADCAST_QUERY_DENY;
  4916. }
  4917. if ((Flags & BSF_QUERY) && (result == BROADCAST_QUERY_DENY)) {
  4918. serviceVetoedQuery = TRUE;
  4919. ServiceVeto(targetEntry, VetoType, VetoName, VetoNameLength );
  4920. //
  4921. // This service vetoed the query, tell everyone
  4922. // else it was cancelled. Note that we always
  4923. // call this routine with the native
  4924. // DEV_BROADCAST_HANDLE structure, since it
  4925. // walks the entire list itself. It will do the
  4926. // conversion again, if necessary.
  4927. //
  4928. SendCancelNotification(targetEntry,
  4929. ServiceControl,
  4930. EventId,
  4931. BSF_QUERY,
  4932. (PDEV_BROADCAST_HDR)HandleData,
  4933. DeviceId);
  4934. }
  4935. } except (EXCEPTION_EXECUTE_HANDLER) {
  4936. KdPrintEx((DPFLTR_PNPMGR_ID,
  4937. DBGF_ERRORS,
  4938. "UMPNPMGR: Exception calling Service Control Manager!\n"));
  4939. ASSERT(0);
  4940. //
  4941. // Reference the "serviceVetoedQuery" variable to
  4942. // ensure the compiler will respect statement
  4943. // ordering w.r.t. this variable. We want to make
  4944. // sure we know with certainty whether any service
  4945. // vetoed the query, even if subsequently sending
  4946. // the cancel caused an access violation.
  4947. //
  4948. serviceVetoedQuery = serviceVetoedQuery;
  4949. }
  4950. if (serviceVetoedQuery) {
  4951. goto Clean0;
  4952. }
  4953. }
  4954. } else if ((pass == DEVICE_NOTIFY_COMPLETION_HANDLE) &&
  4955. (GetPassFromEntry(targetEntry) == DEVICE_NOTIFY_COMPLETION_HANDLE)) {
  4956. //
  4957. // Complete the notification handle.
  4958. // NOTE: Notification completion handles not implemented.
  4959. //
  4960. ;
  4961. }
  4962. }
  4963. targetEntry = nextEntry;
  4964. } // while
  4965. } while ((pass = GetNextPass(pass, (Flags & BSF_QUERY))) != PASS_COMPLETE);
  4966. if (VetoNameLength != NULL) {
  4967. *VetoNameLength = 0;
  4968. }
  4969. Clean0:
  4970. if (bLocked) {
  4971. UnlockNotifyList(&notifyList->Lock);
  4972. }
  4973. #ifdef _WIN64
  4974. //
  4975. // Free the 32-bit DEV_BROADCAST_HANDLE structure, if we allocated one.
  4976. //
  4977. if (HandleData32 != NULL) {
  4978. HeapFree(ghPnPHeap, 0, HandleData32);
  4979. }
  4980. #endif // _WIN64
  4981. return (result != BROADCAST_QUERY_DENY);
  4982. } // NotifyTargetDeviceChange
  4983. ULONG
  4984. NotifyHardwareProfileChange(
  4985. IN DWORD ServiceControl,
  4986. IN DWORD EventId,
  4987. IN DWORD Flags,
  4988. OUT PPNP_VETO_TYPE VetoType OPTIONAL,
  4989. OUT LPWSTR VetoName OPTIONAL,
  4990. IN OUT PULONG VetoNameLength OPTIONAL
  4991. )
  4992. /*++
  4993. Routine Description:
  4994. This routine notifies registered services and all windows of hardware
  4995. profile change events.
  4996. Arguments:
  4997. ServiceControl - Specifies class of service event (power, device, hwprofile
  4998. change).
  4999. EventId - Specifies the DBT style event id for the device event.
  5000. (see sdk\inc\dbt.h for defined hardware profile change events)
  5001. Flags - Specifies BroadcastSystemMessage BSF_ flags.
  5002. VetoType - For query-type events, supplies the address of a variable to
  5003. receive, upon failure, the type of the component responsible
  5004. for vetoing the request.
  5005. VetoName - For query-type events, supplies the address of a variable to
  5006. receive, upon failure, the name of the component
  5007. responsible for vetoing the request.
  5008. VetoNameLength - For query-type events, supplies the address of a variable
  5009. specifying the size of the of buffer specified by the
  5010. VetoName parameter. Upon failure, this address will specify
  5011. the length of the string stored in that buffer by this
  5012. routine.
  5013. Return Value:
  5014. Returns FALSE in the case of a vetoed query event, TRUE otherwise.
  5015. --*/
  5016. {
  5017. DWORD pass;
  5018. DWORD recipients = BSM_ALLDESKTOPS | BSM_APPLICATIONS;
  5019. BSMINFO bsmInfo;
  5020. ULONG length;
  5021. PPNP_NOTIFY_ENTRY entry, nextEntry;
  5022. PPNP_NOTIFY_LIST notifyList;
  5023. BOOL bLocked = FALSE;
  5024. LONG response;
  5025. ULONG successful;
  5026. LONG result;
  5027. DWORD err;
  5028. //
  5029. // If we're doing a query, then VetoType, VetoName, and VetoNameLength must
  5030. // all be specified.
  5031. //
  5032. ASSERT(!(Flags & BSF_QUERY) || (VetoType && VetoName && VetoNameLength));
  5033. if (!(Flags & BSF_QUERY) && (VetoNameLength != NULL)) {
  5034. //
  5035. // Not vetoable.
  5036. //
  5037. *VetoNameLength = 0;
  5038. }
  5039. notifyList = &ServiceList[CINDEX_HWPROFILE];
  5040. LockNotifyList(&notifyList->Lock);
  5041. bLocked = TRUE;
  5042. successful = TRUE;
  5043. pass = GetFirstPass(Flags & BSF_QUERY);
  5044. try {
  5045. while (pass != PASS_COMPLETE) {
  5046. if (pass == DEVICE_NOTIFY_WINDOW_HANDLE) {
  5047. //
  5048. // Notify the Windows
  5049. //
  5050. UnlockNotifyList (&notifyList->Lock);
  5051. bLocked = FALSE;
  5052. bsmInfo.cbSize = sizeof(BSMINFO);
  5053. result = BroadcastSystemMessageEx(Flags | BSF_RETURNHDESK,
  5054. &recipients,
  5055. WM_DEVICECHANGE,
  5056. EventId,
  5057. (LPARAM)NULL,
  5058. &bsmInfo);
  5059. if ((result <= 0) && (Flags & BSF_QUERY)) {
  5060. HDESK hDeskService = GetThreadDesktop(GetCurrentThreadId());
  5061. SetThreadDesktop(bsmInfo.hdesk);
  5062. WinBroadcastVeto(bsmInfo.hwnd, VetoType, VetoName, VetoNameLength);
  5063. SetThreadDesktop(hDeskService);
  5064. CloseDesktop(bsmInfo.hdesk);
  5065. successful = FALSE;
  5066. break;
  5067. }
  5068. if ((result > 0) || (!(Flags & BSF_QUERY))) {
  5069. if (fpWinStationBroadcastSystemMessage) {
  5070. try {
  5071. fpWinStationBroadcastSystemMessage(SERVERNAME_CURRENT,
  5072. TRUE,
  5073. 0,
  5074. DEFAULT_BROADCAST_TIME_OUT,
  5075. Flags,
  5076. &recipients,
  5077. WM_DEVICECHANGE,
  5078. (WPARAM)EventId,
  5079. (LPARAM)NULL,
  5080. &result);
  5081. } except (EXCEPTION_EXECUTE_HANDLER) {
  5082. KdPrintEx((DPFLTR_PNPMGR_ID,
  5083. DBGF_ERRORS,
  5084. "UMPNPMGR: Exception calling WinStationBroadcastSystemMessage!\n"));
  5085. ASSERT(0);
  5086. result = 1;
  5087. }
  5088. }
  5089. }
  5090. LockNotifyList (&notifyList->Lock);
  5091. bLocked = TRUE;
  5092. if ((result < 0) && (Flags & BSF_QUERY)) {
  5093. UnknownVeto(VetoType, VetoName, VetoNameLength);
  5094. successful = FALSE;
  5095. break;
  5096. } else if ((result == 0) && (Flags & BSF_QUERY)) {
  5097. WinBroadcastVeto(NULL, VetoType, VetoName, VetoNameLength);
  5098. successful = FALSE;
  5099. break;
  5100. }
  5101. } else if (pass == DEVICE_NOTIFY_SERVICE_HANDLE) {
  5102. //
  5103. // Notify the services
  5104. //
  5105. entry = GetFirstNotifyEntry (notifyList,Flags & BSF_QUERY);
  5106. while (entry) {
  5107. nextEntry = GetNextNotifyEntry(entry,Flags & BSF_QUERY);
  5108. if (entry->Unregistered) {
  5109. entry = nextEntry;
  5110. continue;
  5111. }
  5112. ASSERT(GetPassFromEntry(entry) == DEVICE_NOTIFY_SERVICE_HANDLE);
  5113. //
  5114. // This is a direct call, not a message via. USER
  5115. //
  5116. if (pServiceControlCallback) {
  5117. UnlockNotifyList (&notifyList->Lock);
  5118. bLocked = FALSE;
  5119. try {
  5120. (pServiceControlCallback)((SERVICE_STATUS_HANDLE)entry->Handle,
  5121. ServiceControl,
  5122. EventId,
  5123. (LPARAM)NULL,
  5124. &err);
  5125. } except (EXCEPTION_EXECUTE_HANDLER) {
  5126. KdPrintEx((DPFLTR_PNPMGR_ID,
  5127. DBGF_ERRORS,
  5128. "UMPNPMGR: Exception calling Service Control Manager!\n"));
  5129. ASSERT(0);
  5130. err = NO_ERROR;
  5131. }
  5132. LockNotifyList (&notifyList->Lock);
  5133. bLocked = TRUE;
  5134. //
  5135. // convert Win32 error into window message-style return
  5136. // value.
  5137. //
  5138. if (err == NO_ERROR) {
  5139. result = TRUE;
  5140. } else {
  5141. KdPrintEx((DPFLTR_PNPMGR_ID,
  5142. DBGF_EVENT,
  5143. "UMPNPMGR: Service %ws responded to HardwareProfileChangeEvent with status=0x%08lx\n",
  5144. entry->ClientName,
  5145. err));
  5146. //
  5147. // This service specifically requested to receive this
  5148. // notification - it should know how to handle it.
  5149. //
  5150. ASSERT(err != ERROR_CALL_NOT_IMPLEMENTED);
  5151. //
  5152. // Log the error the service used to veto.
  5153. //
  5154. LogWarningEvent(WRN_HWPROFILE_CHANGE_SERVICE_VETO,
  5155. 1,
  5156. entry->ClientName);
  5157. result = BROADCAST_QUERY_DENY;
  5158. }
  5159. if ((Flags & BSF_QUERY) &&
  5160. (result == BROADCAST_QUERY_DENY)) {
  5161. ServiceVeto(entry,
  5162. VetoType,
  5163. VetoName,
  5164. VetoNameLength);
  5165. successful = FALSE;
  5166. break;
  5167. }
  5168. }
  5169. entry = nextEntry;
  5170. }
  5171. }
  5172. if (!successful) {
  5173. break;
  5174. }
  5175. pass = GetNextPass (pass,Flags & BSF_QUERY);
  5176. }
  5177. } except (EXCEPTION_EXECUTE_HANDLER) {
  5178. KdPrintEx((DPFLTR_PNPMGR_ID,
  5179. DBGF_ERRORS,
  5180. "UMPNPMGR: Exception in service callback in NotifyHardwareProfileChange\n"));
  5181. ASSERT(0);
  5182. if (Flags & BSF_QUERY) {
  5183. UnknownVeto(VetoType, VetoName, VetoNameLength);
  5184. successful = FALSE;
  5185. }
  5186. }
  5187. try {
  5188. if (!successful) {
  5189. ASSERT(Flags & BSF_QUERY);
  5190. //
  5191. // If a service vetoed the query, inform the services and windows,
  5192. // otherwise only the windows know what was coming.
  5193. //
  5194. if (pass == DEVICE_NOTIFY_SERVICE_HANDLE) {
  5195. SendCancelNotification(
  5196. entry,
  5197. ServiceControl,
  5198. EventId,
  5199. BSF_QUERY,
  5200. NULL,
  5201. NULL);
  5202. }
  5203. UnlockNotifyList (&notifyList->Lock);
  5204. bLocked = FALSE;
  5205. BroadcastSystemMessage(Flags & ~BSF_QUERY,
  5206. &recipients,
  5207. WM_DEVICECHANGE,
  5208. MapQueryEventToCancelEvent(EventId),
  5209. (LPARAM)NULL);
  5210. if (fpWinStationBroadcastSystemMessage) {
  5211. try {
  5212. fpWinStationBroadcastSystemMessage(SERVERNAME_CURRENT,
  5213. TRUE,
  5214. 0,
  5215. DEFAULT_BROADCAST_TIME_OUT,
  5216. Flags & ~BSF_QUERY,
  5217. &recipients,
  5218. WM_DEVICECHANGE,
  5219. (WPARAM)MapQueryEventToCancelEvent(EventId),
  5220. (LPARAM)NULL,
  5221. &response);
  5222. } except (EXCEPTION_EXECUTE_HANDLER) {
  5223. KdPrintEx((DPFLTR_PNPMGR_ID,
  5224. DBGF_ERRORS,
  5225. "UMPNPMGR: Exception calling WinStationBroadcastSystemMessage\n"));
  5226. ASSERT(0);
  5227. }
  5228. }
  5229. LockNotifyList (&notifyList->Lock);
  5230. bLocked = TRUE;
  5231. }
  5232. } except (EXCEPTION_EXECUTE_HANDLER) {
  5233. KdPrintEx((DPFLTR_PNPMGR_ID,
  5234. DBGF_ERRORS,
  5235. "UMPNPMGR: Exception in service callback in NotifyHardwareProfileChange\n"));
  5236. ASSERT(0);
  5237. }
  5238. if (bLocked) {
  5239. UnlockNotifyList (&notifyList->Lock);
  5240. }
  5241. //
  5242. // if successful, we are not returning veto info.
  5243. //
  5244. if (successful && (VetoNameLength != NULL)) {
  5245. *VetoNameLength = 0;
  5246. }
  5247. return successful;
  5248. } // NotifyHardwareProfileChange
  5249. BOOL
  5250. SendCancelNotification(
  5251. IN PPNP_NOTIFY_ENTRY LastEntry,
  5252. IN DWORD ServiceControl,
  5253. IN DWORD EventId,
  5254. IN ULONG Flags,
  5255. IN PDEV_BROADCAST_HDR NotifyData OPTIONAL,
  5256. IN LPWSTR DeviceId OPTIONAL
  5257. )
  5258. /*++
  5259. Routine Description:
  5260. This routine sends a cancel notification to the entries in the range
  5261. specified. This routine assumes the appropriate list is already locked.
  5262. Arguments:
  5263. LastEntry - Specifies the last list entry that received the original
  5264. query notification, and was responsible for failing the
  5265. request. We will stop sending cancel notification events
  5266. when we get to this one.
  5267. ServiceControl - Specifies class of service event (power, device, hwprofile
  5268. change).
  5269. EventId - Specifies the DBT style event id for the device event.
  5270. (see sdk\inc\dbt.h for defined device events)
  5271. Flags - Specifies BroadcastSystemMessage BSF_ flags.
  5272. Note that BroadcastSystemMessage is not actually used for
  5273. target device events, but the specified BSF_ flags are used
  5274. to determine query and cancel event notification ordering.
  5275. NotifyData - Optionally, supplies a pointer to a PDEV_BROADCAST_Xxx
  5276. structure that is already filled out with most of the
  5277. pertinent data for this event.
  5278. This parameter may be NULL for "global" events that are not
  5279. associated with any device, such as power and hardware
  5280. profile change events.
  5281. DeviceId - Optionally, supplies the device instance id of the target
  5282. device for this event.
  5283. This parameter may be NULL for "global" events that are not
  5284. associated with any device, such as power and hardware
  5285. profile change events.
  5286. Return Value:
  5287. Returns TRUE / FALSE.
  5288. --*/
  5289. {
  5290. NTSTATUS ntStatus = STATUS_SUCCESS;
  5291. DWORD cancelEventId;
  5292. DWORD result, pass, lastPass;
  5293. PPNP_NOTIFY_ENTRY entry, headEntry;
  5294. PPNP_NOTIFY_LIST notifyList;
  5295. #ifdef _WIN64
  5296. DEV_BROADCAST_HANDLE32 UNALIGNED *HandleData32 = NULL;
  5297. ULONG ulHandleDataSize;
  5298. #endif // _WIN64
  5299. PVOID pNotifyData;
  5300. #ifdef _WIN64
  5301. if ((ARGUMENT_PRESENT(NotifyData)) &&
  5302. (NotifyData->dbch_devicetype == DBT_DEVTYP_HANDLE)) {
  5303. //
  5304. // If cancelling a DEV_BROADCAST_HANDLE type event, prepare a 32-bit
  5305. // notification structure, which we'll need to send to any WOW64 clients
  5306. // that are registered.
  5307. //
  5308. ulHandleDataSize = ((PDEV_BROADCAST_HANDLE)NotifyData)->dbch_size -
  5309. sizeof(DEV_BROADCAST_HANDLE64) +
  5310. sizeof(DEV_BROADCAST_HANDLE32);
  5311. ASSERT(ulHandleDataSize >= sizeof(DEV_BROADCAST_HANDLE32));
  5312. HandleData32 = HeapAlloc(ghPnPHeap, 0, ulHandleDataSize);
  5313. if (HandleData32 == NULL) {
  5314. return FALSE;
  5315. }
  5316. memset(HandleData32, 0, ulHandleDataSize);
  5317. HandleData32->dbch_size = ulHandleDataSize;
  5318. HandleData32->dbch_devicetype = DBT_DEVTYP_HANDLE;
  5319. HandleData32->dbch_nameoffset = ((PDEV_BROADCAST_HANDLE)NotifyData)->dbch_nameoffset;
  5320. memcpy(&HandleData32->dbch_eventguid,
  5321. &((PDEV_BROADCAST_HANDLE)NotifyData)->dbch_eventguid,
  5322. sizeof(GUID));
  5323. memcpy(&HandleData32->dbch_data,
  5324. &((PDEV_BROADCAST_HANDLE)NotifyData)->dbch_data,
  5325. (NotifyData->dbch_size - FIELD_OFFSET(DEV_BROADCAST_HANDLE, dbch_data)));
  5326. }
  5327. #endif // _WIN64
  5328. //
  5329. // Use the appropriate cancel device event id that corresponds to the
  5330. // original query device event id.
  5331. //
  5332. cancelEventId = MapQueryEventToCancelEvent(EventId);
  5333. //
  5334. // Get the corresponding notification list
  5335. //
  5336. notifyList = GetNotifyListForEntry(LastEntry);
  5337. ASSERT(notifyList);
  5338. if (notifyList == NULL) {
  5339. return FALSE;
  5340. }
  5341. //
  5342. // Get the pass we vetoed things on
  5343. //
  5344. lastPass = GetPassFromEntry(LastEntry);
  5345. //
  5346. // Get the opposite end of the list
  5347. //
  5348. headEntry = GetFirstNotifyEntry(notifyList, (Flags ^ BSF_QUERY));
  5349. //
  5350. // Walk the list of registered callers backwards(!) and notify anyone that registered
  5351. // an interest in this device instance. Start with the FirstEntry and stop
  5352. // just before the LastEntry (the LastEntry is the one that vetoed the
  5353. // request in the first place).
  5354. //
  5355. for(pass = lastPass;
  5356. pass != PASS_COMPLETE;
  5357. pass = GetNextPass(pass, (Flags ^ BSF_QUERY))) {
  5358. //
  5359. // If this is the pass the request was vetoed on, then start on the
  5360. // vetoer entry itself. Otherwise begin again at the appropriate end
  5361. // of the list.
  5362. //
  5363. for(entry = (pass == lastPass) ? LastEntry : headEntry;
  5364. entry;
  5365. entry = GetNextNotifyEntry(entry, (Flags ^ BSF_QUERY))) {
  5366. if (!NotifyEntryThisPass(entry, pass)) {
  5367. continue;
  5368. }
  5369. switch(pass) {
  5370. case DEVICE_NOTIFY_SERVICE_HANDLE:
  5371. if ((!DeviceId) || (lstrcmpi(DeviceId, entry->u.Target.DeviceId) == 0)) {
  5372. if (pServiceControlCallback) {
  5373. //
  5374. // Assume we're sending the native structure.
  5375. //
  5376. pNotifyData = NotifyData;
  5377. if ((ARGUMENT_PRESENT(NotifyData)) &&
  5378. (NotifyData->dbch_devicetype == DBT_DEVTYP_HANDLE)) {
  5379. //
  5380. // If it's a DBT_DEVTYP_HANDLE notification, set
  5381. // the hdevnotify and file handle fields for the
  5382. // client we're notifying.
  5383. //
  5384. ((PDEV_BROADCAST_HANDLE)NotifyData)->dbch_handle = entry->u.Target.FileHandle;
  5385. ((PDEV_BROADCAST_HANDLE)NotifyData)->dbch_hdevnotify = (HDEVNOTIFY)entry->ClientCtxPtr;
  5386. #if _WIN64
  5387. //
  5388. // If the client is running on WOW64, send it the 32-bit
  5389. // DEV_BROADCAST_HANDLE structure we created instead.
  5390. //
  5391. if (entry->Flags & DEVICE_NOTIFY_WOW64_CLIENT) {
  5392. HandleData32->dbch_handle = (ULONG32)PtrToUlong(entry->u.Target.FileHandle);
  5393. HandleData32->dbch_hdevnotify = (ULONG32)PtrToUlong((HDEVNOTIFY)entry->ClientCtxPtr);
  5394. pNotifyData = HandleData32;
  5395. }
  5396. #endif // _WIN64
  5397. }
  5398. //
  5399. // Call the services handler routine...
  5400. //
  5401. UnlockNotifyList(&notifyList->Lock);
  5402. try {
  5403. (pServiceControlCallback)((SERVICE_STATUS_HANDLE)entry->Handle,
  5404. ServiceControl,
  5405. cancelEventId,
  5406. (LPARAM)pNotifyData,
  5407. &result);
  5408. } except (EXCEPTION_EXECUTE_HANDLER) {
  5409. KdPrintEx((DPFLTR_PNPMGR_ID,
  5410. DBGF_ERRORS,
  5411. "UMPNPMGR: Exception calling Service Control Manager!\n"));
  5412. ASSERT(0);
  5413. }
  5414. LockNotifyList(&notifyList->Lock);
  5415. }
  5416. }
  5417. break;
  5418. case DEVICE_NOTIFY_WINDOW_HANDLE:
  5419. //
  5420. // Notify the windows. Note that events with NULL DeviceId's
  5421. // (for example hardware profile change events) are not
  5422. // registerable by windows. Luckily for them, we broadcast
  5423. // such info anyway.
  5424. //
  5425. if (DeviceId && (lstrcmpi(DeviceId, entry->u.Target.DeviceId) == 0)) {
  5426. ASSERT(NotifyData);
  5427. //
  5428. // Always send the native DEV_BROADCAST_HANDLE structure to
  5429. // windows. If any 64-bit/32-bit conversion needs to be
  5430. // done for this client, ntuser will do it for us.
  5431. //
  5432. pNotifyData = NotifyData;
  5433. if ((ARGUMENT_PRESENT(NotifyData)) &&
  5434. (NotifyData->dbch_devicetype == DBT_DEVTYP_HANDLE)) {
  5435. //
  5436. // If it's a DBT_DEVTYP_HANDLE notification, set
  5437. // the hdevnotify and file handle fields for the
  5438. // client we're notifying.
  5439. //
  5440. ((PDEV_BROADCAST_HANDLE)NotifyData)->dbch_handle = entry->u.Target.FileHandle;
  5441. ((PDEV_BROADCAST_HANDLE)NotifyData)->dbch_hdevnotify = (HDEVNOTIFY)entry->ClientCtxPtr;
  5442. }
  5443. UnlockNotifyList(&notifyList->Lock);
  5444. if (entry->SessionId == MAIN_SESSION) {
  5445. ntStatus = DeviceEventWorker(entry->Handle,
  5446. cancelEventId,
  5447. (LPARAM)pNotifyData,
  5448. TRUE,
  5449. &result // ignore result
  5450. );
  5451. } else if (fpWinStationSendWindowMessage) {
  5452. try {
  5453. if (fpWinStationSendWindowMessage(SERVERNAME_CURRENT,
  5454. entry->SessionId,
  5455. DEFAULT_SEND_TIME_OUT,
  5456. HandleToUlong(entry->Handle),
  5457. WM_DEVICECHANGE,
  5458. (WPARAM)cancelEventId,
  5459. (LPARAM)pNotifyData,
  5460. &result)) {
  5461. ntStatus = STATUS_SUCCESS;
  5462. } else {
  5463. ntStatus = STATUS_UNSUCCESSFUL;
  5464. }
  5465. } except (EXCEPTION_EXECUTE_HANDLER) {
  5466. KdPrintEx((DPFLTR_PNPMGR_ID,
  5467. DBGF_ERRORS,
  5468. "UMPNPMGR: Exception calling WinStationSendWindowMessage!\n"));
  5469. ASSERT(0);
  5470. ntStatus = STATUS_SUCCESS;
  5471. }
  5472. }
  5473. LockNotifyList(&notifyList->Lock);
  5474. if (!NT_SUCCESS(ntStatus)) {
  5475. if (ntStatus == STATUS_INVALID_HANDLE) {
  5476. //
  5477. // window handle no longer exists, cleanup this entry
  5478. //
  5479. entry->Freed |= (PNP_UNREG_FREE|PNP_UNREG_DEFER|PNP_UNREG_WIN|PNP_UNREG_CANCEL);
  5480. DeleteNotifyEntry(entry,FALSE);
  5481. } else if (ntStatus == STATUS_UNSUCCESSFUL) {
  5482. KdPrintEx((DPFLTR_PNPMGR_ID,
  5483. DBGF_EVENT,
  5484. "UMPNPMGR: Window '%ws' timed out on cancel notification event\n",
  5485. entry->ClientName));
  5486. LogWarningEvent(WRN_CANCEL_NOTIFICATION_TIMED_OUT,
  5487. 1,
  5488. entry->ClientName);
  5489. }
  5490. }
  5491. }
  5492. break;
  5493. case DEVICE_NOTIFY_COMPLETION_HANDLE:
  5494. //
  5495. // NOTE: Completion handles not currently implemented.
  5496. //
  5497. break;
  5498. }
  5499. }
  5500. }
  5501. #ifdef _WIN64
  5502. //
  5503. // Free the 32-bit DEV_BROADCAST_HANDLE structure, if we allocated one.
  5504. //
  5505. if (HandleData32 != NULL) {
  5506. HeapFree(ghPnPHeap, 0, HandleData32);
  5507. }
  5508. #endif // _WIN64
  5509. return TRUE;
  5510. } // SendCancelNotification
  5511. VOID
  5512. BroadcastCompatibleDeviceMsg(
  5513. IN DWORD EventId,
  5514. IN PDEV_BROADCAST_DEVICEINTERFACE ClassData
  5515. )
  5516. /*++
  5517. Routine Description:
  5518. Deliver Win9x compatible event notification for the arrival and removal of
  5519. device interfaces to volume and port class devices.
  5520. Arguments:
  5521. EventId - Specifies the DBT style event id.
  5522. Currently, only DBT_DEVICEARRIVAL and DBT_DEVICEREMOVECOMPLETE
  5523. events are supported.
  5524. ClassData - Pointer to a PDEV_BROADCAST_DEVICEINTERFACE structure that is
  5525. already filled out with the pertinent data.
  5526. Currently, only volume and port class device interfaces are
  5527. supported.
  5528. (For volume class devices, the symbolic link
  5529. ClassData->dbcc_name is OPTIONAL - see Notes below.)
  5530. Return Value:
  5531. None.
  5532. Notes:
  5533. For volume class device broadcasts only, this routine may also be called
  5534. generically, with no symbolic link information provided. When no symbolic
  5535. link information to a volume device is supplied, the broadcast mask is
  5536. determined only from the current drive letter mappings and the global drive
  5537. letter mask (gAllDrivesMask) prior to this event. In this case, the global
  5538. drive letter mask is NOT updated here, and the caller should do so after
  5539. both the removal and arrival broadcasts in response to the name change are
  5540. performed. Currently, this type of call is only made from
  5541. BroadcastVolumeNameChange.
  5542. For volume class interface DBT_DEVICEREMOVECOMPLETE broadcasts, the drive
  5543. letter mask to be broadcast is always determined only by comparing drive
  5544. letters present prior to the remove of the interface with those present at
  5545. this time. This is done because the former mount points for this device are
  5546. no longer known when the interface removal event is received. Even so, it
  5547. is still necessary for the symbolic link corresponding to this interface to
  5548. be supplied to distinguish between the actual removal of the interface
  5549. (where the global drive letter mask is updated), the above case, where it is
  5550. not.
  5551. --*/
  5552. {
  5553. LONG status = ERROR_SUCCESS;
  5554. LONG result = 0;
  5555. DWORD recipients = BSM_APPLICATIONS | BSM_ALLDESKTOPS;
  5556. DWORD flags = BSF_IGNORECURRENTTASK | BSF_NOHANG;
  5557. //
  5558. // Validate the input event data.
  5559. //
  5560. if ((ClassData->dbcc_devicetype != DBT_DEVTYP_DEVICEINTERFACE) ||
  5561. (ClassData->dbcc_size < sizeof(DEV_BROADCAST_DEVICEINTERFACE))) {
  5562. status = ERROR_INVALID_DATA;
  5563. goto Clean0;
  5564. }
  5565. if ((EventId != DBT_DEVICEARRIVAL) &&
  5566. (EventId != DBT_DEVICEREMOVECOMPLETE)) {
  5567. //
  5568. // If the requested Event is not DBT_DEVICEARRIVAL or
  5569. // DBT_DEVICEREMOVECOMPLETE, don't broadcast any messages.
  5570. //
  5571. status = ERROR_NOT_SUPPORTED;
  5572. goto Clean0;
  5573. }
  5574. if (GuidEqual(&ClassData->dbcc_classguid, (LPGUID)&GUID_DEVINTERFACE_VOLUME)) {
  5575. //
  5576. // Volume class device interface events.
  5577. //
  5578. PDEV_BROADCAST_VOLUME pVolume;
  5579. DWORD broadcastmask = 0;
  5580. if (EventId == DBT_DEVICEARRIVAL) {
  5581. if (!ClassData->dbcc_name[0]) {
  5582. //
  5583. // If no symbolic link name was supplied, we were asked to
  5584. // broadcast volume device arrivals in response to a volume name
  5585. // change event. Broadcast any new drive letters found.
  5586. //
  5587. DWORD currentmask;
  5588. currentmask = GetAllVolumeMountPoints();
  5589. broadcastmask = (~gAllDrivesMask & currentmask);
  5590. } else {
  5591. //
  5592. // For volume class device interface arrival events, the volume
  5593. // device name is retrieved from the interface, and is compared to
  5594. // the volume names of all drive letter mountpoints in the system to
  5595. // determine the drive letter(s) corresponding to the arriving
  5596. // volume device interface.
  5597. //
  5598. LPWSTR devicePath, p;
  5599. WCHAR thisVolumeName[MAX_PATH];
  5600. WCHAR enumVolumeName[MAX_PATH];
  5601. WCHAR driveName[4];
  5602. ULONG length;
  5603. BOOL bResult;
  5604. //
  5605. // Allocate a temporary buffer for munging the symbolic link, with
  5606. // enough room for a trailing '\' char (should we need to add one),
  5607. // and the terminating NULL char.
  5608. //
  5609. length = lstrlen(ClassData->dbcc_name);
  5610. devicePath = HeapAlloc(ghPnPHeap, 0,
  5611. (length+1)*sizeof(WCHAR)+sizeof(UNICODE_NULL));
  5612. if (devicePath == NULL) {
  5613. status = ERROR_NOT_ENOUGH_MEMORY;
  5614. goto Clean0;
  5615. }
  5616. lstrcpyn(devicePath, ClassData->dbcc_name, length+1);
  5617. //
  5618. // Search for the occurence of a refstring (if any) by looking for the
  5619. // next occurance of a '\' char, after the initial "\\?\".
  5620. //
  5621. p = wcschr(&(devicePath[4]), TEXT('\\'));
  5622. if (!p) {
  5623. //
  5624. // No refstring is present in the symbolic link; add a trailing
  5625. // '\' char (as required by GetVolumeNameForVolumeMountPoint).
  5626. //
  5627. p = devicePath + length;
  5628. *p = TEXT('\\');
  5629. }
  5630. //
  5631. // If there is no refstring present, we have added a trailing '\',
  5632. // and placed p at that position. If a refstring is present, p is
  5633. // at the position of the '\' char that separates the munged device
  5634. // interface name, and the refstring; since we don't need the
  5635. // refstring to reach the parent interface key, we can use the next
  5636. // char for NULL terminating the string in both cases.
  5637. //
  5638. p++;
  5639. *p = UNICODE_NULL;
  5640. //
  5641. // Get the Volume Name for this Mount Point
  5642. //
  5643. thisVolumeName[0] = 0;
  5644. bResult = GetVolumeNameForVolumeMountPoint(devicePath,
  5645. thisVolumeName,
  5646. MAX_PATH);
  5647. HeapFree(ghPnPHeap, 0, devicePath);
  5648. if (!bResult || !thisVolumeName[0]) {
  5649. status = ERROR_BAD_PATHNAME;
  5650. goto Clean0;
  5651. }
  5652. //
  5653. // Initialize the drive name string
  5654. //
  5655. driveName[1] = TEXT(':');
  5656. driveName[2] = TEXT('\\');
  5657. driveName[3] = UNICODE_NULL;
  5658. //
  5659. // Find the drive letter mount point(s) for this volume device by
  5660. // enumerating all possible volume mount points and comparing each
  5661. // mounted volume name with the name of the volume corresponding to
  5662. // this device interface.
  5663. //
  5664. for (driveName[0] = TEXT('A'); driveName[0] <= TEXT('Z'); driveName[0]++) {
  5665. enumVolumeName[0] = UNICODE_NULL;
  5666. GetVolumeNameForVolumeMountPoint(driveName, enumVolumeName, MAX_PATH);
  5667. if (!wcscmp(thisVolumeName, enumVolumeName)) {
  5668. //
  5669. // Add the corresponding bit for this drive letter to the mask
  5670. //
  5671. broadcastmask |= (1 << (driveName[0] - TEXT('A')));
  5672. }
  5673. }
  5674. //
  5675. // Update the global drive letter mask of volume device mountpoints
  5676. //
  5677. gAllDrivesMask = GetAllVolumeMountPoints();
  5678. }
  5679. } else if (EventId == DBT_DEVICEREMOVECOMPLETE) {
  5680. //
  5681. // For volume class device interface removal events, the volume name
  5682. // (and hence, drive mountpoints) corresponding to this device
  5683. // interface has already been removed, and is no longer available.
  5684. // Instead, the bitmask of all drive letter mountpoints for current
  5685. // physical volumes is compared with that prior to the removal of
  5686. // this device. All missing drive mountpoints are assumed to have
  5687. // been associated with this volume device interface, and are
  5688. // subsequently broadcasted with this interface removal
  5689. // notification.
  5690. //
  5691. DWORD currentmask;
  5692. //
  5693. // Determine all current volume mount points, and broadcast any
  5694. // missing drive letters.
  5695. //
  5696. currentmask = GetAllVolumeMountPoints();
  5697. broadcastmask = (gAllDrivesMask & ~currentmask);
  5698. if (ClassData->dbcc_name[0]) {
  5699. //
  5700. // Only update the global drive letter in response to the
  5701. // removal of a interface. For volume name changes, we update
  5702. // outside of this routine.
  5703. //
  5704. gAllDrivesMask = currentmask;
  5705. }
  5706. }
  5707. //
  5708. // If there is nothing to broadcast, then we're done.
  5709. //
  5710. if (!broadcastmask) {
  5711. status = ERROR_SUCCESS;
  5712. goto Clean0;
  5713. }
  5714. //
  5715. // Fill out the volume broadcast structure.
  5716. //
  5717. pVolume = HeapAlloc(ghPnPHeap, 0,
  5718. sizeof(DEV_BROADCAST_VOLUME));
  5719. if (pVolume == NULL) {
  5720. status = ERROR_NOT_ENOUGH_MEMORY;
  5721. goto Clean0;
  5722. }
  5723. pVolume->dbcv_size = sizeof(DEV_BROADCAST_VOLUME);
  5724. pVolume->dbcv_devicetype = DBT_DEVTYP_VOLUME;
  5725. pVolume->dbcv_flags = 0;
  5726. pVolume->dbcv_reserved = 0;
  5727. pVolume->dbcv_unitmask = broadcastmask;
  5728. //
  5729. // Broadcast the message to all components
  5730. //
  5731. result = BroadcastSystemMessage(flags,
  5732. &recipients,
  5733. WM_DEVICECHANGE,
  5734. EventId,
  5735. (LPARAM)pVolume);
  5736. if (fpWinStationBroadcastSystemMessage) {
  5737. try {
  5738. fpWinStationBroadcastSystemMessage(SERVERNAME_CURRENT,
  5739. TRUE,
  5740. 0,
  5741. DEFAULT_BROADCAST_TIME_OUT,
  5742. flags,
  5743. &recipients,
  5744. WM_DEVICECHANGE,
  5745. (WPARAM)EventId,
  5746. (LPARAM)pVolume,
  5747. &result);
  5748. } except (EXCEPTION_EXECUTE_HANDLER) {
  5749. KdPrintEx((DPFLTR_PNPMGR_ID,
  5750. DBGF_ERRORS,
  5751. "UMPNPMGR: Exception calling WinStationBroadcastSystemMessage!\n"));
  5752. ASSERT(0);
  5753. }
  5754. }
  5755. //
  5756. // Free the broadcast structure.
  5757. //
  5758. HeapFree(ghPnPHeap, 0, pVolume);
  5759. } else if ((GuidEqual(&ClassData->dbcc_classguid, (LPGUID)&GUID_DEVINTERFACE_PARALLEL)) ||
  5760. (GuidEqual(&ClassData->dbcc_classguid, (LPGUID)&GUID_DEVINTERFACE_COMPORT))) {
  5761. //
  5762. // COM and LPT port class device interface events.
  5763. //
  5764. PDEV_BROADCAST_PORT pPort;
  5765. LPWSTR p;
  5766. LPWSTR deviceInterfacePath;
  5767. LPWSTR deviceInterfaceName;
  5768. LPWSTR classGuidString;
  5769. LPWSTR deviceInstance;
  5770. HKEY hKey;
  5771. WCHAR szPortName[MAX_PATH];
  5772. ULONG length, ulSize;
  5773. //
  5774. // Build the complete path to the device interface key for this device.
  5775. //
  5776. length = lstrlen(ClassData->dbcc_name);
  5777. ASSERT(length);
  5778. if (!length) {
  5779. status = ERROR_INVALID_PARAMETER;
  5780. goto Clean0;
  5781. }
  5782. deviceInterfacePath = HeapAlloc(ghPnPHeap, 0,
  5783. (lstrlen(pszRegPathDeviceClasses)+1)*sizeof(WCHAR) +
  5784. MAX_GUID_STRING_LEN * sizeof(WCHAR) +
  5785. length * sizeof(WCHAR) +
  5786. sizeof(UNICODE_NULL));
  5787. if (deviceInterfacePath == NULL) {
  5788. status = ERROR_NOT_ENOUGH_MEMORY;
  5789. goto Clean0;
  5790. }
  5791. //
  5792. // Place the path to the "DeviceClasses" registry key at the beginning.
  5793. //
  5794. lstrcpyn(deviceInterfacePath, pszRegPathDeviceClasses,
  5795. lstrlen(pszRegPathDeviceClasses) + 1);
  5796. //
  5797. // Determine the appropriate places in the device interface path for the
  5798. // class GUID and interface names.
  5799. //
  5800. classGuidString = deviceInterfacePath + lstrlen(pszRegPathDeviceClasses) + 1;
  5801. deviceInterfaceName = classGuidString + MAX_GUID_STRING_LEN;
  5802. //
  5803. // Copy the symbolic link name to the device interface position in the
  5804. // path for munging.
  5805. //
  5806. lstrcpyn(deviceInterfaceName, ClassData->dbcc_name, length + 1);
  5807. //
  5808. // Search for the begininng of the refstring (if any, by looking for the
  5809. // next occurance of a '\' char, after the initial "\\?\" in the interface name
  5810. //
  5811. p = wcschr(&(deviceInterfaceName[4]), TEXT('\\'));
  5812. if (!p) {
  5813. //
  5814. // This name has no refstring--set the pointer to the end of the string
  5815. //
  5816. p = deviceInterfaceName + length;
  5817. } else {
  5818. //
  5819. // Separate the refString Component from the deviceInterfaceName with a NULL char
  5820. //
  5821. *p = UNICODE_NULL;
  5822. }
  5823. //
  5824. // Retrieve the interface class of this device. Since the device path is of
  5825. // the form "\\?\MungedDevInstName#{InterfaceClassGuid}[\RefString]", we can
  5826. // copy the class GUID directly from the interface name, instead of
  5827. // having to convert the given dbcc_classguid.
  5828. //
  5829. // NOTE: The algorithm about how this name is parsed must be kept in
  5830. // sync with other such kernel-mode and user-mode implementations of how
  5831. // this name is generated and parsed.
  5832. //
  5833. if (p < (deviceInterfaceName + 3 + MAX_GUID_STRING_LEN)) {
  5834. //
  5835. // There is not enough room for a GUID to be present in this device
  5836. // interface path.
  5837. //
  5838. status = ERROR_BAD_PATHNAME;
  5839. HeapFree(ghPnPHeap, 0, deviceInterfacePath);
  5840. goto Clean0;
  5841. }
  5842. //
  5843. // Place the class GUID at the appropriate place in the path.
  5844. //
  5845. lstrcpyn(classGuidString, p - (MAX_GUID_STRING_LEN-1), MAX_GUID_STRING_LEN);
  5846. //
  5847. // Place the path seperator characters at the appropriate places.
  5848. //
  5849. *(classGuidString-1) = TEXT('\\');
  5850. *(deviceInterfaceName-1) = TEXT('\\');
  5851. //
  5852. // Munge the symbolic link name to form the interface key name.
  5853. // (Note: The munging process is optimized here; we only have to munge
  5854. // the leading "\\?\" segment since the rest of the given symbolic link
  5855. // is alrady munged, with the exception of the refstring seperator char,
  5856. // if any, which we have just eliminated from the interface name, above.)
  5857. //
  5858. deviceInterfaceName[0] = TEXT('#');
  5859. deviceInterfaceName[1] = TEXT('#');
  5860. deviceInterfaceName[3] = TEXT('#');
  5861. //
  5862. // Open the device interface key
  5863. //
  5864. status = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  5865. deviceInterfacePath,
  5866. 0,
  5867. KEY_READ,
  5868. &hKey);
  5869. HeapFree(ghPnPHeap, 0, deviceInterfacePath);
  5870. if (status != ERROR_SUCCESS) {
  5871. hKey=NULL;
  5872. goto Clean0;
  5873. }
  5874. //
  5875. // Determine the size of the DeviceInstance value entry
  5876. //
  5877. status = RegQueryValueEx(hKey,
  5878. pszRegValueDeviceInstance,
  5879. 0,
  5880. NULL,
  5881. NULL,
  5882. &ulSize);
  5883. if (status != ERROR_SUCCESS) {
  5884. RegCloseKey(hKey);
  5885. hKey = NULL;
  5886. goto Clean0;
  5887. }
  5888. //
  5889. // Allocate a string large enough to store the path from the Enum key,
  5890. // to the "\Device Parameters" subkey of this Device Instance's registry
  5891. // key.
  5892. //
  5893. deviceInstance = HeapAlloc(ghPnPHeap, 0,
  5894. ulSize + sizeof(WCHAR) +
  5895. lstrlen(pszRegKeyDeviceParam)*sizeof(WCHAR));
  5896. if (deviceInstance == NULL) {
  5897. status = ERROR_NOT_ENOUGH_MEMORY;
  5898. RegCloseKey(hKey);
  5899. hKey = NULL;
  5900. goto Clean0;
  5901. }
  5902. //
  5903. // Retrieve the device instance that owns this interface.
  5904. //
  5905. status = RegQueryValueEx(hKey,
  5906. pszRegValueDeviceInstance,
  5907. 0,
  5908. NULL,
  5909. (LPBYTE)deviceInstance,
  5910. &ulSize);
  5911. RegCloseKey(hKey);
  5912. hKey=NULL;
  5913. if (status != ERROR_SUCCESS) {
  5914. HeapFree(ghPnPHeap, 0, deviceInstance);
  5915. goto Clean0;
  5916. }
  5917. //
  5918. // Open the "Device Parameters" key under the HKLM\SYSTEM\CCS\Enum
  5919. // subkey for this DeviceInstance.
  5920. //
  5921. p = deviceInstance + (ulSize - sizeof(UNICODE_NULL))/sizeof(WCHAR);
  5922. wsprintf(p, TEXT("\\%s"),
  5923. pszRegKeyDeviceParam);
  5924. status = RegOpenKeyEx(ghEnumKey,
  5925. deviceInstance,
  5926. 0,
  5927. KEY_READ,
  5928. &hKey);
  5929. HeapFree(ghPnPHeap, 0, deviceInstance);
  5930. if (status != ERROR_SUCCESS) {
  5931. goto Clean0;
  5932. }
  5933. //
  5934. // Query the "PortName" value for the compatible name of this device.
  5935. //
  5936. ulSize = MAX_PATH*sizeof(WCHAR);
  5937. status = RegQueryValueEx(hKey,
  5938. pszRegValuePortName,
  5939. 0,
  5940. NULL,
  5941. (LPBYTE)szPortName,
  5942. &ulSize);
  5943. RegCloseKey(hKey);
  5944. if (status != ERROR_SUCCESS) {
  5945. goto Clean0;
  5946. }
  5947. //
  5948. // Fill out the port broadcast structure.
  5949. //
  5950. pPort = HeapAlloc (ghPnPHeap, 0,
  5951. sizeof(DEV_BROADCAST_PORT) + ulSize);
  5952. if (pPort == NULL) {
  5953. status = ERROR_NOT_ENOUGH_MEMORY;
  5954. goto Clean0;
  5955. }
  5956. pPort->dbcp_size = sizeof(DEV_BROADCAST_PORT) + ulSize;
  5957. pPort->dbcp_devicetype = DBT_DEVTYP_PORT;
  5958. pPort->dbcp_reserved = 0;
  5959. wsprintf(pPort->dbcp_name, szPortName);
  5960. //
  5961. // Broadcast the message to all components
  5962. //
  5963. result = BroadcastSystemMessage(flags,
  5964. &recipients,
  5965. WM_DEVICECHANGE,
  5966. EventId,
  5967. (LPARAM)pPort);
  5968. if (fpWinStationBroadcastSystemMessage) {
  5969. try {
  5970. fpWinStationBroadcastSystemMessage(SERVERNAME_CURRENT,
  5971. TRUE,
  5972. 0,
  5973. DEFAULT_BROADCAST_TIME_OUT,
  5974. flags,
  5975. &recipients,
  5976. WM_DEVICECHANGE,
  5977. (WPARAM)EventId,
  5978. (LPARAM)pPort,
  5979. &result);
  5980. } except (EXCEPTION_EXECUTE_HANDLER) {
  5981. KdPrintEx((DPFLTR_PNPMGR_ID,
  5982. DBGF_ERRORS,
  5983. "UMPNPMGR: Exception calling WinStationBroadcastSystemMessage!\n"));
  5984. ASSERT(0);
  5985. }
  5986. }
  5987. //
  5988. // Free the broadcast structure.
  5989. //
  5990. HeapFree(ghPnPHeap, 0, pPort);
  5991. }
  5992. Clean0:
  5993. return;
  5994. } // BroadcastCompatibleDeviceMsg
  5995. VOID
  5996. BroadcastVolumeNameChange(
  5997. VOID
  5998. )
  5999. /*++
  6000. Routine Description:
  6001. Perform Win9x compatible volume removal and arrival messages, to be called
  6002. in reponse to a volume name change event.
  6003. Arguments:
  6004. None.
  6005. Return Value:
  6006. None.
  6007. Notes:
  6008. The drive mask to be broadcast will be determined by comparing the current
  6009. drive letter mask with that prior to the event. The global drive letter
  6010. mask is also updated here, after all removal and arrival notifications have
  6011. been sent.
  6012. --*/
  6013. {
  6014. DEV_BROADCAST_DEVICEINTERFACE volumeNotify;
  6015. //
  6016. // Fill out a DEV_BROADCAST_DEVICEINTERFACE structure.
  6017. //
  6018. ZeroMemory(&volumeNotify, sizeof(DEV_BROADCAST_DEVICEINTERFACE));
  6019. volumeNotify.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
  6020. volumeNotify.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
  6021. volumeNotify.dbcc_reserved = 0;
  6022. memcpy(&volumeNotify.dbcc_classguid, &GUID_DEVINTERFACE_VOLUME, sizeof(GUID));
  6023. //
  6024. // A null symbolic link name for dbcc_name designates that
  6025. // BroadcastCompatibleDeviceMsg is to determine the drive mask to
  6026. // broadcast by checking differences between the last broadcast drive
  6027. // mask (gAllDrivesMask), and the current drive mask.
  6028. //
  6029. // When broadcasting in response to a volume name change, we must wait until
  6030. // both removal and arrival messages have been sent before we can update the
  6031. // global drive letter mask. A null symbolic link name specifies that
  6032. // BroadcastCompatibleDeviceMsg should not update the global mask; this will
  6033. // be done here, after all broadcasts are complete.
  6034. //
  6035. volumeNotify.dbcc_name[0] = L'\0';
  6036. //
  6037. // Broadcast volume removal notification for any drive letter moint points
  6038. // no longer in use, followed by volume arrival notification for new
  6039. //
  6040. BroadcastCompatibleDeviceMsg(DBT_DEVICEREMOVECOMPLETE, &volumeNotify);
  6041. BroadcastCompatibleDeviceMsg(DBT_DEVICEARRIVAL, &volumeNotify);
  6042. //
  6043. // Now that both removal and arrival messages have been sent, update the
  6044. // global drive letter mask to reflect what we just broadcast.
  6045. //
  6046. gAllDrivesMask = GetAllVolumeMountPoints();
  6047. return;
  6048. } // BroadcastVolumeNameChange
  6049. DWORD
  6050. GetAllVolumeMountPoints(
  6051. VOID
  6052. )
  6053. /*++
  6054. Routine Description:
  6055. Queries all drive letter mountpoints ('A'-'Z') and returns a bitmask
  6056. representing all such mount points currently in use by physical volume
  6057. devices.
  6058. Arguments:
  6059. None.
  6060. Return Value:
  6061. Returns a bit mask representing drive letter mount points ('A'-'Z') in use
  6062. by physical volume devices.
  6063. Note:
  6064. The returned bit mask includes only mount points for physical volume class
  6065. devices. Network mounted drives are not included.
  6066. --*/
  6067. {
  6068. WCHAR driveName[4];
  6069. WCHAR volumeName[MAX_PATH];
  6070. DWORD driveLetterMask=0;
  6071. //
  6072. // Initialize drive name and mask
  6073. //
  6074. driveName[1] = TEXT(':');
  6075. driveName[2] = TEXT('\\');
  6076. driveName[3] = UNICODE_NULL;
  6077. //
  6078. // Compare the name of this volume with those of all mounted volumes in the system
  6079. //
  6080. for (driveName[0] = TEXT('A'); driveName[0] <= TEXT('Z'); driveName[0]++) {
  6081. volumeName[0] = UNICODE_NULL;
  6082. if (!GetVolumeNameForVolumeMountPoint(driveName,
  6083. volumeName,
  6084. MAX_PATH)) {
  6085. continue;
  6086. }
  6087. if (volumeName[0] != UNICODE_NULL) {
  6088. //
  6089. // Add the corresponding bit for this drive letter to the mask
  6090. //
  6091. driveLetterMask |= (1 << (driveName[0] - TEXT('A')));
  6092. }
  6093. }
  6094. return driveLetterMask;
  6095. } // GetAllVolumeMountPoints
  6096. ULONG
  6097. NotifyPower(
  6098. IN DWORD ServiceControl,
  6099. IN DWORD EventId,
  6100. IN DWORD EventData,
  6101. IN DWORD Flags,
  6102. OUT PPNP_VETO_TYPE VetoType OPTIONAL,
  6103. OUT LPWSTR VetoName OPTIONAL,
  6104. IN OUT PULONG VetoNameLength OPTIONAL
  6105. )
  6106. /*++
  6107. Routine Description:
  6108. This routine notifies services of system-wide power events.
  6109. Arguments:
  6110. ServiceControl - Specifies class of service event (power, device, hwprofile
  6111. change).
  6112. EventId - Specifies the PBT style event id for the power event.
  6113. (see sdk\inc\pbt.h for defined power events)
  6114. EventData - Specifies additional data for the event.
  6115. Flags - Specifies BroadcastSystemMessage BSF_ flags.
  6116. VetoType - For query-type events, supplies the address of a variable to
  6117. receive, upon failure, the type of the component responsible
  6118. for vetoing the request.
  6119. VetoName - For query-type events, supplies the address of a variable to
  6120. receive, upon failure, the name of the component
  6121. responsible for vetoing the request.
  6122. VetoNameLength - For query-type events, supplies the address of a variable
  6123. specifying the size of the of buffer specified by the
  6124. VetoName parameter. Upon failure, this address will specify
  6125. the length of the string stored in that buffer by this
  6126. routine.
  6127. Return Value:
  6128. Returns FALSE in the case of a vetoed query event, TRUE otherwise.
  6129. Notes:
  6130. This routine currently only notifies services of power events. Notification
  6131. to windows is handled directly by USER.
  6132. Power events are placed in the plug and play event queue via a private call
  6133. from USER, for the explicit purpose of notifying services of system-wide
  6134. power events, done here.
  6135. --*/
  6136. {
  6137. NTSTATUS status=STATUS_SUCCESS;
  6138. PPNP_NOTIFY_ENTRY entry, nextEntry;
  6139. PPNP_NOTIFY_LIST notifyList;
  6140. BOOL bLocked = FALSE;
  6141. DWORD err;
  6142. LONG result;
  6143. //
  6144. // NOTE: Services are not currently sent EventData for power events. The
  6145. // SCM currently ASSERTs that this will always be zero.
  6146. //
  6147. // The SDK states that WM_POWERBROADCAST "RESUME" type messages may contain
  6148. // the PBTF_APMRESUMEFROMFAILURE flag in the LPARAM field, and that "QUERY"
  6149. // type messages may contain a single bit in the LPARAM field specifying
  6150. // whether user interaction is allowed.
  6151. //
  6152. // Although these don't currently seem to be used much (even for window
  6153. // messages, as stated), shouldn't EventData also be valid for service power
  6154. // event notification?
  6155. //
  6156. UNREFERENCED_PARAMETER(EventData);
  6157. //
  6158. // If we're doing a query, then VetoType, VetoName, and VetoNameLength must
  6159. // all be specified.
  6160. //
  6161. ASSERT(!(Flags & BSF_QUERY) || (VetoType && VetoName && VetoNameLength));
  6162. if (!(Flags & BSF_QUERY) && (VetoNameLength != NULL)) {
  6163. //
  6164. // Not vetoable.
  6165. //
  6166. *VetoNameLength = 0;
  6167. }
  6168. notifyList = &ServiceList[CINDEX_POWEREVENT];
  6169. LockNotifyList (&notifyList->Lock);
  6170. bLocked = TRUE;
  6171. //
  6172. //Services only. User sends out messages to apps
  6173. //
  6174. try {
  6175. //
  6176. //Notify the services
  6177. //
  6178. entry = GetFirstNotifyEntry(notifyList,0);
  6179. if (!entry) {
  6180. //
  6181. // can't veto if no one registered.
  6182. //
  6183. if (VetoNameLength != NULL) {
  6184. *VetoNameLength = 0;
  6185. }
  6186. }
  6187. while (entry) {
  6188. nextEntry = GetNextNotifyEntry(entry,0);
  6189. if (entry->Unregistered) {
  6190. entry = nextEntry;
  6191. continue;
  6192. }
  6193. //
  6194. // This is a direct call, not a message via. USER
  6195. //
  6196. if (pServiceControlCallback) {
  6197. UnlockNotifyList (&notifyList->Lock);
  6198. bLocked = FALSE;
  6199. try {
  6200. (pServiceControlCallback)((SERVICE_STATUS_HANDLE)entry->Handle,
  6201. ServiceControl,
  6202. EventId,
  6203. (LPARAM)NULL, // Currently, no EventData allowed for services
  6204. &err);
  6205. } except (EXCEPTION_EXECUTE_HANDLER) {
  6206. KdPrintEx((DPFLTR_PNPMGR_ID,
  6207. DBGF_ERRORS,
  6208. "UMPNPMGR: Exception calling Service Control Manager!\n"));
  6209. ASSERT(0);
  6210. }
  6211. LockNotifyList (&notifyList->Lock);
  6212. bLocked = TRUE;
  6213. //
  6214. // convert Win32 error into window message-style return
  6215. // value.
  6216. //
  6217. if (err == NO_ERROR) {
  6218. result = TRUE;
  6219. } else {
  6220. KdPrintEx((DPFLTR_PNPMGR_ID,
  6221. DBGF_EVENT,
  6222. "UMPNPMGR: Service %ws responded to PowerEvent, with status=0x%08lx\n",
  6223. entry->ClientName,
  6224. err));
  6225. //
  6226. // This service specifically requested to receive this
  6227. // notification - it should know how to handle it.
  6228. //
  6229. ASSERT(err != ERROR_CALL_NOT_IMPLEMENTED);
  6230. //
  6231. // Log the error the service used to veto.
  6232. //
  6233. LogWarningEvent(WRN_POWER_EVENT_SERVICE_VETO,
  6234. 1,
  6235. entry->ClientName);
  6236. result = BROADCAST_QUERY_DENY;
  6237. }
  6238. //
  6239. // Check if one of the QUERY messages was denied
  6240. //
  6241. if ((Flags & BSF_QUERY) &&
  6242. (result == BROADCAST_QUERY_DENY)) {
  6243. ServiceVeto(entry, VetoType, VetoName, VetoNameLength );
  6244. //
  6245. // This service vetoed the query, tell everyone else
  6246. // it was cancelled.
  6247. //
  6248. SendCancelNotification(entry,
  6249. ServiceControl,
  6250. EventId,
  6251. 0,
  6252. NULL,
  6253. NULL);
  6254. status = STATUS_UNSUCCESSFUL;
  6255. break;
  6256. }
  6257. }
  6258. entry = nextEntry;
  6259. }
  6260. } except (EXCEPTION_EXECUTE_HANDLER){
  6261. KdPrintEx((DPFLTR_PNPMGR_ID,
  6262. DBGF_ERRORS,
  6263. "UMPNPMGR: Exception delivering Power Notification to Service Control Manager\n"));
  6264. ASSERT(0);
  6265. }
  6266. if (bLocked) {
  6267. UnlockNotifyList (&notifyList->Lock);
  6268. }
  6269. //
  6270. // if successful, we are not returning veto info.
  6271. //
  6272. if (NT_SUCCESS(status) && (VetoNameLength != NULL)) {
  6273. *VetoNameLength = 0;
  6274. }
  6275. return (NT_SUCCESS(status));
  6276. } // NotifyPower
  6277. CONFIGRET
  6278. RegisterServiceNotification(
  6279. IN SERVICE_STATUS_HANDLE hService,
  6280. IN LPWSTR pszService,
  6281. IN DWORD scmControls,
  6282. IN BOOL bServiceStopped
  6283. )
  6284. /*++
  6285. Routine Description:
  6286. This routine is called directly and privately by the service controller.
  6287. It allows the SCM to register or unregister services for events sent by this
  6288. service.
  6289. Arguments:
  6290. hService - Specifies the service handle.
  6291. pszService - Specifies the name of the service.
  6292. scmControls - Specifies the messages that SCM wants to listen to.
  6293. bServiceStopped - Specifies whether the service is stopped.
  6294. Return Value:
  6295. Return CR_SUCCESS if the function succeeds, otherwise it returns one
  6296. of the CR_* errors.
  6297. Notes:
  6298. This routine is called anytime a service changes the state of the
  6299. SERVICE_ACCEPT_POWEREVENT or SERVICE_ACCEPT_HARDWAREPROFILECHANGE flags in
  6300. its list of accepted controls.
  6301. This routine is also called by the SCM whenever any service has stopped, to
  6302. make sure that the specified service status handle is no longer registered
  6303. to receive SERVICE_CONTROL_DEVICEEVENT events.
  6304. Although it is the responsibility of the service to unregister for any
  6305. device event notifications that it has registered to receive before it is
  6306. stopped, its service status handle may be reused by the service controller,
  6307. so we must clean up any remaining device event registrations so that other
  6308. services will not receive them instead.
  6309. This is necessary for shared process services, since RPC rundown on the
  6310. notification handle will not occur until the service's process exits, which
  6311. may be long after the service has stopped.
  6312. --*/
  6313. {
  6314. ULONG cBits, i=0, lenName=0;
  6315. CONFIGRET Status = CR_SUCCESS;
  6316. PPNP_NOTIFY_ENTRY entry = NULL, curentry, nextentry;
  6317. PLOCKINFO LockHeld = NULL;
  6318. //
  6319. // Filter out the accepted controls we care about.
  6320. //
  6321. cBits = MapSCMControlsToControlBit(scmControls);
  6322. //
  6323. // If we were called because the service was stopped, make sure that we
  6324. // always unregister for all notifications.
  6325. //
  6326. if (bServiceStopped) {
  6327. ASSERT(cBits == 0);
  6328. cBits = 0;
  6329. }
  6330. try {
  6331. EnterCriticalSection(&RegistrationCS);
  6332. //
  6333. // Add or remove an entry in the array for each control bits.
  6334. //
  6335. for (i = 0;i< SERVICE_NUM_CONTROLS;i++) {
  6336. if (LockNotifyList(&ServiceList[i].Lock)) {
  6337. LockHeld = &ServiceList[i].Lock;
  6338. } else {
  6339. //
  6340. // Couldn't acquire the lock. Just move on to the next control
  6341. // bit.
  6342. //
  6343. continue;
  6344. }
  6345. //
  6346. // Check to see if an entry for this service handle already exists
  6347. // in our list.
  6348. //
  6349. for (curentry = GetFirstNotifyEntry(&ServiceList[i],0);
  6350. curentry;
  6351. curentry = GetNextNotifyEntry(curentry,0)) {
  6352. if (curentry->Handle == (HANDLE)hService) {
  6353. break;
  6354. }
  6355. }
  6356. //
  6357. // At this point, if curentry is non-NULL, then the service
  6358. // handle is already in our list, otherwise, it is not.
  6359. //
  6360. if (cBits & (1 << i)) {
  6361. //
  6362. // If entry isn't already in the list, then add it.
  6363. //
  6364. if (!curentry) {
  6365. entry = HeapAlloc(ghPnPHeap, 0, sizeof(PNP_NOTIFY_ENTRY));
  6366. if (NULL == entry) {
  6367. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  6368. Status = CR_OUT_OF_MEMORY;
  6369. UnlockNotifyList(LockHeld);
  6370. LockHeld = NULL;
  6371. goto Clean0;
  6372. }
  6373. RtlZeroMemory (entry,sizeof (PNP_NOTIFY_ENTRY));
  6374. entry->Handle = (HANDLE)hService;
  6375. entry->Signature = SERVICE_ENTRY_SIGNATURE;
  6376. entry->Freed = 0;
  6377. entry->Flags = DEVICE_NOTIFY_SERVICE_HANDLE;
  6378. entry->ClientName = NULL;
  6379. if (pszService) {
  6380. lenName = lstrlen(pszService);
  6381. entry->ClientName = HeapAlloc(ghPnPHeap,
  6382. 0,
  6383. (lenName+1)*sizeof(WCHAR));
  6384. if (entry->ClientName == NULL) {
  6385. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  6386. Status = CR_OUT_OF_MEMORY;
  6387. HeapFree(ghPnPHeap,0,entry);
  6388. UnlockNotifyList(LockHeld);
  6389. LockHeld = NULL;
  6390. goto Clean0;
  6391. }
  6392. lstrcpy(entry->ClientName, pszService);
  6393. }
  6394. entry->u.Service.scmControls = scmControls;
  6395. MarkEntryWithList(entry,i);
  6396. AddNotifyEntry(&ServiceList[i], entry);
  6397. //
  6398. // Now reset entry pointer to NULL so we won't try to free
  6399. // it if we encounter an exception
  6400. //
  6401. entry = NULL;
  6402. }
  6403. } else {
  6404. //
  6405. // If entry is in the list, then remove it.
  6406. //
  6407. if (curentry) {
  6408. curentry->Freed |= (PNP_UNREG_FREE|PNP_UNREG_DEFER|PNP_UNREG_SERVICE);
  6409. DeleteNotifyEntry(curentry,TRUE);
  6410. }
  6411. }
  6412. UnlockNotifyList(LockHeld);
  6413. LockHeld = NULL;
  6414. }
  6415. //
  6416. // If the service is being stopped, unregister all outstanding device
  6417. // event registrations.
  6418. //
  6419. if (bServiceStopped) {
  6420. //
  6421. // If a notification is currently in progress, check to see if there
  6422. // are any entries for this service in the deferred RegisterList or
  6423. // UnregisterList.
  6424. //
  6425. if (gNotificationInProg != 0) {
  6426. if (RegisterList) {
  6427. PPNP_DEFERRED_LIST currReg, prevReg;
  6428. currReg = RegisterList;
  6429. prevReg = NULL;
  6430. while (currReg) {
  6431. ASSERT(currReg->Entry->Unregistered);
  6432. if (currReg->Entry->Handle == (HANDLE)hService) {
  6433. if (prevReg) {
  6434. prevReg->Next = currReg->Next;
  6435. } else {
  6436. RegisterList = currReg->Next;
  6437. }
  6438. HeapFree(ghPnPHeap, 0, currReg);
  6439. if (prevReg) {
  6440. currReg = prevReg->Next;
  6441. } else {
  6442. currReg = RegisterList;
  6443. }
  6444. } else {
  6445. prevReg = currReg;
  6446. currReg = currReg->Next;
  6447. }
  6448. }
  6449. }
  6450. if (UnregisterList) {
  6451. PPNP_DEFERRED_LIST currUnreg, prevUnreg;
  6452. currUnreg = UnregisterList;
  6453. prevUnreg = NULL;
  6454. while (currUnreg) {
  6455. ASSERT(currUnreg->Entry->Unregistered);
  6456. if (currUnreg->Entry->Handle == (HANDLE)hService) {
  6457. if (prevUnreg) {
  6458. prevUnreg->Next = currUnreg->Next;
  6459. } else {
  6460. UnregisterList = currUnreg->Next;
  6461. }
  6462. HeapFree(ghPnPHeap, 0, currUnreg);
  6463. if (prevUnreg) {
  6464. currUnreg = prevUnreg->Next;
  6465. } else {
  6466. currUnreg = UnregisterList;
  6467. }
  6468. } else {
  6469. prevUnreg = currUnreg;
  6470. currUnreg = currUnreg->Next;
  6471. }
  6472. }
  6473. }
  6474. }
  6475. //
  6476. // Check for any target device notification entries for this
  6477. // service.
  6478. //
  6479. for (i = 0; i < TARGET_HASH_BUCKETS; i++) {
  6480. if (LockNotifyList(&TargetList[i].Lock)) {
  6481. LockHeld = &TargetList[i].Lock;
  6482. } else {
  6483. //
  6484. // Couldn't acquire the lock. Just move on to the next list.
  6485. //
  6486. continue;
  6487. }
  6488. //
  6489. // Check to see if an entry for this service handle exists in
  6490. // this list.
  6491. //
  6492. curentry = GetFirstNotifyEntry(&TargetList[i],0);
  6493. while(curentry) {
  6494. nextentry = GetNextNotifyEntry(curentry,0);
  6495. if (curentry->Unregistered) {
  6496. curentry = nextentry;
  6497. continue;
  6498. }
  6499. if (curentry->Handle == (HANDLE)hService) {
  6500. //
  6501. // Remove the entry from the notification list.
  6502. //
  6503. curentry->Unregistered = TRUE;
  6504. curentry->Freed |= (PNP_UNREG_FREE|PNP_UNREG_TARGET);
  6505. DeleteNotifyEntry(curentry,FALSE);
  6506. //
  6507. // Only log a warning if the PlugPlay service has not
  6508. // already stopped. Otherwise, the client may actually
  6509. // have tried to unregister after we were shut down.
  6510. //
  6511. if (CurrentServiceState != SERVICE_STOPPED &&
  6512. CurrentServiceState != SERVICE_STOP_PENDING) {
  6513. KdPrintEx((DPFLTR_PNPMGR_ID,
  6514. DBGF_WARNINGS | DBGF_EVENT,
  6515. "UMPNPMGR: Service '%ws' may have stopped without unregistering for TargetDeviceChange notification.\n",
  6516. curentry->ClientName));
  6517. LogWarningEvent(WRN_STOPPED_SERVICE_REGISTERED,
  6518. 1,
  6519. curentry->ClientName);
  6520. }
  6521. }
  6522. curentry = nextentry;
  6523. }
  6524. UnlockNotifyList(LockHeld);
  6525. LockHeld = NULL;
  6526. }
  6527. //
  6528. // Check for any device interface notification entries for this
  6529. // service.
  6530. //
  6531. for (i = 0; i < CLASS_GUID_HASH_BUCKETS; i++) {
  6532. if (LockNotifyList(&ClassList[i].Lock)) {
  6533. LockHeld = &ClassList[i].Lock;
  6534. } else {
  6535. //
  6536. // Couldn't acquire the lock. Just move on to the next list.
  6537. //
  6538. continue;
  6539. }
  6540. //
  6541. // Check to see if an entry for this service handle exists in
  6542. // this list.
  6543. //
  6544. curentry = GetFirstNotifyEntry(&ClassList[i],0);
  6545. while(curentry) {
  6546. nextentry = GetNextNotifyEntry(curentry,0);
  6547. if (curentry->Unregistered) {
  6548. curentry = nextentry;
  6549. continue;
  6550. }
  6551. if (curentry->Handle == (HANDLE)hService) {
  6552. //
  6553. // Remove the entry from the notification list.
  6554. //
  6555. curentry->Unregistered = TRUE;
  6556. curentry->Freed |= (PNP_UNREG_FREE|PNP_UNREG_CLASS);
  6557. DeleteNotifyEntry(curentry,FALSE);
  6558. //
  6559. // Only log a warning if the PlugPlay service has not
  6560. // already stopped. Otherwise, the client may actually
  6561. // have tried to unregister after we were shut down.
  6562. //
  6563. if (CurrentServiceState != SERVICE_STOPPED &&
  6564. CurrentServiceState != SERVICE_STOP_PENDING) {
  6565. KdPrintEx((DPFLTR_PNPMGR_ID,
  6566. DBGF_WARNINGS | DBGF_EVENT,
  6567. "UMPNPMGR: Service '%ws' may have stopped without unregistering for DeviceInterfaceChange notification.\n",
  6568. curentry->ClientName));
  6569. LogWarningEvent(WRN_STOPPED_SERVICE_REGISTERED,
  6570. 1,
  6571. curentry->ClientName);
  6572. }
  6573. }
  6574. curentry = nextentry;
  6575. }
  6576. UnlockNotifyList(LockHeld);
  6577. LockHeld = NULL;
  6578. }
  6579. }
  6580. Clean0:
  6581. LeaveCriticalSection(&RegistrationCS);
  6582. } except (EXCEPTION_EXECUTE_HANDLER){
  6583. KdPrintEx((DPFLTR_PNPMGR_ID,
  6584. DBGF_ERRORS,
  6585. "UMPNPMGR: Exception in RegisterServiceNotification!!\n"));
  6586. ASSERT(0);
  6587. SetLastError(ERROR_EXCEPTION_IN_SERVICE);
  6588. Status = CR_FAILURE;
  6589. if (LockHeld) {
  6590. UnlockNotifyList(LockHeld);
  6591. }
  6592. LeaveCriticalSection(&RegistrationCS);
  6593. if (entry) {
  6594. if (entry->ClientName) {
  6595. HeapFree(ghPnPHeap, 0, entry->ClientName);
  6596. }
  6597. HeapFree(ghPnPHeap, 0, entry);
  6598. }
  6599. }
  6600. return Status;
  6601. } // RegisterServiceNotification
  6602. CONFIGRET
  6603. RegisterScmCallback(
  6604. IN PSCMCALLBACK_ROUTINE pScCallback,
  6605. IN PSCMAUTHENTICATION_CALLBACK pScAuthCallback
  6606. )
  6607. /*++
  6608. Routine Description:
  6609. This routine is called directly and privately by the service controller. It
  6610. allows the SCM to dynamically provide this service with callback routines.
  6611. Arguments:
  6612. pScCallback - Specifies the entrypoint for the routine that should be used
  6613. to have the service controller send special controls to a
  6614. service (which ControlService would block), on behalf of
  6615. the user-mode plug and play manager.
  6616. pScAuthCallback - Specifies the entrypoint for the routine that should be
  6617. used to retrieve the service status for a service.
  6618. Return Value:
  6619. Returns CR_SUCCESS.
  6620. --*/
  6621. {
  6622. ASSERT(pScCallback);
  6623. ASSERT(pScAuthCallback);
  6624. pServiceControlCallback = pScCallback;
  6625. pSCMAuthenticate = pScAuthCallback;
  6626. return CR_SUCCESS;
  6627. }
  6628. CONFIGRET
  6629. UnRegisterScmCallback(
  6630. VOID
  6631. )
  6632. /*++
  6633. Routine Description:
  6634. This routine is called directly and privately by the service controller. It
  6635. allows the SCM to unregister the callback routines previously registered by
  6636. RegisterScmCallback.
  6637. Arguments:
  6638. None.
  6639. Return Value:
  6640. Returns CR_SUCCESS.
  6641. --*/
  6642. {
  6643. pServiceControlCallback = NULL;
  6644. pSCMAuthenticate = NULL;
  6645. return CR_SUCCESS;
  6646. }
  6647. ULONG
  6648. MapSCMControlsToControlBit(
  6649. IN ULONG scmControls
  6650. )
  6651. /*++
  6652. Routine Description:
  6653. Returns a bitmask of control bits specifying ServiceList lists to which a
  6654. service should be added or removed from, based on the controls currently
  6655. accepted by the service.
  6656. Arguments:
  6657. scmControls - Specifies the service controls currently accepted by a
  6658. service.
  6659. Return Value:
  6660. Returns a bitmask of control bits corresponding to entries in the
  6661. ServiceList array of lists to which a service should be added or removed
  6662. from, based on the controls currently accepted by the service.
  6663. Notes:
  6664. Services are added or removed from a ServiceList notification list by adding
  6665. or removing the corresponding SERVICE_ACCEPT_* control from its list of
  6666. accepted controls when calling SetServiceStatus(). The service control
  6667. manager calls RegisterServiceNotification() as appropriate to register or
  6668. unregister the service to receive that control. Currently, only
  6669. SERVICE_ACCEPT_HARDWAREPROFILECHANGE and SERVICE_ACCEPT_POWEREVENT are
  6670. supported.
  6671. A service registers to receive the SERVICE_CONTROL_DEVICEEVENT control by
  6672. calling RegisterDeviceNotification, and is stored in the appropriate
  6673. TargetList or ClassList entry.
  6674. --*/
  6675. {
  6676. ULONG retBits=0;
  6677. if (scmControls & SERVICE_ACCEPT_HARDWAREPROFILECHANGE) {
  6678. retBits |= CBIT_HWPROFILE;
  6679. }
  6680. if (scmControls & SERVICE_ACCEPT_POWEREVENT) {
  6681. retBits |= CBIT_POWEREVENT;
  6682. }
  6683. return retBits;
  6684. } // MapSCMControlsToControlBit
  6685. DWORD
  6686. GetFirstPass(
  6687. IN BOOL bQuery
  6688. )
  6689. /*++
  6690. Routine Description:
  6691. This routine retrieves the first class of handles to notify. The subsequent
  6692. class of handles to notify should be retrieved by calling GetNextPass(...);
  6693. Arguments:
  6694. bQuery - If TRUE, starts with window handles, otherwise service handles.
  6695. Return Value:
  6696. Returns the first class of handles to notify.
  6697. Notes:
  6698. See GetNextPass() for the notification pass progression.
  6699. --*/
  6700. {
  6701. //
  6702. // Since services are generally less likely to veto device event queries, we
  6703. // first make sure that all windows succeed the query before notifying any
  6704. // services. For non-query events, services should be the first to know.
  6705. //
  6706. return (bQuery) ? DEVICE_NOTIFY_WINDOW_HANDLE : DEVICE_NOTIFY_SERVICE_HANDLE;
  6707. }
  6708. DWORD
  6709. GetNextPass(
  6710. IN DWORD curPass,
  6711. IN BOOL bQuery
  6712. )
  6713. /*++
  6714. Routine Description:
  6715. This routine retrieves the next class of handles to notify. If there is no
  6716. subsequent class of handles to notify, PASS_COMPLETE is returned.
  6717. Arguments:
  6718. curPass Current pass.
  6719. bQuery If TRUE, proceed from window handles to completion handles to
  6720. service handles. Otherwise process in reverse.
  6721. Return Value:
  6722. Returns the subsequent pass.
  6723. Notes:
  6724. For query events, the notification pass progression is:
  6725. DEVICE_NOTIFY_WINDOW_HANDLE,
  6726. DEVICE_NOTIFY_COMPLETION_HANDLE,
  6727. DEVICE_NOTIFY_SERVICE_HANDLE,
  6728. PASS_COMPLETE
  6729. For non-query events, the notification pass progression is:
  6730. DEVICE_NOTIFY_SERVICE_HANDLE,
  6731. DEVICE_NOTIFY_COMPLETION_HANDLE,
  6732. DEVICE_NOTIFY_WINDOW_HANDLE,
  6733. PASS_COMPLETE
  6734. --*/
  6735. {
  6736. if (bQuery) {
  6737. if (curPass == DEVICE_NOTIFY_WINDOW_HANDLE ) {
  6738. curPass = DEVICE_NOTIFY_COMPLETION_HANDLE;
  6739. } else if (curPass == DEVICE_NOTIFY_COMPLETION_HANDLE) {
  6740. curPass = DEVICE_NOTIFY_SERVICE_HANDLE;
  6741. } else {
  6742. curPass = PASS_COMPLETE;
  6743. }
  6744. } else {
  6745. if (curPass == DEVICE_NOTIFY_SERVICE_HANDLE ) {
  6746. curPass = DEVICE_NOTIFY_COMPLETION_HANDLE;
  6747. } else if (curPass == DEVICE_NOTIFY_COMPLETION_HANDLE) {
  6748. curPass = DEVICE_NOTIFY_WINDOW_HANDLE;
  6749. } else {
  6750. curPass = PASS_COMPLETE;
  6751. }
  6752. }
  6753. return curPass;
  6754. }
  6755. BOOL
  6756. NotifyEntryThisPass(
  6757. IN PPNP_NOTIFY_ENTRY Entry,
  6758. IN DWORD Pass
  6759. )
  6760. {
  6761. ASSERT(Pass != PASS_COMPLETE);
  6762. return ((!(Entry->Unregistered)) && (GetPassFromEntry(Entry) == Pass));
  6763. }
  6764. DWORD
  6765. GetPassFromEntry(
  6766. IN PPNP_NOTIFY_ENTRY Entry
  6767. )
  6768. {
  6769. return (Entry->Flags & DEVICE_NOTIFY_HANDLE_MASK);
  6770. }
  6771. BOOL
  6772. EventIdFromEventGuid(
  6773. IN CONST GUID *EventGuid,
  6774. OUT LPDWORD EventId,
  6775. OUT LPDWORD Flags,
  6776. OUT LPDWORD ServiceControl
  6777. )
  6778. /*++
  6779. Routine Description:
  6780. This thread routine converts an event guid into the corresponding event id
  6781. that user-mode code expects (used in BroadcastSystemMessage).
  6782. Arguments:
  6783. EventGuid Specifies an event guid.
  6784. EventId Returns the id form (from dbt.h) of the guid in EventGuid.
  6785. Flags Returns the flags that should be used when broadcasting this
  6786. event.
  6787. NOTE: device ARRIVAL and event CANCEL are considered "Queries"
  6788. since the bottom level drivers need to be told first.
  6789. Return Value:
  6790. Currently returns TRUE/FALSE.
  6791. Notes:
  6792. Most users of this function call it mainly to retrieve the EventId. Those
  6793. functions typically examine the returned flags only to check the BSF_QUERY
  6794. flag (ie, they don't call BroadcastSystemMessage). Depending on whether
  6795. BSF_QUERY is set, the notification lists will be walked forwards or
  6796. backwards.
  6797. We should really return something generic such as:
  6798. [MSG_POST, MSG_QUERY, MSG_SEND] | [MSG_FORWARDS, MSG_BACKWARDS]
  6799. Then we should implement a BsmFlagsFromMsgFlags function.
  6800. --*/
  6801. {
  6802. //
  6803. // BSF_IGNORECURRENTTASK - Sent messages do not appear in the sending
  6804. // processes message queue.
  6805. //
  6806. // BSF_QUERY - If any recipient vetoes the message by returning
  6807. // the appropriate value, the broadcast is failed
  6808. // (ie, BroadcastSystemMessage returns 0).
  6809. //
  6810. // BSF_NOHANG - Non-posted messages are automatically failed if
  6811. // the window has not processed any available
  6812. // messages within a system defined time (as of
  6813. // 04/20/1999 this is 5 seconds).
  6814. // (SendMessageTimeout: SMTO_ABORTIFHUNG)
  6815. //
  6816. // BSF_FORCEIFHUNG - Failures due to timeouts or hangs are instead
  6817. // treated as successes.
  6818. //
  6819. // BSF_NOTIMEOUTIFNOTHUNG - If a window has not responded to the passed in
  6820. // notification, but is actively processing
  6821. // subsequent messages, then it is assumed to be
  6822. // interacting with the user, in which case the
  6823. // timeout is on hold.
  6824. // (SendMessageTimeout: SMTO_NOTIMEOUTIFNOTHUNG)
  6825. //
  6826. // BSF_POSTMESSAGE - Message is posted, results ignored. Note that
  6827. // a notification with private data in the lParam
  6828. // *cannot* be posted - the OS does not make a
  6829. // private copy, but rather treats the broadcast
  6830. // as if it were a SendMessage if you try.
  6831. //
  6832. // BSF_ALLOWSFW - Windows that receive the broadcast are allowed
  6833. // to become foreground windows.
  6834. //
  6835. // Also, DBT messages >= 0x8000 have lParams pointing to blocks of data that
  6836. // need to be marshalled around. As user doesn't support "snapshotting" the
  6837. // data for posts, we can't pass in BSF_POSTMESSAGE.
  6838. //
  6839. *Flags = BSF_IGNORECURRENTTASK;
  6840. //
  6841. // Standard (well-known) event guids.
  6842. //
  6843. if (GuidEqual(EventGuid, (LPGUID)&GUID_HWPROFILE_QUERY_CHANGE)) {
  6844. *Flags |= BSF_QUERY | BSF_ALLOWSFW |
  6845. BSF_FORCEIFHUNG | BSF_NOHANG;
  6846. *EventId = DBT_QUERYCHANGECONFIG;
  6847. *ServiceControl = SERVICE_CONTROL_HARDWAREPROFILECHANGE;
  6848. } else if (GuidEqual(EventGuid, (LPGUID)&GUID_HWPROFILE_CHANGE_CANCELLED)) {
  6849. *Flags |= BSF_POSTMESSAGE;
  6850. *EventId = DBT_CONFIGCHANGECANCELED;
  6851. *ServiceControl = SERVICE_CONTROL_HARDWAREPROFILECHANGE;
  6852. } else if (GuidEqual(EventGuid, (LPGUID)&GUID_HWPROFILE_CHANGE_COMPLETE)) {
  6853. *Flags |= BSF_POSTMESSAGE;
  6854. *EventId = DBT_CONFIGCHANGED;
  6855. *ServiceControl = SERVICE_CONTROL_HARDWAREPROFILECHANGE;
  6856. } else if (GuidEqual(EventGuid, (LPGUID)&GUID_DEVICE_INTERFACE_ARRIVAL)) {
  6857. *Flags |= BSF_NOHANG;
  6858. *EventId = DBT_DEVICEARRIVAL;
  6859. *ServiceControl = SERVICE_CONTROL_DEVICEEVENT;
  6860. } else if (GuidEqual(EventGuid, (LPGUID)&GUID_DEVICE_INTERFACE_REMOVAL)) {
  6861. //
  6862. // NOTE - BSF_QUERY is set so that we run the list backwards. No actual
  6863. // broadcasts are done on this Id.
  6864. //
  6865. *Flags |= BSF_NOHANG | BSF_QUERY;
  6866. *EventId = DBT_DEVICEREMOVECOMPLETE;
  6867. *ServiceControl = SERVICE_CONTROL_DEVICEEVENT;
  6868. } else if (GuidEqual(EventGuid, (LPGUID)&GUID_TARGET_DEVICE_QUERY_REMOVE)) {
  6869. *Flags |= BSF_QUERY | BSF_ALLOWSFW |
  6870. BSF_FORCEIFHUNG | BSF_NOHANG;
  6871. *EventId = DBT_DEVICEQUERYREMOVE;
  6872. *ServiceControl = SERVICE_CONTROL_DEVICEEVENT;
  6873. } else if (GuidEqual(EventGuid, (LPGUID)&GUID_TARGET_DEVICE_REMOVE_CANCELLED)) {
  6874. *Flags |= BSF_NOHANG;
  6875. *EventId = DBT_DEVICEQUERYREMOVEFAILED;
  6876. *ServiceControl = SERVICE_CONTROL_DEVICEEVENT;
  6877. } else if (GuidEqual(EventGuid, (LPGUID)&GUID_DEVICE_REMOVE_PENDING)) {
  6878. //
  6879. // NOTE - BSF_QUERY is set so that we run the list backwards. No actual
  6880. // broadcasts are done on this Id.
  6881. //
  6882. *Flags |= BSF_NOHANG | BSF_QUERY;
  6883. *EventId = DBT_DEVICEREMOVEPENDING;
  6884. *ServiceControl = SERVICE_CONTROL_DEVICEEVENT;
  6885. } else if (GuidEqual(EventGuid, (LPGUID)&GUID_TARGET_DEVICE_REMOVE_COMPLETE)) {
  6886. //
  6887. // NOTE - BSF_QUERY is set so that we run the list backwards. No actual
  6888. // broadcasts are done on this Id.
  6889. //
  6890. *Flags |= BSF_NOHANG | BSF_QUERY;
  6891. *EventId = DBT_DEVICEREMOVECOMPLETE;
  6892. *ServiceControl = SERVICE_CONTROL_DEVICEEVENT;
  6893. } else if (GuidEqual(EventGuid, (LPGUID)&GUID_DEVICE_ARRIVAL)) {
  6894. *Flags |= BSF_NOHANG;
  6895. *EventId = DBT_DEVICEARRIVAL;
  6896. *ServiceControl = SERVICE_CONTROL_DEVICEEVENT;
  6897. } else if (GuidEqual(EventGuid, (LPGUID)&GUID_DEVICE_ENUMERATED)) {
  6898. *Flags = 0;
  6899. *EventId = DBT_DEVICEARRIVAL;
  6900. *ServiceControl = 0;
  6901. //
  6902. // Private event guids (kernel-mode pnp to user-mode pnp communication).
  6903. // Setting EventId to zero causes ProcessDeviceEvent to swallow these
  6904. // TargetDeviceChangeEvent events.
  6905. //
  6906. } else if (GuidEqual(EventGuid, (LPGUID)&GUID_DEVICE_SAFE_REMOVAL) ||
  6907. GuidEqual(EventGuid, (LPGUID)&GUID_DEVICE_EJECT_VETOED) ||
  6908. GuidEqual(EventGuid, (LPGUID)&GUID_DEVICE_REMOVAL_VETOED) ||
  6909. GuidEqual(EventGuid, (LPGUID)&GUID_DEVICE_WARM_EJECT_VETOED) ||
  6910. GuidEqual(EventGuid, (LPGUID)&GUID_DEVICE_STANDBY_VETOED) ||
  6911. GuidEqual(EventGuid, (LPGUID)&GUID_DEVICE_HIBERNATE_VETOED) ||
  6912. GuidEqual(EventGuid, (LPGUID)&GUID_DEVICE_KERNEL_INITIATED_EJECT) ||
  6913. GuidEqual(EventGuid, (LPGUID)&GUID_DEVICE_SURPRISE_REMOVAL) ||
  6914. GuidEqual(EventGuid, (LPGUID)&GUID_DRIVER_BLOCKED)) {
  6915. *Flags = 0;
  6916. *EventId = 0;
  6917. *ServiceControl = 0;
  6918. } else if (GuidEqual(EventGuid, (LPGUID)&GUID_PNP_CUSTOM_NOTIFICATION)) {
  6919. //
  6920. // Custom events cannot be failed (ie they aren't queries)
  6921. //
  6922. *EventId = DBT_CUSTOMEVENT;
  6923. *Flags |= BSF_NOHANG;
  6924. *ServiceControl = SERVICE_CONTROL_DEVICEEVENT;
  6925. } else if (GuidEqual(EventGuid, (LPGUID)&GUID_PNP_POWER_NOTIFICATION)) {
  6926. //
  6927. // These are treated as custom too.
  6928. //
  6929. *EventId = DBT_CUSTOMEVENT;
  6930. *Flags |= BSF_NOHANG;
  6931. *ServiceControl = SERVICE_CONTROL_POWEREVENT;
  6932. } else {
  6933. //
  6934. // Anything that makes it here is a bug.
  6935. //
  6936. ASSERT(GuidEqual(EventGuid, (LPGUID)&GUID_PNP_CUSTOM_NOTIFICATION));
  6937. *EventId = 0;
  6938. *Flags = 0;
  6939. *ServiceControl = 0;
  6940. }
  6941. return TRUE;
  6942. } // EventIdFromEventGuid
  6943. ULONG
  6944. SendHotplugNotification(
  6945. IN CONST GUID *EventGuid,
  6946. IN PPNP_VETO_TYPE VetoType OPTIONAL,
  6947. IN LPWSTR MultiSzList,
  6948. IN OUT PULONG SessionId,
  6949. IN ULONG Flags
  6950. )
  6951. /*++
  6952. Routine Description:
  6953. This routine kicks off a hotplug.dll process (if someone is logged in).
  6954. We use a named pipe to comunicate with the user mode process and have it
  6955. display the requested UI.
  6956. Arguments:
  6957. EventGuid - Specifies an event GUID.
  6958. VetoType - For events requiring a vetoer, supplies the address of a
  6959. variable containing the type of the component responsible for
  6960. vetoing the request.
  6961. MultiSzList - Supplies the MultiSz list to be sent to hotplu.dll. This is
  6962. usually a device ID, possibly followed by a list of vetoers
  6963. (which may or may not be device ID's).
  6964. SessionId - Supplies the address of a variable containing the SessionId on
  6965. which the hotplug dialog is to be displayed. If successful,
  6966. the SessionId will contain the id of the session in which the
  6967. device install client process was launched. Otherwise, will
  6968. contain an invalid session id, INVALID_SESSION (0xFFFFFFFF).
  6969. Flags - Specifies flags describing the behavior of the hotplug dialog.
  6970. The following flags are currently defined:
  6971. HOTPLUG_DISPLAY_ON_CONSOLE - if specified, the value in the SessionId
  6972. variable will be ignored, and the hotplug dialog will always be
  6973. displayed on the current active console session.
  6974. Return Value:
  6975. Currently returns TRUE/FALSE.
  6976. Return Value:
  6977. If the process was successfully created, the return value is TRUE. This
  6978. routine doesn't wait until the process terminates.
  6979. If we couldn't create the process (e.g., because no user was logged in),
  6980. the return value is FALSE.
  6981. --*/
  6982. {
  6983. BOOL bStatus;
  6984. STARTUPINFO StartupInfo;
  6985. PROCESS_INFORMATION ProcessInfo;
  6986. WCHAR szCmdLine[MAX_PATH];
  6987. WCHAR szHotPlugDllEntryPoint[80];
  6988. HANDLE hHotPlugPipe = NULL;
  6989. HANDLE hHotPlugEvent = NULL;
  6990. HANDLE hFinishEvents[2] = { NULL, NULL };
  6991. HANDLE hTemp, hUserToken = NULL;
  6992. RPC_STATUS rpcStatus = RPC_S_OK;
  6993. GUID newGuid;
  6994. WCHAR szGuidString[MAX_GUID_STRING_LEN];
  6995. WCHAR szHotPlugPipeName[MAX_PATH];
  6996. WCHAR szHotPlugEventName[MAX_PATH];
  6997. ULONG ulHotPlugEventNameSize;
  6998. ULONG ulMultiSzListSize;
  6999. ULONG ulSize, ulSessionId;
  7000. WIN32_FIND_DATA findData;
  7001. LPWSTR pszName = NULL;
  7002. PVOID lpEnvironment = NULL;
  7003. OVERLAPPED overlapped;
  7004. DWORD dwError, dwWait, dwBytes;
  7005. //
  7006. // Check if we should skip client side UI.
  7007. //
  7008. if (gbSuppressUI) {
  7009. KdPrintEx((DPFLTR_PNPMGR_ID,
  7010. DBGF_WARNINGS,
  7011. "UMPNPMGR: SendHotplugNotification: "
  7012. "UI has been suppressed, exiting.\n"));
  7013. LogWarningEvent(WRN_HOTPLUG_UI_SUPPRESSED, 1, MultiSzList);
  7014. return FALSE;
  7015. }
  7016. //
  7017. // Assume failure
  7018. //
  7019. bStatus = FALSE;
  7020. try {
  7021. //
  7022. // Determine the session to use, based on the supplied flags.
  7023. //
  7024. if (Flags & HOTPLUG_DISPLAY_ON_CONSOLE) {
  7025. ulSessionId = GetActiveConsoleSessionId();
  7026. } else {
  7027. ASSERT(*SessionId != INVALID_SESSION);
  7028. ulSessionId = *SessionId;
  7029. }
  7030. //
  7031. // Before doing anything, check that hotplug.dll is actually present on
  7032. // the system.
  7033. //
  7034. szCmdLine[0] = L'\0';
  7035. ulSize = GetSystemDirectory(szCmdLine, MAX_PATH);
  7036. if ((ulSize == 0) || ((ulSize + 2 + ARRAY_SIZE(HOTPLUG_DLL)) > MAX_PATH)) {
  7037. return FALSE;
  7038. }
  7039. lstrcat(szCmdLine, TEXT("\\"));
  7040. lstrcat(szCmdLine, HOTPLUG_DLL);
  7041. hTemp = FindFirstFile(szCmdLine, &findData);
  7042. if(hTemp != INVALID_HANDLE_VALUE) {
  7043. FindClose(hTemp);
  7044. } else {
  7045. KdPrintEx((DPFLTR_PNPMGR_ID,
  7046. DBGF_ERRORS | DBGF_WARNINGS | DBGF_EVENT,
  7047. "UMPNPMGR: SendHotplugNotification: %ws not found, error = %d, exiting\n",
  7048. szCmdLine,
  7049. GetLastError()));
  7050. LogWarningEvent(WRN_HOTPLUG_NOT_PRESENT, 1, szCmdLine);
  7051. return FALSE;
  7052. }
  7053. //
  7054. // Get the user access token for the active console session user.
  7055. //
  7056. if (!GetSessionUserToken(ulSessionId, &hUserToken)) {
  7057. return FALSE;
  7058. }
  7059. //
  7060. // Create a named pipe and event for communication and synchronization
  7061. // with HotPlug. The event and named pipe must be global so that
  7062. // UMPNPMGR can interact with a HotPlug client in a different session,
  7063. // but it must still be unique for that session. Add a generated GUID
  7064. // so the names are not entirely well-known.
  7065. //
  7066. rpcStatus = UuidCreate(&newGuid);
  7067. if ((rpcStatus != RPC_S_OK) &&
  7068. (rpcStatus != RPC_S_UUID_LOCAL_ONLY)) {
  7069. goto clean0;
  7070. }
  7071. if (StringFromGuid((LPGUID)&newGuid,
  7072. szGuidString,
  7073. MAX_GUID_STRING_LEN) != NO_ERROR) {
  7074. goto clean0;
  7075. }
  7076. wsprintf(szHotPlugPipeName,
  7077. TEXT("%ws_%d.%ws"),
  7078. PNP_HOTPLUG_PIPE,
  7079. ulSessionId,
  7080. szGuidString);
  7081. wsprintf(szHotPlugEventName,
  7082. TEXT("Global\\%ws_%d.%ws"),
  7083. PNP_HOTPLUG_EVENT,
  7084. ulSessionId,
  7085. szGuidString);
  7086. ulHotPlugEventNameSize = (lstrlen(szHotPlugEventName) + 1) * sizeof(WCHAR);
  7087. //
  7088. // Initialize process, startup and overlapped structures, since we
  7089. // depend on them being NULL during cleanup here on out.
  7090. //
  7091. memset(&ProcessInfo, 0, sizeof(ProcessInfo));
  7092. memset(&StartupInfo, 0, sizeof(StartupInfo));
  7093. memset(&overlapped, 0, sizeof(overlapped));
  7094. //
  7095. // Get the length of the multi-sz list. This is usually a device ID
  7096. // possibly followed by a list of vetoers which may or may not be device
  7097. // Id's
  7098. //
  7099. ulMultiSzListSize = 0;
  7100. for (pszName = MultiSzList;
  7101. *pszName;
  7102. pszName += lstrlen(pszName) + 1) {
  7103. ulMultiSzListSize += (lstrlen(pszName) + 1) * sizeof(WCHAR);
  7104. }
  7105. ulMultiSzListSize += sizeof(WCHAR);
  7106. //
  7107. // The approximate size of the named pipe output buffer should be large
  7108. // enough to hold the greater of either:
  7109. // - The name and size of the named event string, OR
  7110. // - The type, size and contents of the multi-sz list.
  7111. //
  7112. ulSize = max(sizeof(ulHotPlugEventNameSize) +
  7113. ulHotPlugEventNameSize,
  7114. sizeof(PNP_VETO_TYPE) +
  7115. sizeof(ulMultiSzListSize) +
  7116. ulMultiSzListSize);
  7117. //
  7118. // Open up a named pipe to communicate with hotplug.dll.
  7119. //
  7120. hHotPlugPipe = CreateNamedPipe(szHotPlugPipeName,
  7121. PIPE_ACCESS_OUTBOUND | // outbound data only
  7122. FILE_FLAG_OVERLAPPED | // use overlapped structure
  7123. FILE_FLAG_FIRST_PIPE_INSTANCE, // make sure we are the creator of the pipe
  7124. PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
  7125. 1, // only one instance is allowed, and we are its creator
  7126. ulSize, // out buffer size
  7127. 0, // in buffer size
  7128. PNP_PIPE_TIMEOUT, // default timeout
  7129. NULL // default security
  7130. );
  7131. if (hHotPlugPipe == INVALID_HANDLE_VALUE) {
  7132. hHotPlugPipe = NULL;
  7133. goto clean0;
  7134. }
  7135. //
  7136. // Create an event that a user-client can synchronize with and set, and
  7137. // that we will block on after we send all the device IDs to
  7138. // hotplug.dll.
  7139. //
  7140. if (CreateUserSynchEvent(szHotPlugEventName,
  7141. &hHotPlugEvent) != NO_ERROR) {
  7142. goto clean0;
  7143. }
  7144. if (GuidEqual(EventGuid, (LPGUID)&GUID_DEVICE_EJECT_VETOED)) {
  7145. //
  7146. // GUID_DEVICE_EJECT_VETOED : HotPlugEjectVetoed
  7147. // Expects veto information.
  7148. //
  7149. lstrcpy(szHotPlugDllEntryPoint, TEXT("HotPlugEjectVetoed"));
  7150. ASSERT(VetoType);
  7151. } else if (GuidEqual(EventGuid, (LPGUID)&GUID_DEVICE_REMOVAL_VETOED)) {
  7152. //
  7153. // GUID_DEVICE_REMOVAL_VETOED : HotPlugRemovalVetoed
  7154. // Expects veto information.
  7155. //
  7156. lstrcpy(szHotPlugDllEntryPoint, TEXT("HotPlugRemovalVetoed"));
  7157. ASSERT(VetoType);
  7158. } else if (GuidEqual(EventGuid, (LPGUID)&GUID_DEVICE_STANDBY_VETOED)) {
  7159. //
  7160. // GUID_DEVICE_STANDBY_VETOED : HotPlugStandbyVetoed
  7161. // Expects veto information.
  7162. //
  7163. lstrcpy(szHotPlugDllEntryPoint, TEXT("HotPlugStandbyVetoed"));
  7164. ASSERT(VetoType);
  7165. } else if (GuidEqual(EventGuid, (LPGUID)&GUID_DEVICE_HIBERNATE_VETOED)) {
  7166. //
  7167. // GUID_DEVICE_HIBERNATE_VETOED : HotPlugHibernateVetoed
  7168. // Expects veto information.
  7169. //
  7170. lstrcpy(szHotPlugDllEntryPoint, TEXT("HotPlugHibernateVetoed"));
  7171. ASSERT(VetoType);
  7172. } else if (GuidEqual(EventGuid, (LPGUID)&GUID_DEVICE_WARM_EJECT_VETOED)) {
  7173. //
  7174. // GUID_DEVICE_WARM_EJECT_VETOED : HotPlugWarmEjectVetoed
  7175. // Expects veto information.
  7176. //
  7177. lstrcpy(szHotPlugDllEntryPoint, TEXT("HotPlugWarmEjectVetoed"));
  7178. ASSERT(VetoType);
  7179. } else if (GuidEqual(EventGuid, (LPGUID)&GUID_DEVICE_SAFE_REMOVAL)) {
  7180. //
  7181. // GUID_DEVICE_SAFE_REMOVAL : HotPlugSafeRemovalNotification
  7182. // No veto information.
  7183. //
  7184. lstrcpy(szHotPlugDllEntryPoint, TEXT("HotPlugSafeRemovalNotification"));
  7185. ASSERT(VetoType == NULL);
  7186. } else if (GuidEqual(EventGuid, (LPGUID)&GUID_DEVICE_SURPRISE_REMOVAL)) {
  7187. //
  7188. // GUID_DEVICE_SURPRISE_REMOVAL : HotPlugSurpriseWarn
  7189. // No veto information.
  7190. //
  7191. lstrcpy(szHotPlugDllEntryPoint, TEXT("HotPlugSurpriseWarn"));
  7192. ASSERT(VetoType == NULL);
  7193. } else if (GuidEqual(EventGuid, (LPGUID)&GUID_DRIVER_BLOCKED)) {
  7194. //
  7195. // GUID_DRIVER_BLOCKED : HotPlugDriverBlocked
  7196. // No veto information.
  7197. //
  7198. lstrcpy(szHotPlugDllEntryPoint, TEXT("HotPlugDriverBlocked"));
  7199. ASSERT(VetoType == NULL);
  7200. } else {
  7201. //
  7202. // Unknown device event.
  7203. //
  7204. KdPrintEx((DPFLTR_PNPMGR_ID,
  7205. DBGF_ERRORS | DBGF_EVENT,
  7206. "UMPNPMGR: SendHotplugNotification: Unknown device event!\n"));
  7207. ASSERT(0);
  7208. goto clean0;
  7209. }
  7210. //
  7211. // Attempt to create the user's environment block. If for some reason we
  7212. // can't, we'll just have to create the process without it.
  7213. //
  7214. if (!CreateEnvironmentBlock(&lpEnvironment,
  7215. hUserToken,
  7216. FALSE)) {
  7217. KdPrintEx((DPFLTR_PNPMGR_ID,
  7218. DBGF_ERRORS | DBGF_EVENT,
  7219. "UMPNPMGR: SendHotplugNotification: "
  7220. "Failed to allocate environment block, error = %d!\n",
  7221. GetLastError()));
  7222. lpEnvironment = NULL;
  7223. }
  7224. //
  7225. // Launch hotplug.dll using rundll32.exe, passing it the pipe name.
  7226. // "rundll32.exe hotplug.dll,<hotplug-entry-point> <hotplug-pipe-name>"
  7227. //
  7228. if (ARRAY_SIZE(szCmdLine) < (ARRAY_SIZE(RUNDLL32_EXE) +
  7229. 1 + // ' '
  7230. ARRAY_SIZE(HOTPLUG_DLL) +
  7231. 1 + // ','
  7232. lstrlen(szHotPlugDllEntryPoint) +
  7233. 1 + // ' '
  7234. lstrlen(szHotPlugPipeName) +
  7235. 1)) { // '\0'
  7236. goto clean0;
  7237. }
  7238. wsprintf(szCmdLine,
  7239. TEXT("%ws %ws,%ws %ws"),
  7240. RUNDLL32_EXE, HOTPLUG_DLL,
  7241. szHotPlugDllEntryPoint,
  7242. szHotPlugPipeName);
  7243. StartupInfo.cb = sizeof(StartupInfo);
  7244. StartupInfo.wShowWindow = SW_SHOW;
  7245. StartupInfo.lpDesktop = DEFAULT_INTERACTIVE_DESKTOP; // WinSta0\Default
  7246. //
  7247. // CreateProcessAsUser will create the process in the session
  7248. // specified by the by user-token.
  7249. //
  7250. if (!CreateProcessAsUser(hUserToken, // hToken
  7251. NULL, // lpApplicationName
  7252. szCmdLine, // lpCommandLine
  7253. NULL, // lpProcessAttributes
  7254. NULL, // lpThreadAttributes
  7255. FALSE, // bInheritHandles
  7256. CREATE_UNICODE_ENVIRONMENT |
  7257. DETACHED_PROCESS, // dwCreationFlags
  7258. lpEnvironment, // lpEnvironment
  7259. NULL, // lpDirectory
  7260. &StartupInfo, // lpStartupInfo
  7261. &ProcessInfo // lpProcessInfo
  7262. )) {
  7263. KdPrintEx((DPFLTR_PNPMGR_ID,
  7264. DBGF_EVENT | DBGF_ERRORS,
  7265. "UMPNPMGR: SendHotplugNotification: "
  7266. "Create rundll32 process failed, error = %d\n",
  7267. GetLastError()));
  7268. goto clean0;
  7269. }
  7270. ASSERT(ProcessInfo.hProcess);
  7271. ASSERT(ProcessInfo.hThread);
  7272. //
  7273. // Create an event for use with overlapped I/O - no security, manual
  7274. // reset, not signalled, no name.
  7275. //
  7276. overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  7277. if (overlapped.hEvent == NULL) {
  7278. goto clean0;
  7279. }
  7280. //
  7281. // Connect to the newly created named pipe. If hotplug is not already
  7282. // connected to the named pipe, then ConnectNamedPipe() will fail with
  7283. // ERROR_IO_PENDING, and we will wait on the overlapped event. If
  7284. // newdev is already connected, it will fail with ERROR_PIPE_CONNECTED.
  7285. // Note however that neither of these is an error condition.
  7286. //
  7287. if (!ConnectNamedPipe(hHotPlugPipe, &overlapped)) {
  7288. //
  7289. // Overlapped ConnectNamedPipe should always return FALSE on
  7290. // success. Check the last error to see what really happened.
  7291. //
  7292. dwError = GetLastError();
  7293. if (dwError == ERROR_IO_PENDING) {
  7294. //
  7295. // I/O is pending, wait up to one minute for the client to
  7296. // connect, also wait on the process in case it terminates
  7297. // unexpectedly.
  7298. //
  7299. hFinishEvents[0] = overlapped.hEvent;
  7300. hFinishEvents[1] = ProcessInfo.hProcess;
  7301. dwWait = WaitForMultipleObjects(2, hFinishEvents,
  7302. FALSE,
  7303. PNP_PIPE_TIMEOUT); // 60 seconds
  7304. if (dwWait == WAIT_OBJECT_0) {
  7305. //
  7306. // The overlapped I/O operation completed. Check the status
  7307. // of the operation.
  7308. //
  7309. if (!GetOverlappedResult(hHotPlugPipe,
  7310. &overlapped,
  7311. &dwBytes,
  7312. FALSE)) {
  7313. goto clean0;
  7314. }
  7315. } else {
  7316. //
  7317. // Either the connection timed out, or the client process
  7318. // exited. Cancel pending I/O against the pipe, and quit.
  7319. //
  7320. KdPrintEx((DPFLTR_PNPMGR_ID,
  7321. DBGF_INSTALL | DBGF_ERRORS,
  7322. "UMPNPMGR: SendHotPlugNotification: "
  7323. "Connect timed out, or client process exited!\n"));
  7324. CancelIo(hHotPlugPipe);
  7325. goto clean0;
  7326. }
  7327. } else if (dwError != ERROR_PIPE_CONNECTED) {
  7328. //
  7329. // If the last error indicates anything other than pending I/O,
  7330. // or that The client is already connected to named pipe, fail.
  7331. //
  7332. goto clean0;
  7333. }
  7334. } else {
  7335. //
  7336. // ConnectNamedPipe should not return anything but FALSE in
  7337. // overlapped mode.
  7338. //
  7339. goto clean0;
  7340. }
  7341. //
  7342. // The client is now connected to the named pipe.
  7343. // Close the overlapped event.
  7344. //
  7345. CloseHandle(overlapped.hEvent);
  7346. overlapped.hEvent = NULL;
  7347. //
  7348. // The first data in the pipe will be the length of the name of the
  7349. // event that will be used to sync up umpnpmgr.dll and hotplug.dll.
  7350. //
  7351. if (!WriteFile(hHotPlugPipe,
  7352. &ulHotPlugEventNameSize,
  7353. sizeof(ulHotPlugEventNameSize),
  7354. &ulSize,
  7355. NULL)) {
  7356. LogErrorEvent(ERR_WRITING_SURPRISE_REMOVE_PIPE, GetLastError(), 0);
  7357. goto clean0;
  7358. }
  7359. //
  7360. // The next data in the pipe will be the name of the event that will
  7361. // be used to sync up umpnpmgr.dll and hotplug.dll.
  7362. //
  7363. if (!WriteFile(hHotPlugPipe,
  7364. (LPCVOID)szHotPlugEventName,
  7365. ulHotPlugEventNameSize,
  7366. &ulSize,
  7367. NULL)) {
  7368. LogErrorEvent(ERR_WRITING_SURPRISE_REMOVE_PIPE, GetLastError(), 0);
  7369. goto clean0;
  7370. }
  7371. if (ARGUMENT_PRESENT(VetoType)) {
  7372. //
  7373. // For the notification types expecting veto information,
  7374. // send the Veto type to the client.
  7375. //
  7376. if (!WriteFile(hHotPlugPipe,
  7377. (LPCVOID)VetoType,
  7378. sizeof(PNP_VETO_TYPE),
  7379. &ulSize,
  7380. NULL)) {
  7381. LogErrorEvent(ERR_WRITING_SURPRISE_REMOVE_PIPE, GetLastError(), 0);
  7382. goto clean0;
  7383. }
  7384. }
  7385. //
  7386. // Send the string length to the client
  7387. //
  7388. if (!WriteFile(hHotPlugPipe,
  7389. (LPCVOID)&ulMultiSzListSize,
  7390. sizeof(ulMultiSzListSize),
  7391. &ulSize,
  7392. NULL)) {
  7393. LogErrorEvent(ERR_WRITING_SURPRISE_REMOVE_PIPE, GetLastError(), 0);
  7394. goto clean0;
  7395. }
  7396. //
  7397. // Now send over the entire string
  7398. //
  7399. if (!WriteFile(hHotPlugPipe,
  7400. MultiSzList,
  7401. ulMultiSzListSize,
  7402. &ulSize,
  7403. NULL)) {
  7404. LogErrorEvent(ERR_WRITING_SURPRISE_REMOVE_PIPE, GetLastError(), 0);
  7405. goto clean0;
  7406. }
  7407. //
  7408. // When we are done writing, we need to close the pipe handles so that
  7409. // the client will get a ReadFile error and know that we are finished.
  7410. //
  7411. if (hHotPlugPipe) {
  7412. CloseHandle(hHotPlugPipe);
  7413. hHotPlugPipe = NULL;
  7414. }
  7415. //
  7416. // Wait for hotplug.dll to respond by setting the event before before
  7417. // returning. Also wait on the process as well, to catch the case where
  7418. // the process crashes (or goes away) without signaling the device
  7419. // install event.
  7420. //
  7421. hFinishEvents[0] = hHotPlugEvent;
  7422. hFinishEvents[1] = ProcessInfo.hProcess;
  7423. WaitForMultipleObjects(2, hFinishEvents, FALSE, INFINITE);
  7424. bStatus = TRUE;
  7425. clean0:
  7426. NOTHING;
  7427. } except (EXCEPTION_EXECUTE_HANDLER) {
  7428. KdPrintEx((DPFLTR_PNPMGR_ID,
  7429. DBGF_ERRORS,
  7430. "UMPNPMGR: Exception in SendHotPlugNotification!!\n"));
  7431. ASSERT(0);
  7432. bStatus = FALSE;
  7433. //
  7434. // Reference the following variables so the compiler will respect
  7435. // statement ordering w.r.t. their assignment.
  7436. //
  7437. lpEnvironment = lpEnvironment;
  7438. ProcessInfo.hThread = ProcessInfo.hThread;
  7439. ProcessInfo.hProcess = ProcessInfo.hProcess;
  7440. hUserToken = hUserToken;
  7441. hHotPlugPipe = hHotPlugPipe;
  7442. hHotPlugEvent = hHotPlugEvent;
  7443. }
  7444. if (lpEnvironment) {
  7445. DestroyEnvironmentBlock(lpEnvironment);
  7446. }
  7447. if (ProcessInfo.hThread) {
  7448. CloseHandle(ProcessInfo.hThread);
  7449. }
  7450. if (ProcessInfo.hProcess) {
  7451. CloseHandle(ProcessInfo.hProcess);
  7452. }
  7453. if (hUserToken) {
  7454. CloseHandle(hUserToken);
  7455. }
  7456. if (overlapped.hEvent) {
  7457. CloseHandle(overlapped.hEvent);
  7458. }
  7459. if (hHotPlugPipe) {
  7460. CloseHandle(hHotPlugPipe);
  7461. }
  7462. if (hHotPlugEvent) {
  7463. CloseHandle(hHotPlugEvent);
  7464. }
  7465. if (!bStatus) {
  7466. *SessionId = INVALID_SESSION;
  7467. } else {
  7468. *SessionId = ulSessionId;
  7469. }
  7470. return bStatus;
  7471. } // SendHotplugNotification
  7472. ULONG
  7473. CheckEjectPermissions(
  7474. IN LPWSTR DeviceId,
  7475. OUT PPNP_VETO_TYPE VetoType OPTIONAL,
  7476. OUT LPWSTR VetoName OPTIONAL,
  7477. IN OUT PULONG VetoNameLength OPTIONAL
  7478. )
  7479. /*++
  7480. Routine Description:
  7481. Checks that the user has eject permissions for the specified device.
  7482. Arguments:
  7483. DeviceId - Specifies the device instance id of the device for which
  7484. eject permissions are to be checked.
  7485. VetoType - Supplies the address of a variable to receive, upon
  7486. failure, the type of the component responsible for vetoing
  7487. the request.
  7488. VetoName - Supplies the address of a variable to receive, upon
  7489. failure, the name of the component responsible for vetoing
  7490. the request.
  7491. VetoNameLength - Supplies the address of a variable specifying the size of
  7492. the of buffer specified by the VetoName parameter. Upon
  7493. failure, this address will specify the length of the string
  7494. stored in that buffer by this routine.
  7495. Return Value:
  7496. FALSE if the eject should be blocked, TRUE otherwise.
  7497. Note:
  7498. This routine is called while processing a kernel-initiated ejection event.
  7499. On this side of the event, we are NOT in the context of the user who
  7500. initiated the ejection, but since only the active console user was allowed
  7501. to initiate the request that triggered this event, we use the access token
  7502. of the active console user for the check on this side also. (should the
  7503. active console user change between the request and this event, this would
  7504. check that the user that the current active console user has eject
  7505. permissions; this is still a valid thing to do since it is the console user
  7506. who will receive the ejected hardware)
  7507. --*/
  7508. {
  7509. BOOL bResult, bDockDevice;
  7510. ULONG ulPropertyData, ulDataSize, ulDataType;
  7511. ULONG ulTransferLen, ulConsoleSessionId;
  7512. HANDLE hUserToken = NULL;
  7513. //
  7514. // Is this a dock?
  7515. //
  7516. bDockDevice = FALSE;
  7517. ulDataSize = ulTransferLen = sizeof(ULONG);
  7518. if (CR_SUCCESS == PNP_GetDeviceRegProp(NULL,
  7519. DeviceId,
  7520. CM_DRP_CAPABILITIES,
  7521. &ulDataType,
  7522. (LPBYTE)&ulPropertyData,
  7523. &ulTransferLen,
  7524. &ulDataSize,
  7525. 0)) {
  7526. if (ulPropertyData & CM_DEVCAP_DOCKDEVICE) {
  7527. //
  7528. // Undocking (ie ejecting a dock) uses a special privilege.
  7529. //
  7530. bDockDevice = TRUE;
  7531. }
  7532. } else {
  7533. KdPrintEx((DPFLTR_PNPMGR_ID,
  7534. DBGF_ERRORS,
  7535. "UMPNPMGR: PNP_GetDeviceRegProp failed, error = %d\n",
  7536. GetLastError()));
  7537. return FALSE;
  7538. }
  7539. ulConsoleSessionId = GetActiveConsoleSessionId();
  7540. if ((IsSessionLocked(ulConsoleSessionId)) ||
  7541. (!GetSessionUserToken(ulConsoleSessionId, &hUserToken))) {
  7542. //
  7543. // If the console session is locked or no user is logged in, supply no
  7544. // user token, and verify strictly against the policy permissions
  7545. // required to eject the dock or device, absent a user.
  7546. //
  7547. hUserToken = NULL;
  7548. }
  7549. bResult = VerifyKernelInitiatedEjectPermissions(hUserToken, bDockDevice);
  7550. if (bResult == FALSE) {
  7551. if (ARGUMENT_PRESENT(VetoType)) {
  7552. *VetoType = PNP_VetoInsufficientRights;
  7553. }
  7554. if (ARGUMENT_PRESENT(VetoNameLength)) {
  7555. //
  7556. // VetoNameLength is in characters.
  7557. //
  7558. if (ARGUMENT_PRESENT(VetoName) && *VetoNameLength) {
  7559. *VetoName = UNICODE_NULL;
  7560. }
  7561. *VetoNameLength = 0;
  7562. }
  7563. }
  7564. if (hUserToken) {
  7565. CloseHandle(hUserToken);
  7566. }
  7567. return bResult;
  7568. } // CheckEjectPermissions
  7569. //---------------------------------------------------------------------------
  7570. // Private Utility Routines
  7571. //---------------------------------------------------------------------------
  7572. BOOL
  7573. GuidEqual(
  7574. CONST GUID UNALIGNED *Guid1,
  7575. CONST GUID UNALIGNED *Guid2
  7576. )
  7577. {
  7578. RPC_STATUS rpcStatus;
  7579. return UuidEqual((LPGUID)Guid1, (LPGUID)Guid2, &rpcStatus);
  7580. } // GuidEqual
  7581. VOID
  7582. LogErrorEvent(
  7583. DWORD dwEventID,
  7584. DWORD dwError,
  7585. WORD nStrings,
  7586. ...
  7587. )
  7588. {
  7589. HANDLE hEventLog;
  7590. LPTSTR *paStrings;
  7591. va_list pArg;
  7592. DWORD index;
  7593. hEventLog = RegisterEventSource(NULL, TEXT("PlugPlayManager"));
  7594. if (hEventLog == NULL) {
  7595. return;
  7596. }
  7597. if (nStrings) {
  7598. paStrings = HeapAlloc(ghPnPHeap, 0, nStrings * sizeof(LPTSTR));
  7599. if (paStrings != NULL) {
  7600. va_start(pArg, nStrings);
  7601. for (index = 0; index < nStrings; index++) {
  7602. paStrings[index] = va_arg(pArg, LPTSTR);
  7603. }
  7604. va_end(pArg);
  7605. ReportEvent( hEventLog,
  7606. EVENTLOG_ERROR_TYPE,
  7607. 0, // wCategory
  7608. dwEventID, // dwEventID
  7609. NULL, // lpUserSID
  7610. nStrings, // wNumStrings
  7611. sizeof(dwError), // dwDataSize
  7612. paStrings, // lpStrings
  7613. &dwError); // lpRawData
  7614. HeapFree(ghPnPHeap, 0, paStrings);
  7615. }
  7616. } else {
  7617. ReportEvent( hEventLog,
  7618. EVENTLOG_ERROR_TYPE,
  7619. 0, // wCategory
  7620. dwEventID, // dwEventID
  7621. NULL, // lpUserSID
  7622. 0, // wNumStrings
  7623. sizeof(dwError), // dwDataSize
  7624. NULL, // lpStrings
  7625. &dwError); // lpRawData
  7626. }
  7627. DeregisterEventSource(hEventLog);
  7628. }
  7629. VOID
  7630. LogWarningEvent(
  7631. DWORD dwEventID,
  7632. WORD nStrings,
  7633. ...
  7634. )
  7635. {
  7636. HANDLE hEventLog;
  7637. LPTSTR *paStrings;
  7638. va_list pArg;
  7639. DWORD index;
  7640. hEventLog = RegisterEventSource(NULL, TEXT("PlugPlayManager"));
  7641. if (hEventLog == NULL) {
  7642. return;
  7643. }
  7644. paStrings = HeapAlloc(ghPnPHeap, 0, nStrings * sizeof(LPTSTR));
  7645. if (paStrings != NULL) {
  7646. va_start(pArg, nStrings);
  7647. for (index = 0; index < nStrings; index++) {
  7648. paStrings[index] = va_arg(pArg, LPTSTR);
  7649. }
  7650. va_end(pArg);
  7651. ReportEvent( hEventLog,
  7652. EVENTLOG_WARNING_TYPE,
  7653. 0, // wCategory
  7654. dwEventID, // dwEventID
  7655. NULL, // lpUserSID
  7656. nStrings, // wNumStrings
  7657. 0, // dwDataSize
  7658. paStrings, // lpStrings
  7659. NULL); // lpRawData
  7660. HeapFree(ghPnPHeap, 0, paStrings);
  7661. }
  7662. DeregisterEventSource(hEventLog);
  7663. }
  7664. BOOL
  7665. LockNotifyList(
  7666. IN LOCKINFO *Lock
  7667. )
  7668. {
  7669. return LockPrivateResource(Lock);
  7670. }
  7671. VOID
  7672. UnlockNotifyList(
  7673. IN LOCKINFO *Lock
  7674. )
  7675. {
  7676. UnlockPrivateResource(Lock);
  7677. }
  7678. PPNP_NOTIFY_LIST
  7679. GetNotifyListForEntry(
  7680. IN PPNP_NOTIFY_ENTRY Entry
  7681. )
  7682. /*++
  7683. Routine Description:
  7684. This routine retrives the notification list that the given entry is in,
  7685. based on the list entry signature. If this entry has been removed from a
  7686. notification list (via DeleteNotifyEntry), NULL is returned.
  7687. Arguments:
  7688. Entry - Specifies a notification entry for the coresponding notification
  7689. list is to be found.
  7690. Return Value:
  7691. Returns the notification list this entry is a member of, or NULL if the
  7692. entry is not in any notification list.
  7693. --*/
  7694. {
  7695. PPNP_NOTIFY_LIST notifyList;
  7696. if (!Entry) {
  7697. return NULL;
  7698. }
  7699. //
  7700. // Retrieve the list pointer from the entry signature.
  7701. // The signature contains two pieces of data.
  7702. //
  7703. // It is a ULONG, with byte 0 being a list index and
  7704. // bytes 1,2,3 being the signature
  7705. // We mask and compare the top 3 bytes to find which list
  7706. // then return the address of the list to lock based on the
  7707. // index in the bottom byte.
  7708. //
  7709. switch (Entry->Signature & LIST_ENTRY_SIGNATURE_MASK) {
  7710. case TARGET_ENTRY_SIGNATURE:
  7711. notifyList = &TargetList[Entry->Signature & LIST_ENTRY_INDEX_MASK];
  7712. break;
  7713. case CLASS_ENTRY_SIGNATURE:
  7714. notifyList = &ClassList[Entry->Signature & LIST_ENTRY_INDEX_MASK];
  7715. break;
  7716. case SERVICE_ENTRY_SIGNATURE:
  7717. notifyList = &ServiceList[Entry->Signature & LIST_ENTRY_INDEX_MASK];
  7718. break;
  7719. case 0:
  7720. //
  7721. // If the entry Signature is 0, this entry has been removed from it's
  7722. // notification list.
  7723. //
  7724. notifyList = NULL;
  7725. break;
  7726. default:
  7727. //
  7728. // Should never get here!
  7729. //
  7730. ASSERT (FALSE);
  7731. notifyList = NULL;
  7732. break;
  7733. }
  7734. return notifyList;
  7735. } // GetNotifyListForEntry
  7736. BOOL
  7737. DeleteNotifyEntry(
  7738. IN PPNP_NOTIFY_ENTRY Entry,
  7739. IN BOOLEAN RpcNotified
  7740. )
  7741. /*++
  7742. Routine Description:
  7743. This routine removes an entry from a notification list and frees the
  7744. memory for that entry.
  7745. Arguments:
  7746. Entry - Specifies an entry in one of the notification lists that is
  7747. to be deleted.
  7748. Return Value:
  7749. Returns TRUE or FALSE.
  7750. --*/
  7751. {
  7752. PPNP_NOTIFY_ENTRY previousEntry = Entry->Previous;
  7753. if (!(Entry->Freed & DEFER_NOTIFY_FREE)) {
  7754. if (previousEntry == NULL) {
  7755. return FALSE;
  7756. }
  7757. //
  7758. // hook up the forward and backwards pointers
  7759. //
  7760. previousEntry->Next = Entry->Next;
  7761. if (Entry->Next) {
  7762. ((PPNP_NOTIFY_ENTRY)(Entry->Next))->Previous = previousEntry;
  7763. }
  7764. //
  7765. // Clear the entry signature now that it is no longer part of any list.
  7766. //
  7767. Entry->Signature = 0;
  7768. }
  7769. if (RpcNotified || (Entry->Freed & DEFER_NOTIFY_FREE)) {
  7770. if (Entry->ClientName) {
  7771. HeapFree (ghPnPHeap,0,Entry->ClientName);
  7772. Entry->ClientName = NULL;
  7773. }
  7774. HeapFree(ghPnPHeap, 0, Entry);
  7775. }else {
  7776. //
  7777. //Let the entry dangle until the RPC rundown
  7778. //
  7779. Entry->Freed |= DEFER_NOTIFY_FREE;
  7780. }
  7781. return TRUE;
  7782. } // DeleteNotifyEntry;
  7783. VOID
  7784. AddNotifyEntry(
  7785. IN PPNP_NOTIFY_LIST NotifyList,
  7786. IN PPNP_NOTIFY_ENTRY NewEntry
  7787. )
  7788. /*++
  7789. Routine Description:
  7790. This routine inserts an entry at the tail of a notification list.
  7791. Arguments:
  7792. Entry - Specifies an entry to be added to a notification list
  7793. Return Value:
  7794. None.
  7795. --*/
  7796. {
  7797. PPNP_NOTIFY_ENTRY previousEntry = NULL, currentEntry = NULL;
  7798. //
  7799. // Skip to the last entry in this list.
  7800. //
  7801. previousEntry = (PPNP_NOTIFY_ENTRY)NotifyList;
  7802. currentEntry = previousEntry->Next;
  7803. while (currentEntry) {
  7804. previousEntry = currentEntry;
  7805. currentEntry = currentEntry->Next;
  7806. }
  7807. //
  7808. // Attach this entry to the end of the list.
  7809. //
  7810. previousEntry->Next = NewEntry;
  7811. NewEntry->Previous = previousEntry;
  7812. NewEntry->Next = NULL;
  7813. return;
  7814. } // AddNotifyEntry;
  7815. PPNP_NOTIFY_ENTRY
  7816. GetNextNotifyEntry(
  7817. IN PPNP_NOTIFY_ENTRY Entry,
  7818. IN DWORD Flags
  7819. )
  7820. /*++
  7821. Routine Description:
  7822. Returns the next entry in the notification list for the entry specified, in
  7823. the direction specified by the Flags.
  7824. Arguments:
  7825. Entry - Specified a notification list entry.
  7826. Flags - Specifies BSF_* flags indicating the direction the list is to be
  7827. traversed. If BSF_QUERY is specified, the previous list entry is
  7828. returned, otherwise returns the next entry forward in the list.
  7829. Return Value:
  7830. Returns the next entry in the notification list, or NULL if no such entry
  7831. exists.
  7832. --*/
  7833. {
  7834. PPNP_NOTIFY_ENTRY nextEntry = NULL;
  7835. if (Entry == NULL) {
  7836. return Entry;
  7837. }
  7838. //
  7839. // Determine if this is a QUERY (or a resume). In which case
  7840. // we go back -> front.
  7841. //
  7842. if (Flags & BSF_QUERY) {
  7843. nextEntry = Entry->Previous;
  7844. //
  7845. // If the previous entry is the list head, there is no next entry.
  7846. //
  7847. if ((nextEntry == NULL) ||
  7848. (nextEntry->Previous == NULL)) {
  7849. return NULL;
  7850. }
  7851. } else {
  7852. nextEntry = Entry->Next;
  7853. }
  7854. return nextEntry;
  7855. }
  7856. PPNP_NOTIFY_ENTRY
  7857. GetFirstNotifyEntry(
  7858. IN PPNP_NOTIFY_LIST List,
  7859. IN DWORD Flags
  7860. )
  7861. /*++
  7862. Routine Description:
  7863. Returns the first entry in the specified notification list, starting from
  7864. the direction specified by the Flags.
  7865. Arguments:
  7866. List - Specified a notification list.
  7867. Flags - Specifies BSF_* flags indicating the end of the list from which the
  7868. first entry is to be retrieved. If BSF_QUERY is specified, the last
  7869. list entry is returned, otherwise returns the first entry in the
  7870. list.
  7871. Return Value:
  7872. Returns the first entry in the notification list, or NULL if no such entry
  7873. exists.
  7874. --*/
  7875. {
  7876. PPNP_NOTIFY_ENTRY previousEntry = NULL, currentEntry = NULL, firstEntry = NULL;
  7877. //
  7878. // Determine if this is a QUERY (or a resume). In which case
  7879. // we go back -> front.
  7880. //
  7881. if (Flags & BSF_QUERY) {
  7882. //
  7883. // Skip to the last entry in this list.
  7884. //
  7885. previousEntry = (PPNP_NOTIFY_ENTRY)List;
  7886. currentEntry = previousEntry->Next;
  7887. while (currentEntry) {
  7888. previousEntry = currentEntry;
  7889. currentEntry = currentEntry->Next;
  7890. }
  7891. if (!previousEntry->Previous) {
  7892. //
  7893. // If the list is empty, there is no first entry.
  7894. //
  7895. firstEntry = NULL;
  7896. } else {
  7897. firstEntry = previousEntry;
  7898. }
  7899. } else {
  7900. firstEntry = (PPNP_NOTIFY_ENTRY)List->Next;
  7901. }
  7902. return firstEntry;
  7903. }
  7904. ULONG
  7905. HashString(
  7906. IN LPWSTR String,
  7907. IN ULONG Buckets
  7908. )
  7909. /*++
  7910. Routine Description:
  7911. This routine performs a quick and dirty hash of a unicode string.
  7912. Arguments:
  7913. String - Null-terminated unicode string to perform hash on.
  7914. Buckets - Number of hashing buckets.
  7915. Return Value:
  7916. Returns a hash value between 0 and Buckets.
  7917. --*/
  7918. {
  7919. LPWSTR p = String;
  7920. ULONG hash = 0;
  7921. while (*p) {
  7922. hash ^= *p;
  7923. p++;
  7924. }
  7925. hash = hash % Buckets;
  7926. return hash;
  7927. } // HashString
  7928. DWORD
  7929. MapQueryEventToCancelEvent(
  7930. IN DWORD QueryEventId
  7931. )
  7932. /*++
  7933. Routine Description:
  7934. This routine maps a query device event id (such as query remove) to the
  7935. corresponding cancel device event id (such as cancel remove). The event
  7936. ids are based on DBT_Xxx values from DBT.H.
  7937. Arguments:
  7938. QueryEventId - A DBT_Xxx query type device event id.
  7939. Return Value:
  7940. Returns the corresponding cancel device event id or -1 if it fails.
  7941. --*/
  7942. {
  7943. DWORD cancelEventId;
  7944. switch (QueryEventId) {
  7945. case DBT_QUERYCHANGECONFIG:
  7946. cancelEventId = DBT_CONFIGCHANGECANCELED;
  7947. break;
  7948. case DBT_DEVICEQUERYREMOVE:
  7949. cancelEventId = DBT_DEVICEQUERYREMOVEFAILED;
  7950. break;
  7951. case PBT_APMQUERYSUSPEND:
  7952. cancelEventId = PBT_APMQUERYSUSPENDFAILED;
  7953. break;
  7954. case PBT_APMQUERYSTANDBY:
  7955. cancelEventId = PBT_APMQUERYSTANDBYFAILED;
  7956. default:
  7957. cancelEventId = -1;
  7958. break;
  7959. }
  7960. return cancelEventId;
  7961. } // MapQueryEventToCancelEvent
  7962. VOID
  7963. FixUpDeviceId(
  7964. IN OUT LPTSTR DeviceId
  7965. )
  7966. /*++
  7967. Routine Description:
  7968. This routine copies a device id, fixing it up as it does the copy.
  7969. 'Fixing up' means that the string is made upper-case, and that the
  7970. following character ranges are turned into underscores (_):
  7971. c <= 0x20 (' ')
  7972. c > 0x7F
  7973. c == 0x2C (',')
  7974. (NOTE: This algorithm is also implemented in the Config Manager APIs,
  7975. and must be kept in sync with that routine. To maintain device identifier
  7976. compatibility, these routines must work the same as Win95.)
  7977. Arguments:
  7978. Return Value:
  7979. None.
  7980. --*/
  7981. {
  7982. PTCHAR p;
  7983. CharUpper(DeviceId);
  7984. p = DeviceId;
  7985. while (*p) {
  7986. if ((*p <= TEXT(' ')) || (*p > (TCHAR)0x7F) || (*p == TEXT(','))) {
  7987. *p = TEXT('_');
  7988. }
  7989. p++;
  7990. }
  7991. } // FixUpDeviceId
  7992. BOOL
  7993. GetWindowsExeFileName(
  7994. IN HWND hWnd,
  7995. OUT LPWSTR lpszFileName,
  7996. IN OUT PULONG pulFileNameLength
  7997. )
  7998. /*++
  7999. Routine Description:
  8000. This routine retrieves the module file name for the process that the
  8001. specified window belongs to.
  8002. Arguments:
  8003. hWnd - Supplies the handle to the window whose process module
  8004. file name is to be retrieved.
  8005. lpszFileName - Supplies the address of a variable to receive, upon
  8006. success, the module file name of the window's process.
  8007. pulFileNameLength - Supplies the address of a variable specifying the size of
  8008. the of buffer specified by the lpszFileName parameter.
  8009. Upon success, this address will specify the length of
  8010. the string stored in that buffer by this routine.
  8011. Return Value:
  8012. Returns TRUE if the module file name was retrieved, FALSE otherwise.
  8013. Notes:
  8014. GetWindowThreadProcessId will fail unless UMPNPMGR has set the Desktop this
  8015. thread is executing on to the same as the application. Note that this is
  8016. only possible for Desktops in SessionId 0.
  8017. --*/
  8018. {
  8019. DWORD pidApp;
  8020. HANDLE hProcess;
  8021. HMODULE hPSAPI;
  8022. DWORD dwLength;
  8023. dwLength = 0;
  8024. if (GetWindowThreadProcessId(hWnd, &pidApp)) {
  8025. hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
  8026. FALSE,
  8027. pidApp);
  8028. if (hProcess != NULL) {
  8029. hPSAPI = LoadLibrary(TEXT("psapi.dll"));
  8030. if (hPSAPI != NULL) {
  8031. typedef DWORD (WINAPI *FP_GETMODULEFILENAMEEXW)(
  8032. HANDLE hProcess,
  8033. HMODULE hModule,
  8034. LPWSTR lpFilename,
  8035. DWORD nSize);
  8036. FP_GETMODULEFILENAMEEXW fpGetModuleFileNameExW;
  8037. fpGetModuleFileNameExW = (FP_GETMODULEFILENAMEEXW)
  8038. GetProcAddress(hPSAPI,
  8039. "GetModuleFileNameExW");
  8040. if (fpGetModuleFileNameExW != NULL) {
  8041. dwLength = fpGetModuleFileNameExW(hProcess,
  8042. NULL,
  8043. lpszFileName,
  8044. *pulFileNameLength);
  8045. }
  8046. FreeLibrary(hPSAPI);
  8047. }
  8048. CloseHandle(hProcess);
  8049. } else {
  8050. KdPrintEx((DPFLTR_PNPMGR_ID,
  8051. DBGF_ERRORS,
  8052. "UMPNPMGR: OpenProcess returned error = %d\n",
  8053. GetLastError()));
  8054. }
  8055. } else {
  8056. KdPrintEx((DPFLTR_PNPMGR_ID,
  8057. DBGF_ERRORS,
  8058. "UMPNPMGR: GetWindowThreadProcessId returned error = %d\n",
  8059. GetLastError()));
  8060. }
  8061. if ((dwLength == 0) && (*pulFileNameLength)) {
  8062. lpszFileName[0] = UNICODE_NULL;
  8063. }
  8064. *pulFileNameLength = dwLength;
  8065. return TRUE;
  8066. } // GetWindowsExeFileName
  8067. BOOL
  8068. InitializeHydraInterface(
  8069. VOID
  8070. )
  8071. /*++
  8072. Routine Description:
  8073. This routine loads the terminal services support libraries and locates
  8074. required function entrypoints.
  8075. Arguments:
  8076. None.
  8077. Return Value:
  8078. Returns TRUE if the terminal services support libraries were successfully
  8079. loaded, and entrypoints located.
  8080. --*/
  8081. {
  8082. BOOL Status = FALSE;
  8083. //
  8084. // Load the base library that contains the user message dispatch routines
  8085. // for Terminal Services.
  8086. //
  8087. ghWinStaLib = LoadLibrary(WINSTA_DLL);
  8088. if (!ghWinStaLib) {
  8089. return FALSE;
  8090. }
  8091. fpWinStationSendWindowMessage =
  8092. (FP_WINSTASENDWINDOWMESSAGE)GetProcAddress(
  8093. ghWinStaLib,
  8094. "WinStationSendWindowMessage");
  8095. fpWinStationBroadcastSystemMessage =
  8096. (FP_WINSTABROADCASTSYSTEMMESSAGE)GetProcAddress(
  8097. ghWinStaLib,
  8098. "WinStationBroadcastSystemMessage");
  8099. fpWinStationQueryInformationW =
  8100. (FP_WINSTAQUERYINFORMATIONW)GetProcAddress(
  8101. ghWinStaLib,
  8102. "WinStationQueryInformationW");
  8103. if (!fpWinStationSendWindowMessage ||
  8104. !fpWinStationBroadcastSystemMessage ||
  8105. !fpWinStationQueryInformationW) {
  8106. goto Clean0;
  8107. }
  8108. //
  8109. // Load the library that contains Terminal Services support routines.
  8110. //
  8111. ghWtsApi32Lib = LoadLibrary(WTSAPI32_DLL);
  8112. if (!ghWtsApi32Lib) {
  8113. goto Clean0;
  8114. }
  8115. fpWTSQuerySessionInformation =
  8116. (FP_WTSQUERYSESSIONINFORMATION)GetProcAddress(
  8117. ghWtsApi32Lib,
  8118. "WTSQuerySessionInformationW");
  8119. fpWTSFreeMemory =
  8120. (FP_WTSFREEMEMORY)GetProcAddress(
  8121. ghWtsApi32Lib,
  8122. "WTSFreeMemory");
  8123. if (!fpWTSQuerySessionInformation ||
  8124. !fpWTSFreeMemory) {
  8125. goto Clean0;
  8126. }
  8127. Status = TRUE;
  8128. Clean0:
  8129. ASSERT(Status == TRUE);
  8130. if (!Status) {
  8131. //
  8132. // Something failed. Unload all libraries.
  8133. //
  8134. fpWinStationSendWindowMessage = NULL;
  8135. fpWinStationBroadcastSystemMessage = NULL;
  8136. fpWinStationQueryInformationW = NULL;
  8137. if (ghWinStaLib) {
  8138. FreeLibrary(ghWinStaLib);
  8139. ghWinStaLib = NULL;
  8140. }
  8141. fpWTSQuerySessionInformation = NULL;
  8142. fpWTSFreeMemory = NULL;
  8143. if (ghWtsApi32Lib) {
  8144. FreeLibrary(ghWtsApi32Lib);
  8145. ghWtsApi32Lib = NULL;
  8146. }
  8147. }
  8148. return Status;
  8149. } // InitializeHydraInterface
  8150. BOOL
  8151. GetClientName(
  8152. IN PPNP_NOTIFY_ENTRY entry,
  8153. OUT LPWSTR lpszClientName,
  8154. IN OUT PULONG pulClientNameLength
  8155. )
  8156. /*++
  8157. Routine Description:
  8158. This routine retrieves the client name for the specified notification list
  8159. entry.
  8160. Arguments:
  8161. entry - Specifies a notification list entry.
  8162. lpszClientName - Supplies the address of a variable to receive, the
  8163. client name of the window's process.
  8164. pulClientrNameLength - Supplies the address of a variable specifying the size of
  8165. the of buffer specified by the lpszFileName parameter.
  8166. Upon return, this address will specify the length of
  8167. the string stored in that buffer by this routine.
  8168. Return Value:
  8169. Returns TRUE.
  8170. --*/
  8171. {
  8172. DWORD dwLength;
  8173. dwLength = lstrlen(entry->ClientName)+1;
  8174. ASSERT (dwLength <= (*pulClientNameLength));
  8175. dwLength = min((*pulClientNameLength), dwLength);
  8176. lstrcpyn(lpszClientName,
  8177. entry->ClientName,
  8178. dwLength);
  8179. *pulClientNameLength = dwLength-1;
  8180. return TRUE;
  8181. } // GetClientName
  8182. void __RPC_USER
  8183. PNP_NOTIFICATION_CONTEXT_rundown(
  8184. PPNP_NOTIFICATION_CONTEXT hEntry
  8185. )
  8186. /*++
  8187. Routine Description:
  8188. Rundown routine for RPC. This will get called if a client/server pipe
  8189. breaks without unregistering a notification. If a notification is in
  8190. progress when rundown is called, the entry is kept in a deferred list, and
  8191. this routines is explicitly called again for the deferred entry, after
  8192. notification is complete.
  8193. This routine frees the memory associated with the notification entry that is
  8194. no longer needed.
  8195. Arguments:
  8196. hEntry - Specifies a notification entry for which RPC has requested rundown.
  8197. Return Value:
  8198. None.
  8199. --*/
  8200. {
  8201. PPNP_NOTIFY_LIST notifyList;
  8202. PPNP_NOTIFY_ENTRY node;
  8203. PPNP_DEFERRED_LIST rundownNode;
  8204. BOOLEAN bLocked = FALSE;
  8205. KdPrintEx((DPFLTR_PNPMGR_ID,
  8206. DBGF_WARNINGS | DBGF_EVENT,
  8207. "UMPNPMGR: Cleaning up broken pipe\n"));
  8208. try {
  8209. EnterCriticalSection(&RegistrationCS);
  8210. node = (PPNP_NOTIFY_ENTRY) hEntry;
  8211. if (gNotificationInProg != 0) {
  8212. //
  8213. // Before freeing the entry, we need to make sure that it's not sitting
  8214. // around in the deferred RegisterList or UnregisterList.
  8215. //
  8216. if (RegisterList != NULL) {
  8217. //
  8218. // Check to see if this entry is in the deferred RegisterList.
  8219. //
  8220. PPNP_DEFERRED_LIST currReg,prevReg;
  8221. currReg = RegisterList;
  8222. prevReg = NULL;
  8223. while (currReg) {
  8224. ASSERT(currReg->Entry->Unregistered);
  8225. if (currReg->Entry == node) {
  8226. //
  8227. // Remove this entry from the deferred RegisterList.
  8228. //
  8229. if (prevReg) {
  8230. prevReg->Next = currReg->Next;
  8231. } else {
  8232. RegisterList = currReg->Next;
  8233. }
  8234. HeapFree(ghPnPHeap, 0, currReg);
  8235. if (prevReg) {
  8236. currReg = prevReg->Next;
  8237. } else {
  8238. currReg = RegisterList;
  8239. }
  8240. } else {
  8241. prevReg = currReg;
  8242. currReg = currReg->Next;
  8243. }
  8244. }
  8245. }
  8246. if (UnregisterList != NULL) {
  8247. //
  8248. // Check to see if this entry is in the deferred UnregisterList.
  8249. //
  8250. PPNP_DEFERRED_LIST currUnreg,prevUnreg;
  8251. currUnreg = UnregisterList;
  8252. prevUnreg = NULL;
  8253. while (currUnreg) {
  8254. ASSERT(currUnreg->Entry->Unregistered);
  8255. if (currUnreg->Entry == node) {
  8256. //
  8257. // Remove this entry from the deferred UnregisterList.
  8258. //
  8259. if (prevUnreg) {
  8260. prevUnreg->Next = currUnreg->Next;
  8261. } else {
  8262. UnregisterList = currUnreg->Next;
  8263. }
  8264. HeapFree(ghPnPHeap, 0, currUnreg);
  8265. if (prevUnreg) {
  8266. currUnreg = prevUnreg->Next;
  8267. } else {
  8268. currUnreg = UnregisterList;
  8269. }
  8270. } else {
  8271. prevUnreg = currUnreg;
  8272. currUnreg = currUnreg->Next;
  8273. }
  8274. }
  8275. }
  8276. //
  8277. // If the entry to be rundown is part of a notification list, make
  8278. // sure it does not get notified.
  8279. //
  8280. notifyList = GetNotifyListForEntry(node);
  8281. if (notifyList) {
  8282. LockNotifyList(&notifyList->Lock);
  8283. bLocked = TRUE;
  8284. node->Unregistered = TRUE;
  8285. UnlockNotifyList(&notifyList->Lock);
  8286. bLocked = FALSE;
  8287. }
  8288. //
  8289. // Delay rundown of this entry until after the notification in
  8290. // progress is complete.
  8291. //
  8292. rundownNode = (PPNP_DEFERRED_LIST)
  8293. HeapAlloc(ghPnPHeap,
  8294. 0,
  8295. sizeof (PNP_DEFERRED_LIST));
  8296. if (!rundownNode) {
  8297. KdPrintEx((DPFLTR_PNPMGR_ID,
  8298. DBGF_ERRORS | DBGF_WARNINGS,
  8299. "UMPNPMGR: Error allocating deferred list entry during RPC rundown!\n"));
  8300. goto Clean0;
  8301. }
  8302. rundownNode->hBinding = 0;
  8303. rundownNode->Entry = node;
  8304. rundownNode->Next = RundownList;
  8305. RundownList = rundownNode;
  8306. } else {
  8307. if (!(node->Freed & DEFER_NOTIFY_FREE)) {
  8308. //
  8309. // This entry is still in a notification list.
  8310. //
  8311. notifyList = GetNotifyListForEntry(node);
  8312. ASSERT(notifyList);
  8313. if (notifyList) {
  8314. //
  8315. // Lock the notification list and delete this entry.
  8316. //
  8317. LockNotifyList (&notifyList->Lock);
  8318. bLocked = TRUE;
  8319. }
  8320. node->Freed |= (PNP_UNREG_FREE|PNP_UNREG_RUNDOWN);
  8321. DeleteNotifyEntry (node,TRUE);
  8322. if (notifyList) {
  8323. UnlockNotifyList (&notifyList->Lock);
  8324. bLocked = FALSE;
  8325. }
  8326. } else {
  8327. //
  8328. // This node has been removed from the list, and should just be deleted
  8329. //
  8330. DeleteNotifyEntry (node,TRUE);
  8331. }
  8332. }
  8333. Clean0:
  8334. LeaveCriticalSection(&RegistrationCS);
  8335. } except (EXCEPTION_EXECUTE_HANDLER) {
  8336. KdPrintEx((DPFLTR_PNPMGR_ID,
  8337. DBGF_ERRORS | DBGF_WARNINGS,
  8338. "UMPNPMGR: Exception during PNP_NOTIFICATION_CONTEXT_rundown!\n"));
  8339. ASSERT(0);
  8340. if (bLocked) {
  8341. UnlockNotifyList (&notifyList->Lock);
  8342. }
  8343. LeaveCriticalSection(&RegistrationCS);
  8344. }
  8345. return;
  8346. } // PNP_NOTIFICATION_CONTEXT_rundown
  8347. DWORD
  8348. LoadDeviceInstaller(
  8349. VOID
  8350. )
  8351. /*++
  8352. Routine Description:
  8353. This routine loads setupapi.dll and retrieves the necessary device install
  8354. entrypoints. It also creates two named events used to communicate with the
  8355. client-side UI in the case where there's a user logged in.
  8356. If setupapi.dll is already loaded, it simply returns success.
  8357. Arguments:
  8358. None
  8359. Return Value:
  8360. If successful, NO_ERROR is returned. Otherwise, a Win32 error code is
  8361. returned indicating the cause of failure.
  8362. --*/
  8363. {
  8364. DWORD Err = NO_ERROR;
  8365. DWORD SetupGlobalFlags;
  8366. if(ghDeviceInstallerLib) {
  8367. return NO_ERROR;
  8368. }
  8369. ghDeviceInstallerLib = LoadLibrary(SETUPAPI_DLL);
  8370. if(!ghDeviceInstallerLib) {
  8371. return GetLastError();
  8372. }
  8373. try {
  8374. if(!(fpCreateDeviceInfoList = (FP_CREATEDEVICEINFOLIST)GetProcAddress(
  8375. ghDeviceInstallerLib,
  8376. "SetupDiCreateDeviceInfoList"))) {
  8377. goto HitFailure;
  8378. }
  8379. if(!(fpOpenDeviceInfo = (FP_OPENDEVICEINFO)GetProcAddress(
  8380. ghDeviceInstallerLib,
  8381. "SetupDiOpenDeviceInfoW"))) {
  8382. goto HitFailure;
  8383. }
  8384. if(!(fpBuildDriverInfoList = (FP_BUILDDRIVERINFOLIST)GetProcAddress(
  8385. ghDeviceInstallerLib,
  8386. "SetupDiBuildDriverInfoList"))) {
  8387. goto HitFailure;
  8388. }
  8389. if(!(fpDestroyDeviceInfoList = (FP_DESTROYDEVICEINFOLIST)GetProcAddress(
  8390. ghDeviceInstallerLib,
  8391. "SetupDiDestroyDeviceInfoList"))) {
  8392. goto HitFailure;
  8393. }
  8394. if(!(fpCallClassInstaller = (FP_CALLCLASSINSTALLER)GetProcAddress(
  8395. ghDeviceInstallerLib,
  8396. "SetupDiCallClassInstaller"))) {
  8397. goto HitFailure;
  8398. }
  8399. if(!(fpInstallClass = (FP_INSTALLCLASS)GetProcAddress(
  8400. ghDeviceInstallerLib,
  8401. "SetupDiInstallClassW"))) {
  8402. goto HitFailure;
  8403. }
  8404. if(!(fpGetSelectedDriver = (FP_GETSELECTEDDRIVER)GetProcAddress(
  8405. ghDeviceInstallerLib,
  8406. "SetupDiGetSelectedDriverW"))) {
  8407. goto HitFailure;
  8408. }
  8409. if(!(fpGetDriverInfoDetail = (FP_GETDRIVERINFODETAIL)GetProcAddress(
  8410. ghDeviceInstallerLib,
  8411. "SetupDiGetDriverInfoDetailW"))) {
  8412. goto HitFailure;
  8413. }
  8414. if(!(fpGetDeviceInstallParams = (FP_GETDEVICEINSTALLPARAMS)GetProcAddress(
  8415. ghDeviceInstallerLib,
  8416. "SetupDiGetDeviceInstallParamsW"))) {
  8417. goto HitFailure;
  8418. }
  8419. if(!(fpSetDeviceInstallParams = (FP_SETDEVICEINSTALLPARAMS)GetProcAddress(
  8420. ghDeviceInstallerLib,
  8421. "SetupDiSetDeviceInstallParamsW"))) {
  8422. goto HitFailure;
  8423. }
  8424. if(!(fpGetDriverInstallParams = (FP_GETDRIVERINSTALLPARAMS)GetProcAddress(
  8425. ghDeviceInstallerLib,
  8426. "SetupDiGetDriverInstallParamsW"))) {
  8427. goto HitFailure;
  8428. }
  8429. if(!(fpSetClassInstallParams = (FP_SETCLASSINSTALLPARAMS)GetProcAddress(
  8430. ghDeviceInstallerLib,
  8431. "SetupDiSetClassInstallParamsW"))) {
  8432. goto HitFailure;
  8433. }
  8434. if(!(fpGetClassInstallParams = (FP_GETCLASSINSTALLPARAMS)GetProcAddress(
  8435. ghDeviceInstallerLib,
  8436. "SetupDiGetClassInstallParamsW"))) {
  8437. goto HitFailure;
  8438. }
  8439. if(!(fpOpenInfFile = (FP_OPENINFFILE)GetProcAddress(
  8440. ghDeviceInstallerLib,
  8441. "SetupOpenInfFileW"))) {
  8442. goto HitFailure;
  8443. }
  8444. if(!(fpCloseInfFile = (FP_CLOSEINFFILE)GetProcAddress(
  8445. ghDeviceInstallerLib,
  8446. "SetupCloseInfFile"))) {
  8447. goto HitFailure;
  8448. }
  8449. if(!(fpFindFirstLine = (FP_FINDFIRSTLINE)GetProcAddress(
  8450. ghDeviceInstallerLib,
  8451. "SetupFindFirstLineW"))) {
  8452. goto HitFailure;
  8453. }
  8454. if(!(fpFindNextMatchLine = (FP_FINDNEXTMATCHLINE)GetProcAddress(
  8455. ghDeviceInstallerLib,
  8456. "SetupFindNextMatchLineW"))) {
  8457. goto HitFailure;
  8458. }
  8459. if(!(fpGetStringField = (FP_GETSTRINGFIELD)GetProcAddress(
  8460. ghDeviceInstallerLib,
  8461. "SetupGetStringFieldW"))) {
  8462. goto HitFailure;
  8463. }
  8464. if(!(fpSetGlobalFlags = (FP_SETGLOBALFLAGS)GetProcAddress(
  8465. ghDeviceInstallerLib,
  8466. "pSetupSetGlobalFlags"))) {
  8467. goto HitFailure;
  8468. }
  8469. if(!(fpGetGlobalFlags = (FP_GETGLOBALFLAGS)GetProcAddress(
  8470. ghDeviceInstallerLib,
  8471. "pSetupGetGlobalFlags"))) {
  8472. goto HitFailure;
  8473. }
  8474. if(!(fpAccessRunOnceNodeList = (FP_ACCESSRUNONCENODELIST)GetProcAddress(
  8475. ghDeviceInstallerLib,
  8476. "pSetupAccessRunOnceNodeList"))) {
  8477. goto HitFailure;
  8478. }
  8479. if(!(fpDestroyRunOnceNodeList = (FP_DESTROYRUNONCENODELIST)GetProcAddress(
  8480. ghDeviceInstallerLib,
  8481. "pSetupDestroyRunOnceNodeList"))) {
  8482. goto HitFailure;
  8483. }
  8484. //
  8485. // Now configure setupapi for server-side installation
  8486. //
  8487. SetupGlobalFlags = fpGetGlobalFlags();
  8488. //
  8489. // We want to run non-interactive and do RunOnce entries server-side
  8490. //
  8491. SetupGlobalFlags |= (PSPGF_NONINTERACTIVE | PSPGF_SERVER_SIDE_RUNONCE);
  8492. //
  8493. // Make sure we _aren't_ skipping backup--it is essential that we be
  8494. // able to completely back-out of an installation half-way through if
  8495. // we encounter a failure (e.g., an unsigned file).
  8496. //
  8497. SetupGlobalFlags &= ~PSPGF_NO_BACKUP;
  8498. fpSetGlobalFlags(SetupGlobalFlags);
  8499. //
  8500. // If we get to here, we succeeded.
  8501. //
  8502. goto clean0;
  8503. HitFailure:
  8504. //
  8505. // Failed to retrieve some entrypoint.
  8506. //
  8507. Err = GetLastError();
  8508. clean0:
  8509. NOTHING;
  8510. } except(EXCEPTION_EXECUTE_HANDLER) {
  8511. KdPrintEx((DPFLTR_PNPMGR_ID,
  8512. DBGF_ERRORS | DBGF_WARNINGS,
  8513. "UMPNPMGR: Exception during LoadDeviceInstaller!\n"));
  8514. ASSERT(0);
  8515. Err = ERROR_INVALID_DATA;
  8516. }
  8517. if(Err != NO_ERROR) {
  8518. KdPrintEx((DPFLTR_PNPMGR_ID,
  8519. DBGF_INSTALL | DBGF_ERRORS,
  8520. "UMPNPMGR: failed to load device installer, error = %d\n",
  8521. Err));
  8522. FreeLibrary(ghDeviceInstallerLib);
  8523. ghDeviceInstallerLib = NULL;
  8524. } else {
  8525. KdPrintEx((DPFLTR_PNPMGR_ID,
  8526. DBGF_INSTALL,
  8527. "UMPNPMGR: Loaded device installer\n",
  8528. Err));
  8529. }
  8530. return Err;
  8531. } // LoadDeviceInstaller
  8532. VOID
  8533. UnloadDeviceInstaller(
  8534. VOID
  8535. )
  8536. /*++
  8537. Routine Description:
  8538. This unloads setupapi.dll if it's presently loaded.
  8539. Arguments:
  8540. None.
  8541. Return Value:
  8542. None.
  8543. --*/
  8544. {
  8545. PINSTALL_CLIENT_ENTRY pDeviceInstallClient, pNextDeviceInstallClient;
  8546. //
  8547. // Unload setupapi.dll.
  8548. //
  8549. if(ghDeviceInstallerLib) {
  8550. FreeLibrary(ghDeviceInstallerLib);
  8551. ghDeviceInstallerLib = NULL;
  8552. KdPrintEx((DPFLTR_PNPMGR_ID,
  8553. DBGF_INSTALL,
  8554. "UMPNPMGR: Unloaded device installer\n"));
  8555. }
  8556. //
  8557. // Close any device install clients that exist.
  8558. //
  8559. LockNotifyList(&InstallClientList.Lock);
  8560. pDeviceInstallClient = InstallClientList.Next;
  8561. while (pDeviceInstallClient) {
  8562. ASSERT(pDeviceInstallClient->RefCount == 1);
  8563. pNextDeviceInstallClient = pDeviceInstallClient->Next;
  8564. DereferenceDeviceInstallClient(pDeviceInstallClient);
  8565. pDeviceInstallClient = pNextDeviceInstallClient;
  8566. }
  8567. UnlockNotifyList(&InstallClientList.Lock);
  8568. return;
  8569. } // UnloadDeviceInstaller
  8570. DWORD
  8571. InstallDeviceServerSide(
  8572. IN LPWSTR pszDeviceId,
  8573. IN OUT PBOOL RebootRequired,
  8574. IN OUT PBOOL DeviceHasProblem,
  8575. IN OUT PULONG SessionId,
  8576. IN ULONG Flags
  8577. )
  8578. /*++
  8579. Routine Description:
  8580. This routine attempts to install the specified device in the context of
  8581. umpnpmgr (i.e., on the server-side of the ConfigMgr interface).
  8582. Arguments:
  8583. pszDeviceId - device instance ID of the devnode to be installed.
  8584. RebootRequired - Supplies the address of a boolean variable that will be
  8585. set to TRUE if the (successful) installation of this device requires a
  8586. reboot. Note, the existing value of this variable is preserved if
  8587. either (a) the installation fails or (b) no reboot was required.
  8588. DeviceHasProblem - Supplies the address of a boolean variable that will be
  8589. set to TRUE if the device has a CM_PROB_Xxx code after the drivers
  8590. were installed. Note, this value is only set if the installation
  8591. succeedes.
  8592. SessionId - Supplies the address of a variable containing the SessionId on
  8593. which the device install client is to be displayed. If successful, the
  8594. SessionId will contain the id of the session in which the device install
  8595. client UI process was launched. Otherwise, will contain an invalid
  8596. session id INVALID_SESSION, (0xFFFFFFFF).
  8597. Flags - Specifies flags describing the behavior of the device install client.
  8598. The following flags are currently defined:
  8599. DEVICE_INSTALL_DISPLAY_ON_CONSOLE - if specified, the value in the
  8600. SessionId variable will be ignored, and the device installclient will
  8601. always be displayed on the current active console session.
  8602. Return Value:
  8603. If the device installation was successful, the return value is NO_ERROR.
  8604. Otherwise, the return value is a Win32 error code indicating the cause of
  8605. failure.
  8606. --*/
  8607. {
  8608. DWORD Err;
  8609. HDEVINFO DeviceInfoSet;
  8610. SP_DEVINFO_DATA DeviceInfoData;
  8611. LPWSTR pszClassGuid;
  8612. WCHAR szBuffer[MAX_PATH];
  8613. HKEY hKey;
  8614. SP_DEVINSTALL_PARAMS DeviceInstallParams;
  8615. BOOL b, bDoClientUI = FALSE;
  8616. LPWSTR p;
  8617. SP_DRVINSTALL_PARAMS DriverInstallParams;
  8618. SP_DRVINFO_DATA DriverInfoData;
  8619. ULONG ulType;
  8620. ULONG ulSize;
  8621. ULONG DeviceIdSize;
  8622. DWORD Capabilities;
  8623. SP_NEWDEVICEWIZARD_DATA NewDevWizData;
  8624. BOOL RemoveNewDevDescValue = FALSE;
  8625. PSP_DRVINFO_DETAIL_DATA pDriverInfoDetailData = NULL;
  8626. DWORD DriverInfoDetailDataSize;
  8627. HINF hInf;
  8628. INFCONTEXT InfContext;
  8629. DWORD i, dwWait;
  8630. HANDLE hFinishEvents[3] = { NULL, NULL, NULL };
  8631. PINSTALL_CLIENT_ENTRY pDeviceInstallClient = NULL;
  8632. ULONG ulSessionId = INVALID_SESSION;
  8633. ULONG ulTransferLen;
  8634. ULONG ulStatus, ulProblem;
  8635. //
  8636. // Now create a container set for our device information element.
  8637. //
  8638. DeviceInfoSet = fpCreateDeviceInfoList(NULL, NULL);
  8639. if(DeviceInfoSet == INVALID_HANDLE_VALUE) {
  8640. return GetLastError();
  8641. }
  8642. DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
  8643. if(!fpOpenDeviceInfo(DeviceInfoSet, pszDeviceId, NULL, 0, &DeviceInfoData)) {
  8644. goto clean1;
  8645. }
  8646. //
  8647. // OK, it looks like we're going to be able to attempt a server-side
  8648. // install. Next up is the (potentially time-consuming) driver search.
  8649. // Before we start that, we want to fire up some UI on the client side (if
  8650. // somebody is logged in) letting them know we've found their hardware and
  8651. // are working on installing it.
  8652. //
  8653. // NOTE: We don't fire up client-side UI if the device has the SilentInstall
  8654. // capability.
  8655. //
  8656. ulSize = ulTransferLen = sizeof(Capabilities);
  8657. if ((CR_SUCCESS != PNP_GetDeviceRegProp(NULL,
  8658. pszDeviceId,
  8659. CM_DRP_CAPABILITIES,
  8660. &ulType,
  8661. (LPBYTE)&Capabilities,
  8662. &ulTransferLen,
  8663. &ulSize,
  8664. 0))
  8665. || !(Capabilities & CM_DEVCAP_SILENTINSTALL)) {
  8666. //
  8667. // Either we couldn't retrieve the capabilities property (shouldn't
  8668. // happen, or we did retrieve it but the silent-install bit wasn't set.
  8669. //
  8670. bDoClientUI = TRUE;
  8671. //
  8672. // If we're not going to determine the session to use for UI, use the
  8673. // SessionId supplied by the caller.
  8674. //
  8675. if ((Flags & DEVICE_INSTALL_DISPLAY_ON_CONSOLE) == 0) {
  8676. ASSERT(*SessionId != INVALID_SESSION);
  8677. ulSessionId = *SessionId;
  8678. }
  8679. //
  8680. // Go ahead and fire up the client-side UI.
  8681. //
  8682. DoDeviceInstallClient(pszDeviceId,
  8683. &ulSessionId,
  8684. Flags | DEVICE_INSTALL_UI_ONLY | DEVICE_INSTALL_PLAY_SOUND,
  8685. &pDeviceInstallClient);
  8686. }
  8687. //
  8688. // Do a default driver search for this device.
  8689. //
  8690. if(!fpBuildDriverInfoList(DeviceInfoSet, &DeviceInfoData, SPDIT_COMPATDRIVER)) {
  8691. goto clean1;
  8692. }
  8693. //
  8694. // Select the best driver from the list we just built.
  8695. //
  8696. if(!fpCallClassInstaller(DIF_SELECTBESTCOMPATDRV, DeviceInfoSet, &DeviceInfoData)) {
  8697. goto clean1;
  8698. }
  8699. DriverInfoData.cbSize = sizeof(SP_DRVINFO_DATA);
  8700. b = fpGetSelectedDriver(DeviceInfoSet, &DeviceInfoData, &DriverInfoData);
  8701. ASSERT(b); // the above call shouldn't fail
  8702. if(!b) {
  8703. goto clean1;
  8704. }
  8705. //
  8706. // NOTE: the multi-port serial class has some buggy co-installers that
  8707. // always popup UI, without using the finish-install wizard page mechanism,
  8708. // and without regard to the DI_QUIETINSTALL flag. Until they clean up
  8709. // their act, we must disallow server-side installation of those devices
  8710. // as well.
  8711. //
  8712. if(GuidEqual(&GUID_DEVCLASS_MULTIPORTSERIAL, &(DeviceInfoData.ClassGuid))) {
  8713. Err = ERROR_DI_DONT_INSTALL;
  8714. goto clean0;
  8715. }
  8716. //
  8717. // Kludge to allow INFs to force client-side (i.e., interactive)
  8718. // installation for certain devices. They do this by referencing a
  8719. // hardware or compatible ID in an "InteractiveInstall" entry in the INF's
  8720. // [ControlFlags] section. The format of one of these lines is:
  8721. //
  8722. // InteractiveInstall = <ID1> [, <ID2>... ]
  8723. //
  8724. // and there may be any number of these lines.
  8725. //
  8726. //
  8727. // First, retrieve the driver info detail data (this contains the hardware
  8728. // ID and any compatible IDs specified by this INF driver entry).
  8729. //
  8730. b = fpGetDriverInfoDetail(DeviceInfoSet,
  8731. &DeviceInfoData,
  8732. &DriverInfoData,
  8733. NULL,
  8734. 0,
  8735. &DriverInfoDetailDataSize
  8736. );
  8737. Err = GetLastError();
  8738. //
  8739. // The above call to get driver info detail data should never succeed
  8740. // because the buffer will alwyas be too small (we're just interested in
  8741. // sizing the buffer at this point).
  8742. //
  8743. ASSERT(!b && (Err == ERROR_INSUFFICIENT_BUFFER));
  8744. if(b || (Err != ERROR_INSUFFICIENT_BUFFER)) {
  8745. Err = ERROR_INVALID_DATA;
  8746. goto clean0;
  8747. }
  8748. //
  8749. // Now that we know how big of a buffer we need to hold the driver info
  8750. // details, allocate the buffer and retrieve the information.
  8751. //
  8752. pDriverInfoDetailData = HeapAlloc(ghPnPHeap, 0, DriverInfoDetailDataSize);
  8753. if(!pDriverInfoDetailData) {
  8754. Err = ERROR_NOT_ENOUGH_MEMORY;
  8755. goto clean0;
  8756. }
  8757. pDriverInfoDetailData->cbSize = sizeof(SP_DRVINFO_DETAIL_DATA);
  8758. if(!fpGetDriverInfoDetail(DeviceInfoSet,
  8759. &DeviceInfoData,
  8760. &DriverInfoData,
  8761. pDriverInfoDetailData,
  8762. DriverInfoDetailDataSize,
  8763. NULL)) {
  8764. Err = GetLastError();
  8765. ASSERT(FALSE); // we should never fail this call.
  8766. goto clean0;
  8767. }
  8768. //
  8769. // OK, we have all the hardware and compatible IDs for this driver node.
  8770. // Now we need to open up the INF and see if any of them are referenced in
  8771. // an "InteractiveInstall" control flag entry.
  8772. //
  8773. hInf = fpOpenInfFile(pDriverInfoDetailData->InfFileName,
  8774. NULL,
  8775. INF_STYLE_WIN4,
  8776. NULL
  8777. );
  8778. if(hInf == INVALID_HANDLE_VALUE) {
  8779. //
  8780. // For some reason, we couldn't open the INF!
  8781. //
  8782. goto clean1;
  8783. }
  8784. b = FALSE;
  8785. //
  8786. // Look at each InteractiveInstall line in the INF's [ControlFlags]
  8787. // section...
  8788. //
  8789. if(fpFindFirstLine(hInf, pszControlFlags, pszInteractiveInstall, &InfContext)) {
  8790. do {
  8791. //
  8792. // and within each line, examine each value...
  8793. //
  8794. for(i = 1;
  8795. fpGetStringField(&InfContext, i, szBuffer, sizeof(szBuffer) / sizeof(WCHAR), NULL);
  8796. i++) {
  8797. //
  8798. // Check to see if this ID matches up with one of the driver
  8799. // node's hardware or compatible IDs.
  8800. //
  8801. for(p = pDriverInfoDetailData->HardwareID; *p; p += (lstrlen(p) + 1)) {
  8802. if(!lstrcmpi(p, szBuffer)) {
  8803. //
  8804. // We found a match! We must defer the installation to
  8805. // the client-side.
  8806. //
  8807. b = TRUE;
  8808. goto InteractiveInstallSearchDone;
  8809. }
  8810. }
  8811. }
  8812. } while(fpFindNextMatchLine(&InfContext, pszInteractiveInstall, &InfContext));
  8813. }
  8814. InteractiveInstallSearchDone:
  8815. //
  8816. // We're done with the INF--close it.
  8817. //
  8818. fpCloseInfFile(hInf);
  8819. if(b) {
  8820. Err = ERROR_REQUIRES_INTERACTIVE_WINDOWSTATION;
  8821. goto clean0;
  8822. }
  8823. //
  8824. // Check to see if it's OK to install this driver.
  8825. //
  8826. if(!fpCallClassInstaller(DIF_ALLOW_INSTALL, DeviceInfoSet, &DeviceInfoData) &&
  8827. ((Err = GetLastError()) != ERROR_DI_DO_DEFAULT)) {
  8828. goto clean0;
  8829. }
  8830. //
  8831. // Tell our client-side UI (if any) it's time to update the device's
  8832. // description and class icon.
  8833. //
  8834. if (pDeviceInstallClient) {
  8835. //
  8836. // Retrieve the device description from the driver node we're about to
  8837. // install. We don't want to write this out as the devnode's DeviceDesc
  8838. // property, because some class installers have dependencies upon being
  8839. // able to retrieve the unaltered description as reported by the
  8840. // enumerator. So instead, we write this out as the REG_SZ
  8841. // NewDeviceDesc value entry to the devnode's hardware key.
  8842. //
  8843. DriverInfoData.cbSize = sizeof(SP_DRVINFO_DATA);
  8844. b = fpGetSelectedDriver(DeviceInfoSet, &DeviceInfoData, &DriverInfoData);
  8845. ASSERT(b); // the above call shouldn't fail
  8846. if(b) {
  8847. //
  8848. // Make sure that the hardware key is created (with the right
  8849. // security).
  8850. //
  8851. PNP_CreateKey(NULL,
  8852. pszDeviceId,
  8853. KEY_READ,
  8854. 0
  8855. );
  8856. //
  8857. // Now, open the Device Parameters subkey so we can write out the
  8858. // device's new description.
  8859. //
  8860. wsprintf(szBuffer, TEXT("%s\\%s"), pszDeviceId, pszRegKeyDeviceParam);
  8861. if(ERROR_SUCCESS == RegOpenKeyEx(ghEnumKey,
  8862. szBuffer,
  8863. 0,
  8864. KEY_READ | KEY_WRITE,
  8865. &hKey)) {
  8866. if(ERROR_SUCCESS == RegSetValueEx(
  8867. hKey,
  8868. pszRegValueNewDeviceDesc,
  8869. 0,
  8870. REG_SZ,
  8871. (LPBYTE)(DriverInfoData.Description),
  8872. (lstrlen(DriverInfoData.Description) + 1) * sizeof(WCHAR))) {
  8873. RemoveNewDevDescValue = TRUE;
  8874. }
  8875. RegCloseKey(hKey);
  8876. }
  8877. }
  8878. //
  8879. // Wait for the device install to be signaled from newdev.dll to let us
  8880. // know that it has completed displaying the UI request.
  8881. //
  8882. // Wait on the client's process as well, to catch the case
  8883. // where the process crashes (or goes away) without signaling the
  8884. // device install event.
  8885. //
  8886. // Also wait on the disconnect event in case we have explicitly
  8887. // disconnected from the client while switching sessions.
  8888. //
  8889. // We don't want to wait forever in case NEWDEV.DLL hangs for some
  8890. // reason. So we will give it 5 seconds to complete the UI only
  8891. // install and then continue on without it.
  8892. //
  8893. // Note that the client is still referenced for our use, and should be
  8894. // dereferenced when we're done with it.
  8895. //
  8896. hFinishEvents[0] = pDeviceInstallClient->hProcess;
  8897. hFinishEvents[1] = pDeviceInstallClient->hEvent;
  8898. hFinishEvents[2] = pDeviceInstallClient->hDisconnectEvent;
  8899. dwWait = WaitForMultipleObjects(3, hFinishEvents, FALSE, 5000);
  8900. if (dwWait == WAIT_OBJECT_0) {
  8901. //
  8902. // If the return is WAIT_OBJECT_0 then the newdev.dll process has
  8903. // gone away. Close the device install client and clean up all of
  8904. // the associated handles.
  8905. //
  8906. KdPrintEx((DPFLTR_PNPMGR_ID,
  8907. DBGF_INSTALL,
  8908. "UMPNPMGR: InstallDeviceServerSide: process signalled, closing device install client!\n"));
  8909. } else if (dwWait == (WAIT_OBJECT_0 + 1)) {
  8910. //
  8911. // If the return is WAIT_OBJECT_0 + 1 then the device installer
  8912. // successfully received the request. This is the only case where
  8913. // we don't want to close the client, since we may want to reuse it
  8914. // later.
  8915. //
  8916. KdPrintEx((DPFLTR_PNPMGR_ID,
  8917. DBGF_INSTALL,
  8918. "UMPNPMGR: InstallDeviceServerSide: device install client succeeded\n"));
  8919. } else if (dwWait == (WAIT_OBJECT_0 + 2)) {
  8920. //
  8921. // If the return is WAIT_OBJECT_0 + 2 then we were explicitly
  8922. // disconnected from the device install client. For server-side
  8923. // installation, we don't need to keep the client UI around on the
  8924. // disconnected session, so we should close it here also.
  8925. //
  8926. KdPrintEx((DPFLTR_PNPMGR_ID,
  8927. DBGF_INSTALL,
  8928. "UMPNPMGR: InstallDeviceServerSide: device install client disconnected\n"));
  8929. } else if (dwWait == WAIT_TIMEOUT) {
  8930. //
  8931. // Timed out while waiting for the device install client.
  8932. //
  8933. KdPrintEx((DPFLTR_PNPMGR_ID,
  8934. DBGF_INSTALL | DBGF_WARNINGS,
  8935. "UMPNPMGR: InstallDeviceServerSide: timed out waiting for device install client!\n"));
  8936. } else {
  8937. //
  8938. // The wait was satisfied for some reason other than the
  8939. // specified objects. This should never happen, but just in
  8940. // case, we'll close the client.
  8941. //
  8942. KdPrintEx((DPFLTR_PNPMGR_ID,
  8943. DBGF_INSTALL | DBGF_ERRORS,
  8944. "UMPNPMGR: InstallDeviceServerSide: wait completed unexpectedly!\n"));
  8945. }
  8946. LockNotifyList(&InstallClientList.Lock);
  8947. //
  8948. // Remove the reference placed on the client while it was in use.
  8949. //
  8950. DereferenceDeviceInstallClient(pDeviceInstallClient);
  8951. if (dwWait != (WAIT_OBJECT_0 + 1)) {
  8952. //
  8953. // Unless the client signalled successful receipt of the
  8954. // request, we probably won't be able to use this client
  8955. // anymore. Remove the initial reference so all
  8956. // associated handles will be closed and the entry will be
  8957. // freed when it is no longer in use.
  8958. //
  8959. //
  8960. // Note that if we were unsuccessful because of a
  8961. // logoff, we would have already dereferenced the
  8962. // client then, in which case the above dereference
  8963. // was the final one, and pDeviceInstallClient would
  8964. // be invalid. Instead, attempt to re-locate the
  8965. // client by the session id.
  8966. //
  8967. pDeviceInstallClient = LocateDeviceInstallClient(ulSessionId);
  8968. if (pDeviceInstallClient) {
  8969. ASSERT(pDeviceInstallClient->RefCount == 1);
  8970. DereferenceDeviceInstallClient(pDeviceInstallClient);
  8971. }
  8972. ulSessionId = INVALID_SESSION;
  8973. }
  8974. pDeviceInstallClient = NULL;
  8975. UnlockNotifyList(&InstallClientList.Lock);
  8976. }
  8977. //
  8978. // If we're doing client side UI for this device, attempt to refresh the UI again.
  8979. //
  8980. if (bDoClientUI) {
  8981. //
  8982. // When we attempt to refresh the client-side UI, if we display the
  8983. // refreshed UI on a different session than the one we had previously,
  8984. // close the previous device install client.
  8985. //
  8986. ULONG ulPrevSessionId = ulSessionId;
  8987. //
  8988. // If we're not going to determine the session to use for UI, use the
  8989. // SessionId supplied by the caller.
  8990. //
  8991. if ((Flags & DEVICE_INSTALL_DISPLAY_ON_CONSOLE) == 0) {
  8992. ASSERT(*SessionId != INVALID_SESSION);
  8993. ulSessionId = *SessionId;
  8994. }
  8995. DoDeviceInstallClient(pszDeviceId,
  8996. &ulSessionId,
  8997. Flags | DEVICE_INSTALL_UI_ONLY,
  8998. &pDeviceInstallClient);
  8999. if ((ulPrevSessionId != INVALID_SESSION) &&
  9000. (ulPrevSessionId != ulSessionId)) {
  9001. PINSTALL_CLIENT_ENTRY pPrevDeviceInstallClient;
  9002. LockNotifyList(&InstallClientList.Lock);
  9003. pPrevDeviceInstallClient = LocateDeviceInstallClient(ulPrevSessionId);
  9004. if (pPrevDeviceInstallClient) {
  9005. ASSERT(pPrevDeviceInstallClient->RefCount == 1);
  9006. DereferenceDeviceInstallClient(pPrevDeviceInstallClient);
  9007. }
  9008. UnlockNotifyList(&InstallClientList.Lock);
  9009. }
  9010. }
  9011. //
  9012. // OK, everything looks good for installing this driver. Check to see if
  9013. // this INF's class is already installed--if not, then we need to install
  9014. // it before proceeding.
  9015. //
  9016. if(RPC_S_OK != UuidToString(&(DeviceInfoData.ClassGuid), &pszClassGuid)) {
  9017. Err = ERROR_NOT_ENOUGH_MEMORY;
  9018. goto clean0;
  9019. }
  9020. wsprintf(szBuffer, TEXT("{%s}"), pszClassGuid);
  9021. RpcStringFree(&pszClassGuid);
  9022. if(ERROR_SUCCESS != RegOpenKeyEx(ghClassKey,
  9023. szBuffer,
  9024. 0,
  9025. KEY_READ,
  9026. &hKey)) {
  9027. if(!fpInstallClass(NULL,
  9028. pDriverInfoDetailData->InfFileName,
  9029. 0,
  9030. NULL)) {
  9031. goto clean1;
  9032. }
  9033. } else {
  9034. //
  9035. // The class key already exists--assume that the class has previously
  9036. // been installed.
  9037. //
  9038. RegCloseKey(hKey);
  9039. }
  9040. //
  9041. // Now we're ready to install the device. First, install the files.
  9042. //
  9043. if(!fpCallClassInstaller(DIF_INSTALLDEVICEFILES, DeviceInfoSet, &DeviceInfoData)) {
  9044. goto clean1;
  9045. }
  9046. //
  9047. // Set a flag in the device install parameters so that we don't try to
  9048. // re-copy the files during subsequent DIF operations.
  9049. //
  9050. DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
  9051. b = fpGetDeviceInstallParams(DeviceInfoSet, &DeviceInfoData, &DeviceInstallParams);
  9052. ASSERT(b); // the above call shouldn't fail
  9053. if(!b) {
  9054. goto clean1;
  9055. }
  9056. DeviceInstallParams.Flags |= DI_NOFILECOPY;
  9057. b = fpSetDeviceInstallParams(DeviceInfoSet, &DeviceInfoData, &DeviceInstallParams);
  9058. ASSERT(b); // the above call shouldn't fail
  9059. if(!b) {
  9060. goto clean1;
  9061. }
  9062. //
  9063. // Now finish up the installation.
  9064. //
  9065. if(!fpCallClassInstaller(DIF_REGISTER_COINSTALLERS, DeviceInfoSet, &DeviceInfoData)) {
  9066. goto clean1;
  9067. }
  9068. if(!fpCallClassInstaller(DIF_INSTALLINTERFACES, DeviceInfoSet, &DeviceInfoData)) {
  9069. goto clean1;
  9070. }
  9071. if(!fpCallClassInstaller(DIF_INSTALLDEVICE, DeviceInfoSet, &DeviceInfoData)) {
  9072. ULONG ulConfig;
  9073. //
  9074. // Before we do anything to blow away last error, retrieve it.
  9075. //
  9076. Err = GetLastError();
  9077. //
  9078. // It's possible that the installation got far enough to have cleared
  9079. // any problems on the device (i.e., SetupDiInstallDevice succeeded,
  9080. // but the class installer or co-installer subsequently failed during
  9081. // some post-processing).
  9082. //
  9083. // We want to make sure that the devnode is marked as needing re-install
  9084. // because we might lose the client-side install request (e.g., the
  9085. // user reboots without logging in).
  9086. //
  9087. ulConfig = GetDeviceConfigFlags(pszDeviceId, NULL);
  9088. ulConfig |= CONFIGFLAG_REINSTALL;
  9089. PNP_SetDeviceRegProp(NULL,
  9090. pszDeviceId,
  9091. CM_DRP_CONFIGFLAGS,
  9092. REG_DWORD,
  9093. (LPBYTE)&ulConfig,
  9094. sizeof(ulConfig),
  9095. 0
  9096. );
  9097. goto clean0;
  9098. }
  9099. //
  9100. // We're not quite out of the woods yet. We need to check if the class-/
  9101. // co-installers want to display finish-install wizard pages. If so, then
  9102. // we need to set the CONFIGFLAG_REINSTALL flag for this devnode and report
  9103. // failure so that we'll re-attempt the install as a client-side
  9104. // installation (where a wizard can actually be displayed).
  9105. //
  9106. ZeroMemory(&NewDevWizData, sizeof(NewDevWizData));
  9107. NewDevWizData.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
  9108. NewDevWizData.ClassInstallHeader.InstallFunction = DIF_NEWDEVICEWIZARD_FINISHINSTALL;
  9109. b = fpSetClassInstallParams(DeviceInfoSet,
  9110. &DeviceInfoData,
  9111. (PSP_CLASSINSTALL_HEADER)&NewDevWizData,
  9112. sizeof(NewDevWizData)
  9113. );
  9114. ASSERT(b); // the above call shouldn't fail
  9115. if(b) {
  9116. b = fpCallClassInstaller(DIF_NEWDEVICEWIZARD_FINISHINSTALL,
  9117. DeviceInfoSet,
  9118. &DeviceInfoData
  9119. );
  9120. if(b || (ERROR_DI_DO_DEFAULT == GetLastError())) {
  9121. //
  9122. // Retrieve the install params
  9123. //
  9124. b = (fpGetClassInstallParams(DeviceInfoSet,
  9125. &DeviceInfoData,
  9126. (PSP_CLASSINSTALL_HEADER)&NewDevWizData,
  9127. sizeof(NewDevWizData),
  9128. NULL)
  9129. && (NewDevWizData.ClassInstallHeader.InstallFunction == DIF_NEWDEVICEWIZARD_FINISHINSTALL)
  9130. );
  9131. if(b) {
  9132. //
  9133. // Are there any pages?
  9134. //
  9135. if(!NewDevWizData.NumDynamicPages) {
  9136. b = FALSE;
  9137. } else {
  9138. //
  9139. // b is already TRUE if we made it here so no need to set
  9140. //
  9141. HMODULE hComCtl32;
  9142. FP_DESTROYPROPERTYSHEETPAGE fpDestroyPropertySheetPage;
  9143. //
  9144. // We don't want to link to comctl32, nor do we want to
  9145. // always explicitly load it every time we load the device
  9146. // installer. (The number of devices that request finish-
  9147. // install pages should be small.) Thus, we load it on-
  9148. // demand right here, retrieve the entrypoint to the
  9149. // DestroyPropertySheetPage routine, and then unload the
  9150. // DLL once we've destroyed all the property pages.
  9151. //
  9152. // NOTE: (lonnym): If we can't load comctl32 or get the
  9153. // entrypont for DestroyPropertySheetPage, then we'll leak
  9154. // these wizard pages!
  9155. //
  9156. hComCtl32 = LoadLibrary(TEXT("comctl32.dll"));
  9157. if(hComCtl32) {
  9158. fpDestroyPropertySheetPage = (FP_DESTROYPROPERTYSHEETPAGE)GetProcAddress(
  9159. hComCtl32,
  9160. "DestroyPropertySheetPage"
  9161. );
  9162. if(fpDestroyPropertySheetPage) {
  9163. for(i = 0; i < NewDevWizData.NumDynamicPages; i++) {
  9164. fpDestroyPropertySheetPage(NewDevWizData.DynamicPages[i]);
  9165. }
  9166. }
  9167. FreeLibrary(hComCtl32);
  9168. }
  9169. }
  9170. }
  9171. }
  9172. }
  9173. if(b) {
  9174. ULONG ulConfig;
  9175. CONFIGRET cr;
  9176. //
  9177. // One or more finish-install wizard pages were provided--we must defer
  9178. // this installation to the client-side.
  9179. //
  9180. ulConfig = GetDeviceConfigFlags(pszDeviceId, NULL);
  9181. ulConfig |= CONFIGFLAG_REINSTALL;
  9182. cr = PNP_SetDeviceRegProp(NULL,
  9183. pszDeviceId,
  9184. CM_DRP_CONFIGFLAGS,
  9185. REG_DWORD,
  9186. (LPBYTE)&ulConfig,
  9187. sizeof(ulConfig),
  9188. 0
  9189. );
  9190. ASSERT(cr == CR_SUCCESS);
  9191. Err = ERROR_REQUIRES_INTERACTIVE_WINDOWSTATION;
  9192. goto clean0;
  9193. }
  9194. //
  9195. // The installation was a success! Check to see if a reboot is needed.
  9196. //
  9197. b = fpGetDeviceInstallParams(DeviceInfoSet, &DeviceInfoData, &DeviceInstallParams);
  9198. ASSERT(b); // the above call shouldn't fail
  9199. if(b) {
  9200. if(DeviceInstallParams.Flags & (DI_NEEDRESTART | DI_NEEDREBOOT)) {
  9201. *RebootRequired = TRUE;
  9202. }
  9203. }
  9204. //
  9205. // Process any RunOnce (RunDll32) entries that may have been queued up
  9206. // during this installation.
  9207. //
  9208. DoRunOnce();
  9209. //
  9210. // Check to see if the device has a problem.
  9211. //
  9212. if ((GetDeviceStatus(pszDeviceId, &ulStatus, &ulProblem) != CR_SUCCESS) ||
  9213. (ulStatus & DN_HAS_PROBLEM)) {
  9214. *DeviceHasProblem = TRUE;
  9215. } else {
  9216. *DeviceHasProblem = FALSE;
  9217. }
  9218. Err = NO_ERROR;
  9219. goto clean0;
  9220. clean1:
  9221. //
  9222. // Failures where error is in GetLastError() can come here.
  9223. //
  9224. Err = GetLastError();
  9225. clean0:
  9226. fpDestroyDeviceInfoList(DeviceInfoSet);
  9227. if(pDriverInfoDetailData) {
  9228. HeapFree(ghPnPHeap, 0, pDriverInfoDetailData);
  9229. }
  9230. //
  9231. // Clear out our list of RunOnce work items (note that the list will
  9232. // already be empty if the device install succeeded and we called
  9233. // DoRunOnce() above).
  9234. //
  9235. fpDestroyRunOnceNodeList();
  9236. //
  9237. // If we stored out a NewDeviceDesc value to the devnode's hardware key
  9238. // above, go and remove that turd now.
  9239. //
  9240. if(RemoveNewDevDescValue) {
  9241. //
  9242. // Open the Device Parameters subkey so we can delete the value.
  9243. //
  9244. wsprintf(szBuffer, TEXT("%s\\%s"), pszDeviceId, pszRegKeyDeviceParam);
  9245. if(ERROR_SUCCESS == RegOpenKeyEx(ghEnumKey,
  9246. szBuffer,
  9247. 0,
  9248. KEY_READ | KEY_WRITE,
  9249. &hKey)) {
  9250. RegDeleteValue(hKey, pszRegValueNewDeviceDesc);
  9251. RegCloseKey(hKey);
  9252. }
  9253. }
  9254. if (pDeviceInstallClient) {
  9255. //
  9256. // Wait for the device install to be signaled from newdev.dll to let us
  9257. // know that it has completed displaying the UI request.
  9258. //
  9259. // Wait on the client's process as well, to catch the case
  9260. // where the process crashes (or goes away) without signaling the
  9261. // device install event.
  9262. //
  9263. // Also wait on the disconnect event in case we have explicitly
  9264. // disconnected from the client while switching sessions.
  9265. //
  9266. // We don't want to wait forever in case NEWDEV.DLL hangs for some
  9267. // reason. So we will give it 5 seconds to complete the UI only
  9268. // install and then continue on without it.
  9269. //
  9270. // Note that the client is still referenced for our use, and should be
  9271. // dereferenced when we're done with it.
  9272. //
  9273. hFinishEvents[0] = pDeviceInstallClient->hProcess;
  9274. hFinishEvents[1] = pDeviceInstallClient->hEvent;
  9275. hFinishEvents[2] = pDeviceInstallClient->hDisconnectEvent;
  9276. dwWait = WaitForMultipleObjects(3, hFinishEvents, FALSE, 5000);
  9277. if (dwWait == WAIT_OBJECT_0) {
  9278. //
  9279. // If the return is WAIT_OBJECT_0 then the newdev.dll process has
  9280. // gone away. Close the device install client and clean up all of
  9281. // the associated handles.
  9282. //
  9283. KdPrintEx((DPFLTR_PNPMGR_ID,
  9284. DBGF_INSTALL,
  9285. "UMPNPMGR: InstallDeviceServerSide: process signalled, closing device install client!\n"));
  9286. } else if (dwWait == (WAIT_OBJECT_0 + 1)) {
  9287. //
  9288. // If the return is WAIT_OBJECT_0 + 1 then the device installer
  9289. // successfully received the request. This is the only case where
  9290. // we don't want to close the client, since we may want to reuse it
  9291. // later.
  9292. //
  9293. KdPrintEx((DPFLTR_PNPMGR_ID,
  9294. DBGF_INSTALL,
  9295. "UMPNPMGR: InstallDeviceServerSide: device install client succeeded\n"));
  9296. } else if (dwWait == (WAIT_OBJECT_0 + 2)) {
  9297. //
  9298. // If the return is WAIT_OBJECT_0 + 2 then we were explicitly
  9299. // disconnected from the device install client. For server-side
  9300. // installation, we don't need to keep the client UI around on the
  9301. // disconnected session, so we should close it here also.
  9302. //
  9303. KdPrintEx((DPFLTR_PNPMGR_ID,
  9304. DBGF_INSTALL,
  9305. "UMPNPMGR: InstallDeviceServerSide: device install client disconnected\n"));
  9306. } else if (dwWait == WAIT_TIMEOUT) {
  9307. //
  9308. // Timed out while waiting for the device install client.
  9309. //
  9310. KdPrintEx((DPFLTR_PNPMGR_ID,
  9311. DBGF_INSTALL | DBGF_WARNINGS,
  9312. "UMPNPMGR: InstallDeviceServerSide: timed out waiting for device install client!\n"));
  9313. } else {
  9314. //
  9315. // The wait was satisfied for some reason other than the
  9316. // specified objects. This should never happen, but just in
  9317. // case, we'll close the client.
  9318. //
  9319. KdPrintEx((DPFLTR_PNPMGR_ID,
  9320. DBGF_INSTALL | DBGF_ERRORS,
  9321. "UMPNPMGR: InstallDeviceServerSide: wait completed unexpectedly!\n"));
  9322. }
  9323. LockNotifyList(&InstallClientList.Lock);
  9324. //
  9325. // Remove the reference placed on the client while it was in use.
  9326. //
  9327. DereferenceDeviceInstallClient(pDeviceInstallClient);
  9328. if (dwWait != (WAIT_OBJECT_0 + 1)) {
  9329. //
  9330. // Unless the client signalled successful receipt of the
  9331. // request, we probably won't be able to use this client
  9332. // anymore. Remove the initial reference so all
  9333. // associated handles will be closed and the entry will be
  9334. // freed when it is no longer in use.
  9335. //
  9336. //
  9337. // Note that if we were unsuccessful because of a
  9338. // logoff, we would have already dereferenced the
  9339. // client then, in which case the above dereference
  9340. // was the final one, and pDeviceInstallClient would
  9341. // be invalid. Instead, attempt to re-locate the
  9342. // client by the session id.
  9343. //
  9344. pDeviceInstallClient = LocateDeviceInstallClient(ulSessionId);
  9345. if (pDeviceInstallClient) {
  9346. ASSERT(pDeviceInstallClient->RefCount == 1);
  9347. DereferenceDeviceInstallClient(pDeviceInstallClient);
  9348. }
  9349. ulSessionId = INVALID_SESSION;
  9350. }
  9351. pDeviceInstallClient = NULL;
  9352. UnlockNotifyList(&InstallClientList.Lock);
  9353. }
  9354. if (bDoClientUI) {
  9355. //
  9356. // Note that if client-side UI was created during the server-side device
  9357. // install, it will still exist when we are done. The caller should
  9358. // dereference it when it is done installing all devices to make it go
  9359. // away.
  9360. //
  9361. *SessionId = ulSessionId;
  9362. } else {
  9363. //
  9364. // There was never any client-side UI for this device install.
  9365. //
  9366. *SessionId = INVALID_SESSION;
  9367. }
  9368. return Err;
  9369. } // InstallDeviceServerSide
  9370. BOOL
  9371. PromptUser(
  9372. IN OUT PULONG SessionId,
  9373. IN ULONG Flags
  9374. )
  9375. /*++
  9376. Routine Description:
  9377. This routine will notify the logged-on user (if any) with a specified
  9378. message.
  9379. Arguments:
  9380. SessionId - Supplies the address of a variable containing the SessionId on
  9381. which the device install client is to be displayed. If successful, the
  9382. SessionId will contain the id of the session in which the reboot dialog
  9383. process was launched. Otherwise, will contain an invalid session id,
  9384. INVALID_SESSION, (0xFFFFFFFF).
  9385. Flags - Specifies flags describing the behavior of the reboot dialog
  9386. displayed by the device install client.
  9387. The following flags are currently defined:
  9388. DEVICE_INSTALL_FINISHED_REBOOT - if specified, the user should be
  9389. prompted to reboot.
  9390. DEVICE_INSTALL_BATCH_COMPLETE - if specified, the user should be
  9391. prompted that the plug and play manager is finished installing a
  9392. batch of devices.
  9393. DEVICE_INSTALL_DISPLAY_ON_CONSOLE - if specified, the value in the
  9394. SessionId variable will be ignored, and the device installclient will
  9395. always be displayed on the current active console session.
  9396. Return Value:
  9397. If the user is successfully notified, the return value is TRUE.
  9398. If we couldn't ask the user (i.e., no user was logged in), the return
  9399. value is FALSE.
  9400. Notes:
  9401. If the user was prompted for a reboot, this doesn't necessarily mean that a
  9402. reboot is in progress.
  9403. --*/
  9404. {
  9405. BOOL bStatus = FALSE;
  9406. ULONG ulValue, ulSize, ulSessionId;
  9407. HANDLE hFinishEvents[3] = { NULL, NULL, NULL };
  9408. DWORD dwWait;
  9409. PINSTALL_CLIENT_ENTRY pDeviceInstallClient = NULL;
  9410. try {
  9411. //
  9412. // Check if we should skip client side UI.
  9413. //
  9414. if (gbSuppressUI) {
  9415. KdPrintEx((DPFLTR_PNPMGR_ID,
  9416. DBGF_INSTALL | DBGF_WARNINGS,
  9417. "UMPNPMGR: PromptUser: Client-side UI has been suppressed, exiting.\n"));
  9418. LogWarningEvent(WRN_REBOOT_UI_SUPPRESSED, 0, NULL);
  9419. *SessionId = INVALID_SESSION;
  9420. return FALSE;
  9421. }
  9422. //
  9423. // Determine the session to use, based on the supplied flags.
  9424. //
  9425. if (Flags & DEVICE_INSTALL_DISPLAY_ON_CONSOLE) {
  9426. ulSessionId = GetActiveConsoleSessionId();
  9427. } else {
  9428. ASSERT(*SessionId != INVALID_SESSION);
  9429. ulSessionId = *SessionId;
  9430. }
  9431. ASSERT(ulSessionId != INVALID_SESSION);
  9432. //
  9433. // If the specified session is not currently connected anywhere, don't
  9434. // bother creating any UI.
  9435. //
  9436. if (!IsSessionConnected(ulSessionId)) {
  9437. KdPrintEx((DPFLTR_PNPMGR_ID,
  9438. DBGF_EVENT,
  9439. "UMPNPMGR: PromptUser: SessionId %d not connected, exiting\n",
  9440. ulSessionId));
  9441. return FALSE;
  9442. }
  9443. //
  9444. // If a device install client is already running on this session,
  9445. // connect to it. Otherwise, create a new one.
  9446. //
  9447. LockNotifyList(&InstallClientList.Lock);
  9448. //
  9449. // First, try to connect to an existing client already running on this
  9450. // session.
  9451. //
  9452. bStatus = ConnectDeviceInstallClient(ulSessionId,
  9453. &pDeviceInstallClient);
  9454. if (bStatus) {
  9455. if ((Flags & DEVICE_INSTALL_BATCH_COMPLETE) &&
  9456. (pDeviceInstallClient->ulInstallFlags & DEVICE_INSTALL_BATCH_COMPLETE)) {
  9457. //
  9458. // If there is an existing client, and we're sending it the
  9459. // "we're done" message, and the last thing this client did was
  9460. // display that message, don't bother sending it again.
  9461. //
  9462. pDeviceInstallClient = NULL;
  9463. bStatus = FALSE;
  9464. }
  9465. } else if (!(Flags & DEVICE_INSTALL_BATCH_COMPLETE)) {
  9466. //
  9467. // If there isn't an existing client for this session, and we're not
  9468. // launching one just to say "we're done", then go ahead and create
  9469. // a new device install client for this session.
  9470. //
  9471. bStatus = CreateDeviceInstallClient(ulSessionId,
  9472. &pDeviceInstallClient);
  9473. }
  9474. if (bStatus) {
  9475. //
  9476. // Whether we are using an existing client, or created a
  9477. // new one, the client should only have the initial
  9478. // reference from when it was added to the list, since any
  9479. // use of the client is done on this single install
  9480. // thread.
  9481. //
  9482. ASSERT(pDeviceInstallClient);
  9483. ASSERT(pDeviceInstallClient->RefCount == 1);
  9484. //
  9485. // Reference the device install client while it is in use.
  9486. // We'll remove this reference when we're done with it.
  9487. //
  9488. ReferenceDeviceInstallClient(pDeviceInstallClient);
  9489. }
  9490. UnlockNotifyList(&InstallClientList.Lock);
  9491. if (!bStatus) {
  9492. *SessionId = INVALID_SESSION;
  9493. return FALSE;
  9494. }
  9495. ASSERT(pDeviceInstallClient);
  9496. //
  9497. // Don't send newdev the display on console flag, if it was specified.
  9498. //
  9499. ulValue = Flags & ~DEVICE_INSTALL_DISPLAY_ON_CONSOLE;
  9500. //
  9501. // Send newdev.dll the specified signal.
  9502. //
  9503. if (WriteFile(pDeviceInstallClient->hPipe,
  9504. &ulValue,
  9505. sizeof(ulValue),
  9506. &ulSize,
  9507. NULL
  9508. )) {
  9509. //
  9510. // newdev.dll expects two DWORDs to be sent over the pipe each time. The second
  9511. // DWORD should just be set to 0 in this case.
  9512. //
  9513. ulValue = 0;
  9514. if (WriteFile(pDeviceInstallClient->hPipe,
  9515. &ulValue,
  9516. sizeof(ulValue),
  9517. &ulSize,
  9518. NULL
  9519. )) {
  9520. bStatus = TRUE;
  9521. } else {
  9522. bStatus = FALSE;
  9523. LogErrorEvent(ERR_WRITING_SERVER_INSTALL_PIPE, GetLastError(), 0);
  9524. }
  9525. } else {
  9526. bStatus = FALSE;
  9527. LogErrorEvent(ERR_WRITING_SERVER_INSTALL_PIPE, GetLastError(), 0);
  9528. }
  9529. if (bStatus) {
  9530. bStatus = FALSE;
  9531. //
  9532. // Wait for the event to be signaled from newdev.dll
  9533. // to let us know that it has received the information.
  9534. //
  9535. // Wait on the process as well, to catch the case where the process
  9536. // crashes (or goes away) without signaling the event.
  9537. //
  9538. // Also wait on the disconnect event in case we have just
  9539. // disconnected from the device install client, in which case the
  9540. // event and process handles are no longer valid.
  9541. //
  9542. hFinishEvents[0] = pDeviceInstallClient->hProcess;
  9543. hFinishEvents[1] = pDeviceInstallClient->hEvent;
  9544. hFinishEvents[2] = pDeviceInstallClient->hDisconnectEvent;
  9545. dwWait = WaitForMultipleObjects(3, hFinishEvents, FALSE, INFINITE);
  9546. if (dwWait == WAIT_OBJECT_0) {
  9547. //
  9548. // If the return is WAIT_OBJECT_0 then the newdev.dll
  9549. // process has gone away. Consider the request unsuccessful
  9550. // so that we will retry again at a later time. Orphan the
  9551. // device install client and clean up all of the associated
  9552. // handles.
  9553. //
  9554. KdPrintEx((DPFLTR_PNPMGR_ID,
  9555. DBGF_EVENT,
  9556. "UMPNPMGR: PromptUser: process signalled, orphaning device install client!\n"));
  9557. } else if (dwWait == (WAIT_OBJECT_0 + 1)) {
  9558. //
  9559. // If the return is WAIT_OBJECT_0 + 1 then the request was
  9560. // received successfully.
  9561. //
  9562. KdPrintEx((DPFLTR_PNPMGR_ID,
  9563. DBGF_EVENT,
  9564. "UMPNPMGR: PromptUser: device install client succeeded\n"));
  9565. //
  9566. // Remember the last request serviced by this client.
  9567. //
  9568. pDeviceInstallClient->ulInstallFlags = Flags;
  9569. bStatus = TRUE;
  9570. } else if (dwWait == (WAIT_OBJECT_0 + 2)) {
  9571. //
  9572. // If the return is WAIT_OBJECT_0 + 2 then the device
  9573. // install client was explicitly disconnected before
  9574. // the request was received. Consider the request
  9575. // unsuccessful so that we will retry again at a later
  9576. // time.
  9577. //
  9578. KdPrintEx((DPFLTR_PNPMGR_ID,
  9579. DBGF_EVENT,
  9580. "UMPNPMGR: PromptUser: device install client orphaned!\n"));
  9581. }
  9582. }
  9583. LockNotifyList(&InstallClientList.Lock);
  9584. //
  9585. // Remove the reference placed on the client while it was in use.
  9586. //
  9587. DereferenceDeviceInstallClient(pDeviceInstallClient);
  9588. if (!bStatus) {
  9589. //
  9590. // Unless the client signalled successful receipt of the
  9591. // request, we probably won't be able to use this client
  9592. // anymore. Remove the initial reference so all
  9593. // associated handles will be closed and the entry will be
  9594. // freed when it is no longer in use.
  9595. //
  9596. //
  9597. // Note that if we were unsuccessful because of a
  9598. // logoff, we would have already dereferenced the
  9599. // client then, in which case the above dereference
  9600. // was the final one, and pDeviceInstallClient would
  9601. // be invalid. Instead, attempt to re-locate the
  9602. // client by the session id.
  9603. //
  9604. pDeviceInstallClient = LocateDeviceInstallClient(ulSessionId);
  9605. if (pDeviceInstallClient) {
  9606. ASSERT(pDeviceInstallClient->RefCount == 1);
  9607. DereferenceDeviceInstallClient(pDeviceInstallClient);
  9608. }
  9609. }
  9610. UnlockNotifyList(&InstallClientList.Lock);
  9611. } except (EXCEPTION_EXECUTE_HANDLER) {
  9612. KdPrintEx((DPFLTR_PNPMGR_ID,
  9613. DBGF_ERRORS | DBGF_WARNINGS,
  9614. "UMPNPMGR: Exception during PromptUser!\n"));
  9615. ASSERT(0);
  9616. bStatus = FALSE;
  9617. }
  9618. if (!bStatus) {
  9619. *SessionId = INVALID_SESSION;
  9620. } else {
  9621. *SessionId = ulSessionId;
  9622. }
  9623. return bStatus;
  9624. } // PromptUser
  9625. BOOL
  9626. CreateDeviceInstallClient(
  9627. IN ULONG SessionId,
  9628. OUT PINSTALL_CLIENT_ENTRY *DeviceInstallClient
  9629. )
  9630. /*++
  9631. Routine Description:
  9632. This routine kicks off a newdev.dll process (if someone is logged in).
  9633. We use a named pipe to comunicate with the user mode process
  9634. and have it either display UI for a server side install, or do the install
  9635. itself on the client side.
  9636. Arguments:
  9637. SessionId - Session for which a device install client should be
  9638. created or connected to.
  9639. DeviceInstallClient - Receives a pointer to receive a pointer to the
  9640. device install client for this session.
  9641. Return Value:
  9642. Returns TRUE if a device install client was created, or if an existing
  9643. device install client was found for the specified session. This routine
  9644. doesn't wait until the process terminates. Returns FALSE if a device
  9645. install client could not be created.
  9646. Notes:
  9647. The InstallClientList lock must be acquired by the caller of this routine.
  9648. --*/
  9649. {
  9650. STARTUPINFO StartupInfo;
  9651. PROCESS_INFORMATION ProcessInfo;
  9652. WCHAR szCmdLine[MAX_PATH];
  9653. WCHAR szDeviceInstallPipeName[MAX_PATH];
  9654. WCHAR szDeviceInstallEventName[MAX_PATH];
  9655. ULONG ulDeviceInstallEventNameSize;
  9656. HANDLE hFinishEvents[2] = { NULL, NULL };
  9657. HANDLE hTemp, hUserToken = NULL;
  9658. PINSTALL_CLIENT_ENTRY entry;
  9659. RPC_STATUS rpcStatus = RPC_S_OK;
  9660. GUID newGuid;
  9661. WCHAR szGuidString[MAX_GUID_STRING_LEN];
  9662. HANDLE hDeviceInstallPipe = NULL, hDeviceInstallEvent = NULL;
  9663. HANDLE hDeviceInstallDisconnectEvent = NULL;
  9664. PINSTALL_CLIENT_ENTRY pDeviceInstallClient = NULL;
  9665. SECURITY_ATTRIBUTES sa;
  9666. ULONG ulSize;
  9667. WIN32_FIND_DATA findData;
  9668. BOOL bStatus;
  9669. PVOID lpEnvironment = NULL;
  9670. OVERLAPPED overlapped = {0,0,0,0,0};
  9671. DWORD dwError, dwWait, dwBytes;
  9672. //
  9673. // Validate output parameter.
  9674. //
  9675. ASSERT(DeviceInstallClient);
  9676. if (!DeviceInstallClient) {
  9677. return FALSE;
  9678. }
  9679. //
  9680. // Make sure the specified SessionId is valid.
  9681. //
  9682. ASSERT(SessionId != INVALID_SESSION);
  9683. if (SessionId == INVALID_SESSION) {
  9684. KdPrintEx((DPFLTR_PNPMGR_ID,
  9685. DBGF_INSTALL | DBGF_ERRORS,
  9686. "UMPNPMGR: CreateDeviceInstallClient: Invalid Console SessionId %d, exiting!\n",
  9687. SessionId));
  9688. return FALSE;
  9689. }
  9690. //
  9691. // Assume failure
  9692. //
  9693. bStatus = FALSE;
  9694. try {
  9695. //
  9696. // Before doing anything, check that newdev.dll is actually present on
  9697. // the system.
  9698. //
  9699. szCmdLine[0] = L'\0';
  9700. ulSize = GetSystemDirectory(szCmdLine, MAX_PATH);
  9701. if ((ulSize == 0) || ((ulSize + 2 + ARRAY_SIZE(NEWDEV_DLL)) > MAX_PATH)) {
  9702. return FALSE;
  9703. }
  9704. lstrcat(szCmdLine, TEXT("\\"));
  9705. lstrcat(szCmdLine, NEWDEV_DLL);
  9706. hTemp = FindFirstFile(szCmdLine, &findData);
  9707. if(hTemp != INVALID_HANDLE_VALUE) {
  9708. FindClose(hTemp);
  9709. } else {
  9710. KdPrintEx((DPFLTR_PNPMGR_ID,
  9711. DBGF_INSTALL | DBGF_ERRORS,
  9712. "UMPNPMGR: CreateDeviceInstallClient: %ws not found, error = %d, exiting\n",
  9713. szCmdLine,
  9714. GetLastError()));
  9715. LogWarningEvent(WRN_NEWDEV_NOT_PRESENT, 1, szCmdLine);
  9716. return FALSE;
  9717. }
  9718. //
  9719. // Get the user access token for the active console session user.
  9720. //
  9721. if (!GetSessionUserToken(SessionId, &hUserToken) || (hUserToken == NULL)) {
  9722. KdPrintEx((DPFLTR_PNPMGR_ID,
  9723. DBGF_INSTALL,
  9724. "UMPNPMGR: CreateDeviceInstallClient: Unable to get user token for Session %d,\n"
  9725. " postponing client-side installation, error = %d\n",
  9726. SessionId,
  9727. GetLastError()));
  9728. return FALSE;
  9729. }
  9730. //
  9731. // If the user Winstation for this session is locked, and Fast User
  9732. // Switching is enabled, then we're at the welcome screen. Don't create
  9733. // a device install client, because we don't want to hang the install
  9734. // thread if nobody's actually around to do anything about it. If the
  9735. // session is locked, but FUS is not disabled, maintain previous
  9736. // behavior, and launch the device install client. The user will have
  9737. // to unlock or logoff before another user can logon anyways.
  9738. //
  9739. if (IsSessionLocked(SessionId) && IsFastUserSwitchingEnabled()) {
  9740. KdPrintEx((DPFLTR_PNPMGR_ID,
  9741. DBGF_INSTALL,
  9742. "UMPNPMGR: CreateDeviceInstallClient: Session %d locked with FUS enabled,\n"
  9743. " postponing client-side installation.\n",
  9744. SessionId));
  9745. CloseHandle(hUserToken);
  9746. return FALSE;
  9747. }
  9748. //
  9749. // Create a named pipe and event for communication and synchronization
  9750. // with the client-side device installer. The event and named pipe must
  9751. // be global so that UMPNPMGR can interact with a device install client
  9752. // in a different session, but it must still be unique for that session.
  9753. // Add a generated GUID so the names are not entirely well-known.
  9754. //
  9755. rpcStatus = UuidCreate(&newGuid);
  9756. if ((rpcStatus != RPC_S_OK) &&
  9757. (rpcStatus != RPC_S_UUID_LOCAL_ONLY)) {
  9758. goto Clean0;
  9759. }
  9760. if (StringFromGuid((LPGUID)&newGuid,
  9761. szGuidString,
  9762. MAX_GUID_STRING_LEN) != NO_ERROR) {
  9763. goto Clean0;
  9764. }
  9765. wsprintf(szDeviceInstallPipeName,
  9766. TEXT("%ws_%d.%ws"),
  9767. PNP_DEVICE_INSTALL_PIPE,
  9768. SessionId,
  9769. szGuidString);
  9770. wsprintf(szDeviceInstallEventName,
  9771. TEXT("Global\\%ws_%d.%ws"),
  9772. PNP_DEVICE_INSTALL_EVENT,
  9773. SessionId,
  9774. szGuidString);
  9775. ulDeviceInstallEventNameSize = (lstrlen(szDeviceInstallEventName) + 1) * sizeof(WCHAR);
  9776. //
  9777. // Initialize process, startup and overlapped structures, since we
  9778. // depend on them being NULL during cleanup here on out.
  9779. //
  9780. memset(&ProcessInfo, 0, sizeof(ProcessInfo));
  9781. memset(&StartupInfo, 0, sizeof(StartupInfo));
  9782. memset(&overlapped, 0, sizeof(overlapped));
  9783. //
  9784. // The approximate size of the named pipe output buffer should be large
  9785. // enough to hold the greater of either:
  9786. // - The name and size of the named event string, OR
  9787. // - The install flags, name and device instance id size for at least
  9788. // one device install.
  9789. //
  9790. ulSize = max(sizeof(ulDeviceInstallEventNameSize) +
  9791. ulDeviceInstallEventNameSize,
  9792. 2 * sizeof(ULONG) +
  9793. (MAX_DEVICE_ID_LEN * sizeof(WCHAR)));
  9794. //
  9795. // Open up a named pipe to communicate with newdev.dll to display
  9796. // the device install Ui. Note that if creating the pipe fails we will just
  9797. // continue the device install with no Ui.
  9798. //
  9799. hDeviceInstallPipe = CreateNamedPipe(szDeviceInstallPipeName,
  9800. PIPE_ACCESS_OUTBOUND | // outbound data only
  9801. FILE_FLAG_OVERLAPPED | // use overlapped structure
  9802. FILE_FLAG_FIRST_PIPE_INSTANCE, // make sure we are the creator of the pipe
  9803. PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
  9804. 1, // only one instance is allowed, and we are its creator
  9805. ulSize, // out buffer size
  9806. 0, // in buffer size
  9807. PNP_PIPE_TIMEOUT, // default timeout
  9808. NULL // default security
  9809. );
  9810. if (hDeviceInstallPipe == INVALID_HANDLE_VALUE) {
  9811. hDeviceInstallPipe = NULL;
  9812. goto Clean0;
  9813. }
  9814. //
  9815. // Create an event that a user-client can synchronize with and set, and
  9816. // that we will block on after we send a device install to newdev.dll.
  9817. //
  9818. if (CreateUserSynchEvent(szDeviceInstallEventName,
  9819. &hDeviceInstallEvent) != NO_ERROR) {
  9820. goto Clean0;
  9821. }
  9822. //
  9823. // Create an event that we can use internally such that waiters can know
  9824. // when to disconnect from the device install client.
  9825. //
  9826. hDeviceInstallDisconnectEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  9827. if (!hDeviceInstallDisconnectEvent) {
  9828. goto Clean0;
  9829. }
  9830. //
  9831. // Launch newdev.dll using rundll32.exe, passing it the pipe name.
  9832. // "rundll32.exe newdev.dll,ClientSideInstall <device-install-pipe-name>"
  9833. //
  9834. if (ARRAY_SIZE(szCmdLine) < (ARRAY_SIZE(RUNDLL32_EXE) +
  9835. 1 + // ' '
  9836. ARRAY_SIZE(NEWDEV_DLL) +
  9837. 1 + // ','
  9838. lstrlen(TEXT("ClientSideInstall")) +
  9839. 1 + // ' '
  9840. lstrlen(szDeviceInstallPipeName) +
  9841. 1)) { // '\0'
  9842. goto Clean0;
  9843. }
  9844. wsprintf(szCmdLine,
  9845. TEXT("%ws %ws,%ws %ws"),
  9846. RUNDLL32_EXE, NEWDEV_DLL,
  9847. TEXT("ClientSideInstall"),
  9848. szDeviceInstallPipeName);
  9849. #if DBG
  9850. //
  9851. // Retrieve debugger settings from the service key.
  9852. //
  9853. {
  9854. HKEY hKey;
  9855. if (RegOpenKeyEx(ghServicesKey,
  9856. pszRegKeyPlugPlayServiceParams,
  9857. 0,
  9858. KEY_READ,
  9859. &hKey) == ERROR_SUCCESS) {
  9860. ULONG ulValue = 0;
  9861. WCHAR szDebugCmdLine[MAX_PATH];
  9862. ulSize = sizeof(ulValue);
  9863. if ((RegQueryValueEx(hKey,
  9864. pszRegValueDebugInstall,
  9865. NULL,
  9866. NULL,
  9867. (LPBYTE)&ulValue,
  9868. &ulSize) == ERROR_SUCCESS) &&(ulValue == 1)) {
  9869. ulSize = sizeof(szDebugCmdLine);
  9870. if (RegQueryValueEx(hKey,
  9871. pszRegValueDebugInstallCommand,
  9872. NULL,
  9873. NULL,
  9874. (LPBYTE)szDebugCmdLine,
  9875. &ulSize) != ERROR_SUCCESS) {
  9876. //
  9877. // If no debugger was retrieved, use the default
  9878. // debugger (ntsd.exe).
  9879. //
  9880. lstrcpy(szDebugCmdLine, NTSD_EXE);
  9881. }
  9882. lstrcat(szDebugCmdLine, TEXT(" "));
  9883. lstrcat(szDebugCmdLine, szCmdLine);
  9884. lstrcpy(szCmdLine, szDebugCmdLine);
  9885. }
  9886. RegCloseKey(hKey);
  9887. }
  9888. }
  9889. #endif // DBG
  9890. //
  9891. // Attempt to create the user's environment block. If for some reason we
  9892. // can't, we'll just have to create the process without it.
  9893. //
  9894. if (!CreateEnvironmentBlock(&lpEnvironment,
  9895. hUserToken,
  9896. FALSE)) {
  9897. KdPrintEx((DPFLTR_PNPMGR_ID,
  9898. DBGF_INSTALL | DBGF_ERRORS,
  9899. "UMPNPMGR: CreateDeviceInstallClient: "
  9900. "Failed to allocate environment block, error = %d!\n",
  9901. GetLastError()));
  9902. lpEnvironment = NULL;
  9903. }
  9904. StartupInfo.cb = sizeof(StartupInfo);
  9905. StartupInfo.wShowWindow = SW_SHOW;
  9906. StartupInfo.lpDesktop = DEFAULT_INTERACTIVE_DESKTOP; // WinSta0\Default
  9907. //
  9908. // CreateProcessAsUser will create the process in the session
  9909. // specified by the by user-token.
  9910. //
  9911. if (!CreateProcessAsUser(hUserToken, // hToken
  9912. NULL, // lpApplicationName
  9913. szCmdLine, // lpCommandLine
  9914. NULL, // lpProcessAttributes
  9915. NULL, // lpThreadAttributes
  9916. FALSE, // bInheritHandles
  9917. CREATE_UNICODE_ENVIRONMENT |
  9918. DETACHED_PROCESS, // dwCreationFlags
  9919. lpEnvironment, // lpEnvironment
  9920. NULL, // lpDirectory
  9921. &StartupInfo, // lpStartupInfo
  9922. &ProcessInfo // lpProcessInfo
  9923. )) {
  9924. KdPrintEx((DPFLTR_PNPMGR_ID,
  9925. DBGF_INSTALL | DBGF_ERRORS,
  9926. "UMPNPMGR: CreateDeviceInstallClient: "
  9927. "Create rundll32 process failed, error = %d\n",
  9928. GetLastError()));
  9929. goto Clean0;
  9930. }
  9931. ASSERT(ProcessInfo.hProcess);
  9932. ASSERT(ProcessInfo.hThread);
  9933. //
  9934. // Create an event for use with overlapped I/O - no security, manual
  9935. // reset, not signalled, no name.
  9936. //
  9937. overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  9938. if (overlapped.hEvent == NULL) {
  9939. goto Clean0;
  9940. }
  9941. //
  9942. // Connect to the newly created named pipe. If newdev is not already
  9943. // connected to the named pipe, then ConnectNamedPipe() will fail with
  9944. // ERROR_IO_PENDING, and we will wait on the overlapped event. If
  9945. // newdev is already connected, it will fail with ERROR_PIPE_CONNECTED.
  9946. // Note however that neither of these is an error condition.
  9947. //
  9948. if (!ConnectNamedPipe(hDeviceInstallPipe, &overlapped)) {
  9949. //
  9950. // Overlapped ConnectNamedPipe should always return FALSE on
  9951. // success. Check the last error to see what really happened.
  9952. //
  9953. dwError = GetLastError();
  9954. if (dwError == ERROR_IO_PENDING) {
  9955. //
  9956. // I/O is pending, wait up to one minute for the client to
  9957. // connect, also wait on the process in case it terminates
  9958. // unexpectedly.
  9959. //
  9960. hFinishEvents[0] = overlapped.hEvent;
  9961. hFinishEvents[1] = ProcessInfo.hProcess;
  9962. dwWait = WaitForMultipleObjects(2, hFinishEvents,
  9963. FALSE,
  9964. PNP_PIPE_TIMEOUT); // 60 seconds
  9965. if (dwWait == WAIT_OBJECT_0) {
  9966. //
  9967. // The overlapped I/O operation completed. Check the status
  9968. // of the operation.
  9969. //
  9970. if (!GetOverlappedResult(hDeviceInstallPipe,
  9971. &overlapped,
  9972. &dwBytes,
  9973. FALSE)) {
  9974. goto Clean0;
  9975. }
  9976. } else {
  9977. //
  9978. // Either the connection timed out, or the client process
  9979. // exited. Cancel pending I/O against the pipe, and quit.
  9980. //
  9981. KdPrintEx((DPFLTR_PNPMGR_ID,
  9982. DBGF_INSTALL | DBGF_ERRORS,
  9983. "UMPNPMGR: CreateDeviceInstallClient: "
  9984. "Connect timed out, or client process exited!\n"));
  9985. CancelIo(hDeviceInstallPipe);
  9986. goto Clean0;
  9987. }
  9988. } else if (dwError != ERROR_PIPE_CONNECTED) {
  9989. //
  9990. // If the last error indicates anything other than pending I/O,
  9991. // or that The client is already connected to named pipe, fail.
  9992. //
  9993. goto Clean0;
  9994. }
  9995. } else {
  9996. //
  9997. // ConnectNamedPipe should not return anything but FALSE in
  9998. // overlapped mode.
  9999. //
  10000. goto Clean0;
  10001. }
  10002. //
  10003. // The client is now connected to the named pipe.
  10004. // Close the overlapped event.
  10005. //
  10006. CloseHandle(overlapped.hEvent);
  10007. overlapped.hEvent = NULL;
  10008. //
  10009. // The first data in the device install pipe will be the length of
  10010. // the name of the event that will be used to sync up umpnpmgr.dll
  10011. // and newdev.dll.
  10012. //
  10013. if (!WriteFile(hDeviceInstallPipe,
  10014. &ulDeviceInstallEventNameSize,
  10015. sizeof(ulDeviceInstallEventNameSize),
  10016. &ulSize,
  10017. NULL)) {
  10018. LogErrorEvent(ERR_WRITING_SERVER_INSTALL_PIPE, GetLastError(), 0);
  10019. goto Clean0;
  10020. }
  10021. //
  10022. // The next data in the device install pipe will be the name of the
  10023. // event that will be used to sync up umpnpmgr.dll and newdev.dll.
  10024. //
  10025. if (!WriteFile(hDeviceInstallPipe,
  10026. (LPCVOID)szDeviceInstallEventName,
  10027. ulDeviceInstallEventNameSize,
  10028. &ulSize,
  10029. NULL)) {
  10030. LogErrorEvent(ERR_WRITING_SERVER_INSTALL_PIPE, GetLastError(), 0);
  10031. goto Clean0;
  10032. }
  10033. //
  10034. // Allocate a new device install client entry for the list, and save all
  10035. // the handles with it.
  10036. //
  10037. pDeviceInstallClient = HeapAlloc(ghPnPHeap, 0, sizeof(INSTALL_CLIENT_ENTRY));
  10038. if(!pDeviceInstallClient) {
  10039. goto Clean0;
  10040. }
  10041. pDeviceInstallClient->Next = NULL;
  10042. pDeviceInstallClient->RefCount = 1;
  10043. pDeviceInstallClient->ulSessionId = SessionId;
  10044. pDeviceInstallClient->hEvent = hDeviceInstallEvent;
  10045. pDeviceInstallClient->hPipe = hDeviceInstallPipe;
  10046. pDeviceInstallClient->hProcess = ProcessInfo.hProcess;
  10047. pDeviceInstallClient->hDisconnectEvent = hDeviceInstallDisconnectEvent;
  10048. pDeviceInstallClient->ulInstallFlags = 0;
  10049. pDeviceInstallClient->LastDeviceId[0] = L'\0';
  10050. //
  10051. // Insert the newly created device install client info to our list.
  10052. // The caller must have previously acquired the InstallClientList lock.
  10053. //
  10054. entry = (PINSTALL_CLIENT_ENTRY)InstallClientList.Next;
  10055. if (!entry) {
  10056. InstallClientList.Next = pDeviceInstallClient;
  10057. } else {
  10058. while ((PINSTALL_CLIENT_ENTRY)entry->Next) {
  10059. entry = (PINSTALL_CLIENT_ENTRY)entry->Next;
  10060. }
  10061. entry->Next = pDeviceInstallClient;
  10062. }
  10063. bStatus = TRUE;
  10064. Clean0:
  10065. NOTHING;
  10066. } except (EXCEPTION_EXECUTE_HANDLER) {
  10067. KdPrintEx((DPFLTR_PNPMGR_ID,
  10068. DBGF_ERRORS | DBGF_INSTALL,
  10069. "UMPNPMGR: Exception during CreateDeviceInstallClient!\n"));
  10070. ASSERT(0);
  10071. bStatus = FALSE;
  10072. //
  10073. // Reference the following variable so the compiler will respect
  10074. // statement ordering w.r.t. its assignment.
  10075. //
  10076. lpEnvironment = lpEnvironment;
  10077. ProcessInfo.hThread = ProcessInfo.hThread;
  10078. ProcessInfo.hProcess = ProcessInfo.hProcess;
  10079. hUserToken = hUserToken;
  10080. hDeviceInstallDisconnectEvent = hDeviceInstallDisconnectEvent;
  10081. hDeviceInstallEvent = hDeviceInstallEvent;
  10082. hDeviceInstallPipe = hDeviceInstallPipe;
  10083. }
  10084. if (lpEnvironment) {
  10085. DestroyEnvironmentBlock(lpEnvironment);
  10086. }
  10087. //
  10088. // Close the handle to the thread since we don't need it.
  10089. //
  10090. if (ProcessInfo.hThread) {
  10091. CloseHandle(ProcessInfo.hThread);
  10092. }
  10093. if (hUserToken) {
  10094. CloseHandle(hUserToken);
  10095. }
  10096. if (overlapped.hEvent) {
  10097. CloseHandle(overlapped.hEvent);
  10098. }
  10099. if (!bStatus) {
  10100. ASSERT(!pDeviceInstallClient);
  10101. if (hDeviceInstallDisconnectEvent) {
  10102. CloseHandle(hDeviceInstallDisconnectEvent);
  10103. }
  10104. if (hDeviceInstallEvent) {
  10105. CloseHandle(hDeviceInstallEvent);
  10106. }
  10107. if (hDeviceInstallPipe) {
  10108. CloseHandle(hDeviceInstallPipe);
  10109. }
  10110. if (ProcessInfo.hProcess) {
  10111. CloseHandle(ProcessInfo.hProcess);
  10112. }
  10113. *DeviceInstallClient = NULL;
  10114. } else {
  10115. KdPrintEx((DPFLTR_PNPMGR_ID,
  10116. DBGF_INSTALL,
  10117. "UMPNPMGR: CreateDeviceInstallClient: created new client for Session %d.\n",
  10118. SessionId));
  10119. ASSERT(pDeviceInstallClient);
  10120. ASSERT(pDeviceInstallClient->hEvent);
  10121. ASSERT(pDeviceInstallClient->hPipe);
  10122. ASSERT(pDeviceInstallClient->hProcess);
  10123. ASSERT(pDeviceInstallClient->hDisconnectEvent);
  10124. *DeviceInstallClient = pDeviceInstallClient;
  10125. }
  10126. return bStatus;
  10127. } // CreateDeviceInstallClient
  10128. BOOL
  10129. ConnectDeviceInstallClient(
  10130. IN ULONG SessionId,
  10131. OUT PINSTALL_CLIENT_ENTRY *DeviceInstallClient
  10132. )
  10133. /*++
  10134. Routine Description:
  10135. Retrieves the device install client handles for the specified session,
  10136. if one exists.
  10137. Arguments:
  10138. SessionId - Session for which a device install client should be
  10139. created or connected to.
  10140. DeviceInstallClient - Receives a pointer to receive the a pointer to the
  10141. device install client for this session.
  10142. Return Value:
  10143. Returns TRUE if an existing device install client was found for the
  10144. specified session, FALSE otherwise.
  10145. Notes:
  10146. The InstallClientList lock must be acquired by the caller of this routine.
  10147. --*/
  10148. {
  10149. PINSTALL_CLIENT_ENTRY entry;
  10150. BOOL bClientFound = FALSE;
  10151. //
  10152. // Make sure the specified SessionId is valid.
  10153. //
  10154. ASSERT(SessionId != INVALID_SESSION);
  10155. if (SessionId == INVALID_SESSION) {
  10156. KdPrintEx((DPFLTR_PNPMGR_ID,
  10157. DBGF_INSTALL | DBGF_ERRORS,
  10158. "UMPNPMGR: ConnectDeviceInstallClient: Invalid SessionId %d, exiting!\n",
  10159. SessionId));
  10160. return FALSE;
  10161. }
  10162. //
  10163. // Validate output parameters.
  10164. //
  10165. ASSERT(DeviceInstallClient);
  10166. if (!DeviceInstallClient) {
  10167. return FALSE;
  10168. }
  10169. entry = LocateDeviceInstallClient(SessionId);
  10170. if (entry) {
  10171. //
  10172. // An existing client was found for this session, so we should already
  10173. // have event, pipe, and process handles for it.
  10174. //
  10175. ASSERT(entry->hEvent);
  10176. ASSERT(entry->hPipe);
  10177. ASSERT(entry->hProcess);
  10178. //
  10179. // Make sure the client's process object is in the nonsignalled state,
  10180. // else newdev has already gone away, and we can't use it.
  10181. //
  10182. if (WaitForSingleObject(entry->hProcess, 0) != WAIT_TIMEOUT) {
  10183. //
  10184. // Remove the initial reference to close the handles and remove it
  10185. // from our list.
  10186. //
  10187. ASSERT(entry->RefCount == 1);
  10188. DereferenceDeviceInstallClient(entry);
  10189. } else {
  10190. //
  10191. // If we are reconnecting to a client that was last used during a
  10192. // previous connection to this session, we will not have a disconnect
  10193. // event for it yet, so create one here. If we just created this client
  10194. // during the current connection to this session, we will already have a
  10195. // disconnect event for it.
  10196. //
  10197. if (!entry->hDisconnectEvent) {
  10198. entry->hDisconnectEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  10199. }
  10200. //
  10201. // Either way, make sure we have a disconnect event by now.
  10202. //
  10203. ASSERT(entry->hDisconnectEvent);
  10204. if (entry->hDisconnectEvent) {
  10205. bClientFound = TRUE;
  10206. KdPrintEx((DPFLTR_PNPMGR_ID,
  10207. DBGF_INSTALL,
  10208. "UMPNPMGR: ConnectDeviceInstallClient: found existing client on Session %d.\n",
  10209. SessionId));
  10210. *DeviceInstallClient = entry;
  10211. }
  10212. }
  10213. }
  10214. if (!bClientFound) {
  10215. *DeviceInstallClient = NULL;
  10216. }
  10217. return bClientFound;
  10218. } // ConnectDeviceInstallClient
  10219. BOOL
  10220. DisconnectDeviceInstallClient(
  10221. IN PINSTALL_CLIENT_ENTRY DeviceInstallClient
  10222. )
  10223. /*++
  10224. Routine Description:
  10225. This routine disconnects from the current client-side install process (if
  10226. one exists) by signalling the appropriate hDisconnectEvent and closing the
  10227. handle.
  10228. Arguments:
  10229. DeviceInstallClient - Receives a pointer to the device install client that
  10230. should be disconnected.
  10231. Return Value:
  10232. Returns TRUE if successful, FALSE otherwise.
  10233. Notes:
  10234. The InstallClientList lock must be acquired by the caller of this routine.
  10235. --*/
  10236. {
  10237. PINSTALL_CLIENT_ENTRY entry;
  10238. BOOL bStatus = FALSE;
  10239. ASSERT(DeviceInstallClient);
  10240. if (DeviceInstallClient) {
  10241. ASSERT(DeviceInstallClient->hEvent);
  10242. ASSERT(DeviceInstallClient->hPipe);
  10243. ASSERT(DeviceInstallClient->hProcess);
  10244. //
  10245. // We may or may not have a handle to a diconnect event because we may
  10246. // have an existing client for this session, but not reconnected to it.
  10247. //
  10248. // If we do have an hDisconnectEvent, set the event now since we
  10249. // will otherwise block waiting for newdev.dll to set the
  10250. // hDeviceInstallEvent. Setting the hDisconnectEvent alerts the
  10251. // waiter that the device install was NOT successful, and that it
  10252. // should preserve the device in the install list.
  10253. //
  10254. if (DeviceInstallClient->hDisconnectEvent) {
  10255. SetEvent(DeviceInstallClient->hDisconnectEvent);
  10256. CloseHandle(DeviceInstallClient->hDisconnectEvent);
  10257. DeviceInstallClient->hDisconnectEvent = NULL;
  10258. }
  10259. KdPrintEx((DPFLTR_PNPMGR_ID,
  10260. DBGF_INSTALL,
  10261. "UMPNPMGR: Disconnected from device install client on Console SessionId %d\n",
  10262. DeviceInstallClient->ulSessionId));
  10263. bStatus = TRUE;
  10264. }
  10265. return bStatus;
  10266. } // DisconnectDeviceInstallClient
  10267. PINSTALL_CLIENT_ENTRY
  10268. LocateDeviceInstallClient(
  10269. IN ULONG SessionId
  10270. )
  10271. /*++
  10272. Routine Description:
  10273. This routine locates the client-side install process for a given session (if
  10274. one exists).
  10275. Arguments:
  10276. SessionId - Session whose device install client should be located.
  10277. Return Value:
  10278. Returns a device install client entry if successful, NULL otherwise.
  10279. Note:
  10280. The InstallClientList lock must be acquired by the caller of this routine.
  10281. --*/
  10282. {
  10283. PINSTALL_CLIENT_ENTRY entry, foundEntry = NULL;
  10284. BOOL bClientFound = FALSE;
  10285. //
  10286. // Make sure the specified SessionId is valid.
  10287. //
  10288. ASSERT(SessionId != INVALID_SESSION);
  10289. if (SessionId == INVALID_SESSION) {
  10290. KdPrintEx((DPFLTR_PNPMGR_ID,
  10291. DBGF_INSTALL | DBGF_ERRORS,
  10292. "UMPNPMGR: LocateDeviceInstallClient: Invalid Console SessionId %d, exiting!\n",
  10293. SessionId));
  10294. return FALSE;
  10295. }
  10296. //
  10297. // Search for a client on the specified session.
  10298. //
  10299. for (entry = (PINSTALL_CLIENT_ENTRY)InstallClientList.Next;
  10300. entry != NULL;
  10301. entry = entry->Next) {
  10302. if (entry->ulSessionId == SessionId) {
  10303. //
  10304. // Make sure we only have one entry per session.
  10305. //
  10306. ASSERT(!bClientFound);
  10307. bClientFound = TRUE;
  10308. foundEntry = entry;
  10309. }
  10310. }
  10311. return foundEntry;
  10312. } // LocateDeviceInstallClient
  10313. VOID
  10314. ReferenceDeviceInstallClient(
  10315. IN PINSTALL_CLIENT_ENTRY DeviceInstallClient
  10316. )
  10317. /*++
  10318. Routine Description:
  10319. This routine increments the reference count for a device install client
  10320. entry.
  10321. Parameters:
  10322. DeviceInstallClient - Supplies a pointer to the device install client to be
  10323. referenced.
  10324. Return Value:
  10325. None.
  10326. Note:
  10327. The appropriate synchronization lock must be held on the device install
  10328. client list before this routine can be called
  10329. --*/
  10330. {
  10331. ASSERT(DeviceInstallClient);
  10332. ASSERT(((LONG)DeviceInstallClient->RefCount) > 0);
  10333. KdPrintEx((DPFLTR_PNPMGR_ID,
  10334. DBGF_EVENT | DBGF_INSTALL,
  10335. "UMPNPMGR: ---------------- ReferenceDeviceInstallClient : Session %d [%d --> %d]\n",
  10336. DeviceInstallClient->ulSessionId,
  10337. DeviceInstallClient->RefCount,
  10338. DeviceInstallClient->RefCount + 1));
  10339. DeviceInstallClient->RefCount++;
  10340. return;
  10341. } // ReferenceDeviceInstallClient
  10342. VOID
  10343. DereferenceDeviceInstallClient(
  10344. IN PINSTALL_CLIENT_ENTRY DeviceInstallClient
  10345. )
  10346. /*++
  10347. Routine Description:
  10348. This routine decrements the reference count for a device install client
  10349. entry, removing the entry from the list and freeing the associated memory if
  10350. there are no outstanding reference counts.
  10351. Parameters:
  10352. DeviceInstallClient - Supplies a pointer to the device install client to be
  10353. dereferenced.
  10354. Return Value:
  10355. None.
  10356. Note:
  10357. The appropriate synchronization lock must be held on the device install
  10358. client list before this routine can be called
  10359. --*/
  10360. {
  10361. ASSERT(DeviceInstallClient);
  10362. ASSERT(((LONG)DeviceInstallClient->RefCount) > 0);
  10363. //
  10364. // Avoid over-dereferencing the client.
  10365. //
  10366. if (((LONG)DeviceInstallClient->RefCount) > 0) {
  10367. KdPrintEx((DPFLTR_PNPMGR_ID,
  10368. DBGF_EVENT | DBGF_INSTALL,
  10369. "UMPNPMGR: ---------------- DereferenceDeviceInstallClient: Session %d [%d --> %d]\n",
  10370. DeviceInstallClient->ulSessionId,
  10371. DeviceInstallClient->RefCount,
  10372. DeviceInstallClient->RefCount - 1));
  10373. DeviceInstallClient->RefCount--;
  10374. } else {
  10375. return;
  10376. }
  10377. //
  10378. // If the refcount is zero then the entry no longer needs to be in the list
  10379. // so remove and free it.
  10380. //
  10381. if (DeviceInstallClient->RefCount == 0) {
  10382. BOOL bClientFound = FALSE;
  10383. PINSTALL_CLIENT_ENTRY entry, prev;
  10384. entry = (PINSTALL_CLIENT_ENTRY)InstallClientList.Next;
  10385. prev = NULL;
  10386. while (entry) {
  10387. if (entry == DeviceInstallClient) {
  10388. KdPrintEx((DPFLTR_PNPMGR_ID,
  10389. DBGF_EVENT | DBGF_INSTALL,
  10390. "UMPNPMGR: ---------------- DereferenceDeviceInstallClient: Removing client for Session %d\n",
  10391. entry->ulSessionId));
  10392. //
  10393. // We should have handles to the pipe, event and process objects for
  10394. // the client, because we will close them here.
  10395. //
  10396. ASSERT(entry->hPipe);
  10397. ASSERT(entry->hEvent);
  10398. ASSERT(entry->hProcess);
  10399. //
  10400. // We may or may not have a handle to a diconnect event because we
  10401. // may have an existing client for this session, but not yet
  10402. // connected to it.
  10403. //
  10404. // If we do have an hDisconnectEvent, set the event now since we
  10405. // will otherwise block waiting for newdev.dll to set the
  10406. // hDeviceInstallEvent. Setting the hDisconnectEvent alerts the
  10407. // waiter that the device install was NOT successful, and that it
  10408. // should preserve the device in the install list.
  10409. //
  10410. if (entry->hDisconnectEvent) {
  10411. SetEvent(entry->hDisconnectEvent);
  10412. CloseHandle(entry->hDisconnectEvent);
  10413. }
  10414. //
  10415. // Close the pipe and event handles so that the client will get a
  10416. // ReadFile error and know that we are finished. Close the process
  10417. // handle as well.
  10418. //
  10419. if (entry->hPipe) {
  10420. CloseHandle(entry->hPipe);
  10421. }
  10422. if (entry->hEvent) {
  10423. CloseHandle(entry->hEvent);
  10424. }
  10425. if (entry->hProcess) {
  10426. CloseHandle(entry->hProcess);
  10427. }
  10428. //
  10429. // Remove the device install client entry from the list, and free it
  10430. // now.
  10431. //
  10432. if (prev) {
  10433. prev->Next = entry->Next;
  10434. } else {
  10435. InstallClientList.Next = entry->Next;
  10436. }
  10437. HeapFree(ghPnPHeap, 0, entry);
  10438. if(prev) {
  10439. entry = (PINSTALL_CLIENT_ENTRY)prev->Next;
  10440. } else {
  10441. entry = (PINSTALL_CLIENT_ENTRY)InstallClientList.Next;
  10442. }
  10443. bClientFound = TRUE;
  10444. break;
  10445. }
  10446. prev = entry;
  10447. entry = (PINSTALL_CLIENT_ENTRY)entry->Next;
  10448. }
  10449. ASSERT(bClientFound);
  10450. }
  10451. return;
  10452. } // DereferenceDeviceInstallClient
  10453. BOOL
  10454. DoDeviceInstallClient(
  10455. IN LPWSTR DeviceId,
  10456. IN PULONG SessionId,
  10457. IN ULONG Flags,
  10458. OUT PINSTALL_CLIENT_ENTRY *DeviceInstallClient
  10459. )
  10460. /*++
  10461. Routine Description:
  10462. This routine kicks off a newdev.dll process (if someone is logged in) that
  10463. displays UI informing the user of the status of the server-side device
  10464. installation.
  10465. Arguments:
  10466. DeviceId - Supplies the devnode ID of the device being installed.
  10467. SessionId - Specifies the session that the newdev client is to be launched
  10468. on. If the DEVICE_INSTALL_DISPLAY_ON_CONSOLE flag is
  10469. specified, the specified SessionId is ignored.
  10470. Upon successful return, the SessionId for the the session where
  10471. the device install client was created is returned.
  10472. If unsuccessful, the returned SessionId is INVALID_SESSION,
  10473. (0xFFFFFFFF).
  10474. Flags - Specifies flags describing the behavior of the device install client.
  10475. The following flags are currently defined:
  10476. DEVICE_INSTALL_UI_ONLY - tells newdev.dll whether to do a full
  10477. install or just show UI while umpnpmgr.dll is doing a server
  10478. side install.
  10479. DEVICE_INSTALL_PLAY_SOUND - tells newdev.dll whether to play a
  10480. sound.
  10481. DEVICE_INSTALL_DISPLAY_ON_CONSOLE - if specified, the value
  10482. specified in SessionId will be ignored, and the client will
  10483. always be displayed on the current active console session.
  10484. DeviceInstallClient - Supplies the address of a variable to receive, upon
  10485. success, a pointer to a pointer to a device install client.
  10486. Return Value:
  10487. If the process was successfully created, the return value is TRUE. This
  10488. routine doesn't wait until the process terminates.
  10489. If we couldn't create the process (e.g., because no user was logged in),
  10490. the return value is FALSE.
  10491. Notes:
  10492. None.
  10493. --*/
  10494. {
  10495. BOOL bStatus, bSameDevice = FALSE;
  10496. ULONG DeviceIdSize, ulSize, ulSessionId;
  10497. ULONG InstallFlags;
  10498. PINSTALL_CLIENT_ENTRY pDeviceInstallClient = NULL;
  10499. //
  10500. // Assume failure.
  10501. //
  10502. bStatus = FALSE;
  10503. //
  10504. // Validate output parameters.
  10505. //
  10506. if (!DeviceInstallClient || !SessionId) {
  10507. return FALSE;
  10508. }
  10509. try {
  10510. //
  10511. // Check if we should skip all client side UI.
  10512. //
  10513. if (gbSuppressUI) {
  10514. //
  10515. // If we were launching newdev for client-side installation, log an
  10516. // event to let someone know that we didn't.
  10517. //
  10518. if (!(Flags & DEVICE_INSTALL_UI_ONLY)) {
  10519. KdPrintEx((DPFLTR_PNPMGR_ID,
  10520. DBGF_INSTALL | DBGF_WARNINGS,
  10521. "UMPNPMGR: DoDeviceInstallClient: Client-side newdev UI has been suppressed, exiting.\n"));
  10522. LogWarningEvent(WRN_NEWDEV_UI_SUPPRESSED, 1, DeviceId);
  10523. }
  10524. goto Clean0;
  10525. }
  10526. //
  10527. // Determine the session to use, based on the supplied flags.
  10528. //
  10529. if (Flags & DEVICE_INSTALL_DISPLAY_ON_CONSOLE) {
  10530. ulSessionId = GetActiveConsoleSessionId();
  10531. } else {
  10532. ASSERT(*SessionId != INVALID_SESSION);
  10533. ulSessionId = *SessionId;
  10534. }
  10535. ASSERT(ulSessionId != INVALID_SESSION);
  10536. //
  10537. // If the specified session is not currently connected anywhere, don't
  10538. // bother creating any UI.
  10539. //
  10540. if (!IsSessionConnected(ulSessionId)) {
  10541. KdPrintEx((DPFLTR_PNPMGR_ID,
  10542. DBGF_EVENT,
  10543. "UMPNPMGR: DoDeviceInstallClient: SessionId %d not connected, exiting\n",
  10544. ulSessionId));
  10545. goto Clean0;
  10546. }
  10547. //
  10548. // Lock the client list while we retrieve / create a client to use.
  10549. //
  10550. LockNotifyList(&InstallClientList.Lock);
  10551. //
  10552. // First, try to connect to an existing client already running on this
  10553. // session.
  10554. //
  10555. bStatus = ConnectDeviceInstallClient(ulSessionId,
  10556. &pDeviceInstallClient);
  10557. if (bStatus) {
  10558. //
  10559. // If the client we just reconnected to was client-side installing
  10560. // this same device when it was last disconnected, don't send it
  10561. // again.
  10562. //
  10563. if (!(Flags & DEVICE_INSTALL_UI_ONLY) && (lstrcmpi(pDeviceInstallClient->LastDeviceId, DeviceId) == 0)) {
  10564. KdPrintEx((DPFLTR_PNPMGR_ID,
  10565. DBGF_INSTALL,
  10566. "UMPNPMGR: DoDeviceInstallClient: Session %d already installing %ws\n",
  10567. ulSessionId,
  10568. DeviceId));
  10569. bSameDevice = TRUE;
  10570. }
  10571. } else {
  10572. //
  10573. // Create a new device install client for this session.
  10574. //
  10575. bStatus = CreateDeviceInstallClient(ulSessionId,
  10576. &pDeviceInstallClient);
  10577. }
  10578. if (bStatus) {
  10579. //
  10580. // The client should only have the initial reference from when it
  10581. // was added to the list, since any use of the client is done on
  10582. // this single install thread.
  10583. //
  10584. ASSERT(pDeviceInstallClient);
  10585. ASSERT(pDeviceInstallClient->RefCount == 1);
  10586. //
  10587. // Keep track of both client and server flags.
  10588. //
  10589. pDeviceInstallClient->ulInstallFlags = Flags;
  10590. //
  10591. // Reference the device install client while it is in use. The
  10592. // caller must remove this reference when it is done with it.
  10593. //
  10594. ReferenceDeviceInstallClient(pDeviceInstallClient);
  10595. }
  10596. UnlockNotifyList(&InstallClientList.Lock);
  10597. if (!bStatus || bSameDevice) {
  10598. //
  10599. // If we don't have a client, or we don't need to resend the device
  10600. // instance to install, we're done.
  10601. //
  10602. goto Clean0;
  10603. }
  10604. //
  10605. // Filter out the install flags that the client doesn't know about.
  10606. //
  10607. InstallFlags = (Flags & DEVICE_INSTALL_CLIENT_MASK);
  10608. DeviceIdSize = (lstrlen(DeviceId) + 1) * sizeof(WCHAR);
  10609. //
  10610. // Make sure we reset the device install event since we will block waiting for
  10611. // newdev.dll to set this event to let us know that it is finished with the current
  10612. // installation.
  10613. //
  10614. if (pDeviceInstallClient->hEvent) {
  10615. ResetEvent(pDeviceInstallClient->hEvent);
  10616. }
  10617. //
  10618. // When sending stuff to newdev.dll over the device install pipe it expects
  10619. // two ULONGs followed by the DeviceID. The first ULONG is the Flags which
  10620. // tells newdev whether we are doing a UI only install or a full install.
  10621. // The next ULONG is the size of the Device ID and then we send the DeviceID.
  10622. //
  10623. if (WriteFile(pDeviceInstallClient->hPipe,
  10624. &InstallFlags,
  10625. sizeof(InstallFlags),
  10626. &ulSize,
  10627. NULL
  10628. )) {
  10629. if (WriteFile(pDeviceInstallClient->hPipe,
  10630. &DeviceIdSize,
  10631. sizeof(DeviceIdSize),
  10632. &ulSize,
  10633. NULL
  10634. )) {
  10635. if (WriteFile(pDeviceInstallClient->hPipe,
  10636. DeviceId,
  10637. DeviceIdSize,
  10638. &ulSize,
  10639. NULL
  10640. )) {
  10641. bStatus = TRUE;
  10642. } else {
  10643. LogErrorEvent(ERR_WRITING_SERVER_INSTALL_PIPE, GetLastError(), 0);
  10644. }
  10645. } else {
  10646. LogErrorEvent(ERR_WRITING_SERVER_INSTALL_PIPE, GetLastError(), 0);
  10647. }
  10648. } else {
  10649. LogErrorEvent(ERR_WRITING_SERVER_INSTALL_PIPE, GetLastError(), 0);
  10650. }
  10651. //
  10652. // Note that we don't remove the reference placed on the install client
  10653. // entry while it was in use, because it will be handed back to the
  10654. // caller, who will wait on the client's event and process handles. The
  10655. // caller should remove the reference when it is no longer using these.
  10656. // Removing the final reference will cause the client to be closed.
  10657. //
  10658. } except(EXCEPTION_EXECUTE_HANDLER) {
  10659. KdPrintEx((DPFLTR_PNPMGR_ID,
  10660. DBGF_ERRORS | DBGF_WARNINGS,
  10661. "UMPNPMGR: Exception during DoDeviceInstallClient!\n"));
  10662. ASSERT(0);
  10663. bStatus = FALSE;
  10664. //
  10665. // Reference the following variable so the compiler will respect
  10666. // statement ordering w.r.t. its assignment.
  10667. //
  10668. pDeviceInstallClient = pDeviceInstallClient;
  10669. }
  10670. Clean0:
  10671. if (!bStatus) {
  10672. //
  10673. // If we had a device install client at some point, but failed to send
  10674. // it the request, remove the reference we placed on it.
  10675. //
  10676. if (pDeviceInstallClient) {
  10677. LockNotifyList(&InstallClientList.Lock);
  10678. DereferenceDeviceInstallClient(pDeviceInstallClient);
  10679. UnlockNotifyList(&InstallClientList.Lock);
  10680. }
  10681. //
  10682. // Let the caller know there isn't a device install client handling
  10683. // this request.
  10684. //
  10685. *SessionId = INVALID_SESSION;
  10686. *DeviceInstallClient = NULL;
  10687. } else {
  10688. //
  10689. // Make sure we're returning valid client information.
  10690. //
  10691. ASSERT(pDeviceInstallClient);
  10692. ASSERT(pDeviceInstallClient->hEvent);
  10693. ASSERT(pDeviceInstallClient->hPipe);
  10694. ASSERT(pDeviceInstallClient->hProcess);
  10695. ASSERT(pDeviceInstallClient->hDisconnectEvent);
  10696. ASSERT(pDeviceInstallClient->ulSessionId != INVALID_SESSION);
  10697. *SessionId = pDeviceInstallClient->ulSessionId;
  10698. *DeviceInstallClient = pDeviceInstallClient;
  10699. }
  10700. return bStatus;
  10701. } // DoDeviceInstallClient
  10702. VOID
  10703. DoRunOnce(
  10704. VOID
  10705. )
  10706. /*++
  10707. Routine Description:
  10708. This routine performs server-side processing of the RunOnce entries that
  10709. have been accumulated by setupapi. The RunOnce node list will be empty
  10710. upon return.
  10711. Arguments:
  10712. None.
  10713. Return Value:
  10714. None.
  10715. --*/
  10716. {
  10717. PPSP_RUNONCE_NODE RunOnceNode;
  10718. HINSTANCE hLib;
  10719. CHAR AnsiBuffer[MAX_PATH * 2];
  10720. PSTR EndPtr;
  10721. RUNDLLPROCA fpRunDllProcA;
  10722. RUNDLLPROCW fpRunDllProcW;
  10723. RunOnceNode = fpAccessRunOnceNodeList();
  10724. //
  10725. // Process each node in the list.
  10726. //
  10727. while(RunOnceNode) {
  10728. hLib = NULL;
  10729. try {
  10730. //
  10731. // First, load the DLL (setupapi already did the signature
  10732. // verification for us, so this should be safe).
  10733. //
  10734. hLib = LoadLibrary(RunOnceNode->DllFullPath);
  10735. if(!hLib) {
  10736. goto clean0;
  10737. }
  10738. //
  10739. // First, try to retrieve the 'W' (Unicode) version of the entrypoint.
  10740. //
  10741. lstrcpyA(AnsiBuffer, RunOnceNode->DllEntryPointName);
  10742. EndPtr = AnsiBuffer + lstrlenA(AnsiBuffer);
  10743. *EndPtr = 'W';
  10744. *(EndPtr+1) = '\0';
  10745. fpRunDllProcW = (RUNDLLPROCW)GetProcAddress(hLib, AnsiBuffer);
  10746. if(!fpRunDllProcW) {
  10747. //
  10748. // Could't find unicode entrypt, try 'A' decorated one
  10749. //
  10750. *EndPtr = 'A';
  10751. fpRunDllProcA = (RUNDLLPROCA)GetProcAddress(hLib, AnsiBuffer);
  10752. if(!fpRunDllProcA) {
  10753. //
  10754. // Couldn't find 'A' decorated entrypt, try undecorated name
  10755. // undecorated entrypts are assumed to be ANSI
  10756. //
  10757. *EndPtr = '\0';
  10758. fpRunDllProcA = (RUNDLLPROCA)GetProcAddress(hLib, AnsiBuffer);
  10759. }
  10760. }
  10761. //
  10762. // We shoulda found one of these...
  10763. //
  10764. ASSERT(fpRunDllProcW || fpRunDllProcA);
  10765. if(fpRunDllProcW) {
  10766. //
  10767. // Re-use our ANSI buffer to hold a writeable copy of our
  10768. // DLL argument string.
  10769. //
  10770. lstrcpy((LPWSTR)AnsiBuffer, RunOnceNode->DllParams);
  10771. fpRunDllProcW(NULL, ghInst, (LPWSTR)AnsiBuffer, SW_HIDE);
  10772. } else if(fpRunDllProcA) {
  10773. //
  10774. // Need to convert the arg string to ANSI first...
  10775. //
  10776. WideCharToMultiByte(CP_ACP,
  10777. 0, // default composite char behavior
  10778. RunOnceNode->DllParams,
  10779. -1,
  10780. AnsiBuffer,
  10781. sizeof(AnsiBuffer),
  10782. NULL,
  10783. NULL
  10784. );
  10785. fpRunDllProcA(NULL, ghInst, AnsiBuffer, SW_HIDE);
  10786. }
  10787. clean0:
  10788. NOTHING;
  10789. } except(EXCEPTION_EXECUTE_HANDLER) {
  10790. KdPrintEx((DPFLTR_PNPMGR_ID,
  10791. DBGF_ERRORS | DBGF_INSTALL,
  10792. "UMPNPMGR: Exception during DoRunOnce!\n"));
  10793. ASSERT(0);
  10794. //
  10795. // Reference the following variable so the compiler will respect
  10796. // statement ordering w.r.t. its assignment.
  10797. //
  10798. hLib = hLib;
  10799. }
  10800. if(hLib) {
  10801. FreeLibrary(hLib);
  10802. }
  10803. RunOnceNode = RunOnceNode->Next;
  10804. }
  10805. //
  10806. // Free all the members in the list.
  10807. //
  10808. fpDestroyRunOnceNodeList();
  10809. return;
  10810. } // DoRunOnce
  10811. DWORD
  10812. SessionNotificationHandler(
  10813. IN DWORD EventType,
  10814. IN PWTSSESSION_NOTIFICATION SessionNotification
  10815. )
  10816. /*++
  10817. Routine Description:
  10818. This routine handles console switch events.
  10819. Arguments:
  10820. EventType - The type of event that has occurred.
  10821. SessionNotification - Additional event information.
  10822. Return Value:
  10823. If successful, the return value is NO_ERROR.
  10824. If failure, the return value is a Win32 error code indicating the cause of
  10825. failure.
  10826. Notes:
  10827. Session change notification events are used to determine when there is a
  10828. session with a logged on user currently connected to the Console. When a
  10829. user session is connected to the Console, we signal the "logged on" event,
  10830. which will wake the device installation thread to perform any pending
  10831. client-side device install events. When there is no user session connected
  10832. to the Console, the "logged on" event is reset. The "logged on" event may
  10833. also be set/reset for logon/logoff events to session 0 by PNP_ReportLogOn /
  10834. PnpConsoleCtrlHandler, in the event that Terminal Services are not
  10835. available.
  10836. --*/
  10837. {
  10838. HANDLE hUserToken = INVALID_HANDLE_VALUE;
  10839. LPTSTR pszUserName = NULL;
  10840. DWORD dwSize = 0;
  10841. PINSTALL_CLIENT_ENTRY pDeviceInstallClient;
  10842. //
  10843. // Validate the session change notification structure.
  10844. //
  10845. ASSERT(SessionNotification);
  10846. ASSERT(SessionNotification->cbSize >= sizeof(WTSSESSION_NOTIFICATION));
  10847. if ((!ARGUMENT_PRESENT(SessionNotification)) ||
  10848. (SessionNotification->cbSize < sizeof(WTSSESSION_NOTIFICATION))) {
  10849. return ERROR_INVALID_PARAMETER;
  10850. }
  10851. switch (EventType) {
  10852. case WTS_CONSOLE_CONNECT:
  10853. //
  10854. // The notification was sent because the specified session was
  10855. // connected to the Console.
  10856. //
  10857. KdPrintEx((DPFLTR_PNPMGR_ID,
  10858. DBGF_EVENT | DBGF_INSTALL,
  10859. "UMPNPMGR: WTS_CONSOLE_CONNECT: "
  10860. "SessionId %d\n",
  10861. SessionNotification->dwSessionId));
  10862. //
  10863. // Keep track globally of the current active console session, and
  10864. // signal that it's safe to access it.
  10865. //
  10866. // NOTE - we must set the ghActiveConsoleSessionEvent here, prior to
  10867. // calling IsConsoleSession below, which waits on it, else we will
  10868. // deadlock out service's control handler.
  10869. //
  10870. gActiveConsoleSessionId = (ULONG)SessionNotification->dwSessionId;
  10871. if (ghActiveConsoleSessionEvent) {
  10872. SetEvent(ghActiveConsoleSessionEvent);
  10873. }
  10874. //
  10875. // If the session just connected to the Console already has a logged
  10876. // on user, signal the "logged on" event.
  10877. //
  10878. if (IsConsoleSession((ULONG)SessionNotification->dwSessionId) &&
  10879. IsUserLoggedOnSession((ULONG)SessionNotification->dwSessionId)) {
  10880. if (InstallEvents[LOGGED_ON_EVENT]) {
  10881. SetEvent(InstallEvents[LOGGED_ON_EVENT]);
  10882. KdPrintEx((DPFLTR_PNPMGR_ID,
  10883. DBGF_EVENT | DBGF_INSTALL,
  10884. "UMPNPMGR: WTS_CONSOLE_CONNECT: "
  10885. "SetEvent LOGGED_ON_EVENT\n"));
  10886. }
  10887. }
  10888. break;
  10889. case WTS_CONSOLE_DISCONNECT:
  10890. //
  10891. // The notification was sent because the specified session
  10892. // was disconnected from the Console.
  10893. //
  10894. KdPrintEx((DPFLTR_PNPMGR_ID,
  10895. DBGF_EVENT | DBGF_INSTALL,
  10896. "UMPNPMGR: WTS_CONSOLE_DISCONNECT: "
  10897. "SessionId %d\n",
  10898. SessionNotification->dwSessionId));
  10899. //
  10900. // Check if the session just disconnected from the "Console" has a
  10901. // logged on user.
  10902. //
  10903. if (IsConsoleSession((ULONG)SessionNotification->dwSessionId) &&
  10904. IsUserLoggedOnSession((ULONG)SessionNotification->dwSessionId)) {
  10905. //
  10906. // Reset the "logged on" event.
  10907. //
  10908. if (InstallEvents[LOGGED_ON_EVENT]) {
  10909. KdPrintEx((DPFLTR_PNPMGR_ID,
  10910. DBGF_EVENT | DBGF_INSTALL,
  10911. "UMPNPMGR: WTS_CONSOLE_DISCONNECT: "
  10912. "ResetEvent LOGGED_ON_EVENT\n"));
  10913. ResetEvent(InstallEvents[LOGGED_ON_EVENT]);
  10914. }
  10915. //
  10916. // Since this is a console switch event, only do something with
  10917. // a device install client on the console session if it's
  10918. // behavior was specifically designated for the console (i.e. -
  10919. // it was put on this session because it was the active console
  10920. // session at the time).
  10921. //
  10922. LockNotifyList(&InstallClientList.Lock);
  10923. pDeviceInstallClient = LocateDeviceInstallClient((ULONG)SessionNotification->dwSessionId);
  10924. if ((pDeviceInstallClient) &&
  10925. (pDeviceInstallClient->ulInstallFlags & DEVICE_INSTALL_DISPLAY_ON_CONSOLE)) {
  10926. if (pDeviceInstallClient->ulInstallFlags & DEVICE_INSTALL_UI_ONLY) {
  10927. //
  10928. // If it was just for UI only, dereference it to make it
  10929. // go away when it's no longer in use.
  10930. //
  10931. DereferenceDeviceInstallClient(pDeviceInstallClient);
  10932. } else {
  10933. //
  10934. // Otherwise, it is a legitimate client-side
  10935. // installation in progress, so just disconnect from it.
  10936. // This does not remove a reference because we want it
  10937. // to stay around in case the session is reconnected to
  10938. // and the device still needs to be installed, - or
  10939. // until we find out that there are no more devices to
  10940. // install, in which case we'll close it.
  10941. //
  10942. DisconnectDeviceInstallClient(pDeviceInstallClient);
  10943. }
  10944. }
  10945. UnlockNotifyList(&InstallClientList.Lock);
  10946. }
  10947. //
  10948. // The current active console session is invalid until we receive a
  10949. // subsequent console connect event. Reset the event.
  10950. //
  10951. // NOTE - we must reset the ghActiveConsoleSessionEvent here, after
  10952. // calling IsConsoleSession above, which waits on it, else we will
  10953. // deadlock out service's control handler.
  10954. //
  10955. if (ghActiveConsoleSessionEvent) {
  10956. ResetEvent(ghActiveConsoleSessionEvent);
  10957. }
  10958. gActiveConsoleSessionId = INVALID_SESSION;
  10959. break;
  10960. case WTS_REMOTE_CONNECT:
  10961. //
  10962. // The specified session was connected remotely.
  10963. //
  10964. KdPrintEx((DPFLTR_PNPMGR_ID,
  10965. DBGF_EVENT | DBGF_INSTALL,
  10966. "UMPNPMGR: WTS_REMOTE_CONNECT: "
  10967. "SessionId %d\n",
  10968. SessionNotification->dwSessionId));
  10969. if (((ULONG)SessionNotification->dwSessionId == MAIN_SESSION) &&
  10970. (IsUserLoggedOnSession((ULONG)SessionNotification->dwSessionId)) &&
  10971. (!IsFastUserSwitchingEnabled())) {
  10972. //
  10973. // If the remote session that was just connected from the "Console"
  10974. // has a logged on user, signal the "logged on" event.
  10975. //
  10976. if (InstallEvents[LOGGED_ON_EVENT]) {
  10977. SetEvent(InstallEvents[LOGGED_ON_EVENT]);
  10978. KdPrintEx((DPFLTR_PNPMGR_ID,
  10979. DBGF_EVENT | DBGF_INSTALL,
  10980. "UMPNPMGR: WTS_REMOTE_CONNECT: "
  10981. "SetEvent LOGGED_ON_EVENT\n"));
  10982. }
  10983. }
  10984. break;
  10985. case WTS_REMOTE_DISCONNECT:
  10986. //
  10987. // The specified session was disconnected remotely.
  10988. //
  10989. KdPrintEx((DPFLTR_PNPMGR_ID,
  10990. DBGF_EVENT | DBGF_INSTALL,
  10991. "UMPNPMGR: WTS_REMOTE_DISCONNECT: "
  10992. "SessionId %d\n",
  10993. SessionNotification->dwSessionId));
  10994. if (((ULONG)SessionNotification->dwSessionId == MAIN_SESSION) &&
  10995. (IsUserLoggedOnSession((ULONG)SessionNotification->dwSessionId)) &&
  10996. (!IsFastUserSwitchingEnabled())) {
  10997. //
  10998. // If the remote session that was disconnected from the "Console"
  10999. // has a logged on user, reset the "logged on" event.
  11000. //
  11001. if (InstallEvents[LOGGED_ON_EVENT]) {
  11002. ResetEvent(InstallEvents[LOGGED_ON_EVENT]);
  11003. KdPrintEx((DPFLTR_PNPMGR_ID,
  11004. DBGF_EVENT | DBGF_INSTALL,
  11005. "UMPNPMGR: WTS_REMOTE_DISCONNECT: "
  11006. "ResetEvent LOGGED_ON_EVENT\n"));
  11007. }
  11008. //
  11009. // Since this remote session is being treated as the console,
  11010. // only do something with a device install client if it's
  11011. // behavior was NOT specifically designated for the console
  11012. // (i.e. - it was put on this session because it was the active
  11013. // console session at the time).
  11014. //
  11015. LockNotifyList(&InstallClientList.Lock);
  11016. pDeviceInstallClient = LocateDeviceInstallClient((ULONG)SessionNotification->dwSessionId);
  11017. if ((pDeviceInstallClient) &&
  11018. ((pDeviceInstallClient->ulInstallFlags & DEVICE_INSTALL_DISPLAY_ON_CONSOLE) == 0)) {
  11019. if (pDeviceInstallClient->ulInstallFlags & DEVICE_INSTALL_UI_ONLY) {
  11020. //
  11021. // If it was just for UI only, dereference it to make it
  11022. // go away when it's no longer in use.
  11023. //
  11024. DereferenceDeviceInstallClient(pDeviceInstallClient);
  11025. } else {
  11026. //
  11027. // Otherwise, it is a legitimate client-side
  11028. // installation in progress, so just disconnect from it.
  11029. // This does not remove a reference because we want it
  11030. // to stay around in case the session is reconnected to
  11031. // and the device still needs to be installed, - or
  11032. // until we find out that there are no more devices to
  11033. // install, in which case we'll close it.
  11034. //
  11035. DisconnectDeviceInstallClient(pDeviceInstallClient);
  11036. }
  11037. }
  11038. UnlockNotifyList(&InstallClientList.Lock);
  11039. }
  11040. break;
  11041. case WTS_SESSION_UNLOCK:
  11042. //
  11043. // The interactive windowstation on the specified session was unlocked.
  11044. //
  11045. KdPrintEx((DPFLTR_PNPMGR_ID,
  11046. DBGF_EVENT | DBGF_INSTALL,
  11047. "UMPNPMGR: WTS_SESSION_UNLOCK: "
  11048. "SessionId %d\n",
  11049. SessionNotification->dwSessionId));
  11050. if (SessionNotification->dwSessionId == MAIN_SESSION) {
  11051. //
  11052. // For the main session, Terminal Services may or may not be
  11053. // available, so we keep track of this state ourselves.
  11054. //
  11055. gbMainSessionLocked = FALSE;
  11056. }
  11057. if (IsFastUserSwitchingEnabled()) {
  11058. //
  11059. // When Fast User Switching is enabled, unlocking the windowstation
  11060. // is a return from the "Welcome" desktop, so we treat it as a
  11061. // logon ...
  11062. //
  11063. //
  11064. // If this is a logon to the "Console" session, signal the event that
  11065. // indicates a Console user is currently logged on.
  11066. //
  11067. // NOTE: we check gActiveConsoleSessionId directly here, without
  11068. // waiting on the corresponding event because this unlock may
  11069. // happen during a Console session change for another session,
  11070. // in which case we will hang here in the service control
  11071. // handler, waiting for the event to be set - and not be able to
  11072. // receive the service control that actually lets us set the
  11073. // event!!! Synchronization is not so important here because we
  11074. // are not using the session for anything, just comparing
  11075. // against it. If a session change really is in progress, this
  11076. // session can't be the Console session anyways.
  11077. //
  11078. // Also, since Fast User Switching is enabled, we can just
  11079. // compare against the active Console session id, and not bother
  11080. // with the session 0 thing.
  11081. //
  11082. if (SessionNotification->dwSessionId == gActiveConsoleSessionId) {
  11083. if (InstallEvents[LOGGED_ON_EVENT]) {
  11084. SetEvent(InstallEvents[LOGGED_ON_EVENT]);
  11085. KdPrintEx((DPFLTR_PNPMGR_ID,
  11086. DBGF_EVENT | DBGF_INSTALL,
  11087. "UMPNPMGR: WTS_SESSION_UNLOCK with FUS: "
  11088. "SetEvent LOGGED_ON_EVENT\n"));
  11089. }
  11090. }
  11091. } else {
  11092. //
  11093. // When Fast User Switching is not enabled, we don't do anything
  11094. // special when the winstation is unlocked.
  11095. //
  11096. // No-FUS, no-muss.
  11097. NOTHING;
  11098. }
  11099. break;
  11100. case WTS_SESSION_LOGON:
  11101. //
  11102. // NTRAID #181685-2000/09/11-jamesca:
  11103. //
  11104. // Currently, terminal services sends notification of logons to
  11105. // "remote" sessions before the server's process creation thread
  11106. // is running. If we set the logged on event, and there are
  11107. // devices waiting to be installed, we will immediately call
  11108. // CreateProcessAsUser on that session, which will fail. As a
  11109. // (temporary?) workaround, we'll continue to use PNP_ReportLogOn
  11110. // to receive logon notification from userinit.exe, now for all
  11111. // sessions.
  11112. //
  11113. break;
  11114. case WTS_SESSION_LOCK:
  11115. //
  11116. // The interactive windowstation on the specified session was locked.
  11117. //
  11118. KdPrintEx((DPFLTR_PNPMGR_ID,
  11119. DBGF_EVENT | DBGF_INSTALL,
  11120. "UMPNPMGR: WTS_SESSION_LOCK: "
  11121. "SessionId %d\n",
  11122. SessionNotification->dwSessionId));
  11123. if (SessionNotification->dwSessionId == MAIN_SESSION) {
  11124. //
  11125. // For the main session, Terminal Services may or may not be
  11126. // available, so we keep track of this state ourselves.
  11127. //
  11128. gbMainSessionLocked = TRUE;
  11129. }
  11130. if (IsFastUserSwitchingEnabled()) {
  11131. //
  11132. // When Fast User Switching is enabled, locking the windowstation
  11133. // displays the "Welcome" desktop, potentially allowing a different
  11134. // user to logon, so we treat it as a logoff ...
  11135. //
  11136. //
  11137. // If this is a "logoff" from the "Console" session, reset the event
  11138. // that indicates a Console user is currently logged on.
  11139. //
  11140. //
  11141. // NOTE: we check gActiveConsoleSessionId directly here, without
  11142. // waiting on the corresponding event because this lock may
  11143. // happen during a Console session change for another session,
  11144. // in which case we will hang here in the service control
  11145. // handler, waiting for the event to be set - and not be able to
  11146. // receive the service control that actually lets us set the
  11147. // event!!! Synchronization is not so important here because we
  11148. // are not using the session for anything, just comparing
  11149. // against it. If a session change really is in progress, this
  11150. // session can't be the Console session anyways.
  11151. //
  11152. // Also, since Fast User Switching is enabled, we can just
  11153. // compare against the active Console session id, and not bother
  11154. // with the session 0 thing.
  11155. //
  11156. if (SessionNotification->dwSessionId == gActiveConsoleSessionId) {
  11157. if (InstallEvents[LOGGED_ON_EVENT]) {
  11158. ResetEvent(InstallEvents[LOGGED_ON_EVENT]);
  11159. KdPrintEx((DPFLTR_PNPMGR_ID,
  11160. DBGF_EVENT | DBGF_INSTALL,
  11161. "UMPNPMGR: WTS_SESSION_LOCK with FUS: "
  11162. "ResetEvent LOGGED_ON_EVENT\n"));
  11163. }
  11164. }
  11165. } else {
  11166. //
  11167. // When Fast User Switching is not enabled, we don't do anything
  11168. // special when the winstation is locked.
  11169. //
  11170. // No-FUS, no-muss.
  11171. NOTHING;
  11172. }
  11173. break;
  11174. case WTS_SESSION_LOGOFF:
  11175. //
  11176. // A user logged off from the specified session.
  11177. //
  11178. KdPrintEx((DPFLTR_PNPMGR_ID,
  11179. DBGF_EVENT | DBGF_INSTALL,
  11180. "UMPNPMGR: WTS_SESSION_LOGOFF: "
  11181. "SessionId %d\n",
  11182. SessionNotification->dwSessionId));
  11183. if (((ULONG)SessionNotification->dwSessionId != MAIN_SESSION) &&
  11184. ((ULONG)SessionNotification->dwSessionId == gActiveConsoleSessionId)) {
  11185. //
  11186. // If the logoff occurred on the Console session (but not
  11187. // session 0), reset the "logged on" event.
  11188. // Session 0 logoffs are still handled by PnpConsoleCtrlHandler.
  11189. //
  11190. if (InstallEvents[LOGGED_ON_EVENT]) {
  11191. ResetEvent(InstallEvents[LOGGED_ON_EVENT]);
  11192. KdPrintEx((DPFLTR_PNPMGR_ID,
  11193. DBGF_EVENT | DBGF_INSTALL,
  11194. "UMPNPMGR: WTS_SESSION_LOGOFF: "
  11195. "ResetEvent LOGGED_ON_EVENT\n",
  11196. SessionNotification->dwSessionId));
  11197. }
  11198. //
  11199. // If we currently have a device install UI client on this session,
  11200. // we should attempt to close it now, before logging off.
  11201. //
  11202. LockNotifyList(&InstallClientList.Lock);
  11203. pDeviceInstallClient = LocateDeviceInstallClient((ULONG)SessionNotification->dwSessionId);
  11204. if (pDeviceInstallClient) {
  11205. DereferenceDeviceInstallClient(pDeviceInstallClient);
  11206. }
  11207. UnlockNotifyList(&InstallClientList.Lock);
  11208. }
  11209. break;
  11210. default:
  11211. //
  11212. // Unrecognized session change notification event.
  11213. //
  11214. KdPrintEx((DPFLTR_PNPMGR_ID,
  11215. DBGF_EVENT | DBGF_INSTALL | DBGF_ERRORS,
  11216. "UMPNPMGR: Unknown SERVICE_CONTROL_SESSIONCHANGE event type (%d) "
  11217. "received for SessionId %d!!\n",
  11218. EventType,
  11219. SessionNotification->dwSessionId));
  11220. break;
  11221. }
  11222. return NO_ERROR;
  11223. } // SessionNotificationHandler
  11224. BOOL
  11225. IsUserLoggedOnSession(
  11226. IN ULONG ulSessionId
  11227. )
  11228. /*++
  11229. Routine Description:
  11230. Checks to see if a user is logged on to the specified session.
  11231. Arguments:
  11232. ulSessionId - The session to be checked.
  11233. Return Value:
  11234. Returns TRUE if a user is currently logged on to the specified session,
  11235. FALSE otherwise.
  11236. --*/
  11237. {
  11238. BOOL bResult = FALSE;
  11239. LPTSTR pszUserName;
  11240. DWORD dwSize;
  11241. if (ulSessionId == MAIN_SESSION) {
  11242. //
  11243. // For the main session, Terminal Services may or may not be available,
  11244. // so we just check if we currently have a handle to the user token.
  11245. //
  11246. ASSERT(gTokenLock.LockHandles);
  11247. LockPrivateResource(&gTokenLock);
  11248. if (ghUserToken != NULL) {
  11249. bResult = TRUE;
  11250. }
  11251. UnlockPrivateResource(&gTokenLock);
  11252. } else {
  11253. //
  11254. // If the specified session is not the main session,
  11255. // query the session information to see if there is already a
  11256. // user logged on.
  11257. //
  11258. if (fpWTSQuerySessionInformation && fpWTSFreeMemory) {
  11259. pszUserName = NULL;
  11260. dwSize = 0;
  11261. if (fpWTSQuerySessionInformation((HANDLE)WTS_CURRENT_SERVER_HANDLE,
  11262. (DWORD)ulSessionId,
  11263. (WTS_INFO_CLASS)WTSUserName,
  11264. (LPWSTR*)&pszUserName,
  11265. &dwSize)) {
  11266. if ((pszUserName != NULL) && (lstrlen(pszUserName) != 0)) {
  11267. bResult = TRUE;
  11268. }
  11269. //
  11270. // Free the supplied buffer
  11271. //
  11272. if (pszUserName) {
  11273. fpWTSFreeMemory((PVOID)pszUserName);
  11274. }
  11275. } else {
  11276. KdPrintEx((DPFLTR_PNPMGR_ID,
  11277. DBGF_WARNINGS,
  11278. "UMPNPMGR: WTSQuerySessionInformation failed for SessionId %d, "
  11279. "error = %d\n",
  11280. ulSessionId, GetLastError()));
  11281. }
  11282. }
  11283. }
  11284. return bResult;
  11285. } // IsUserLoggedOnSession
  11286. BOOL
  11287. IsSessionConnected(
  11288. IN ULONG ulSessionId
  11289. )
  11290. /*++
  11291. Routine Description:
  11292. Checks if the specified session is connected.
  11293. Arguments:
  11294. ulSessionId - The session to be checked.
  11295. Return Value:
  11296. Returns TRUE if the specified session is currently connected, FALSE
  11297. otherwise.
  11298. Notes:
  11299. This routine assumes that the specified session is connected, unless we can
  11300. poitively determine that it is not. i.e., if Terminal Services are not
  11301. available, it is assumed that the specified session is connected.
  11302. --*/
  11303. {
  11304. BOOL bResult = TRUE;
  11305. LPTSTR pBuffer;
  11306. DWORD dwSize;
  11307. //
  11308. // Query the specified session.
  11309. //
  11310. if (fpWTSQuerySessionInformation && fpWTSFreeMemory) {
  11311. pBuffer = NULL;
  11312. dwSize = 0;
  11313. if (fpWTSQuerySessionInformation((HANDLE)WTS_CURRENT_SERVER_HANDLE,
  11314. (DWORD)ulSessionId,
  11315. (WTS_INFO_CLASS)WTSConnectState,
  11316. (LPWSTR*)&pBuffer,
  11317. &dwSize)) {
  11318. //
  11319. // The session state must be either Active or Connected.
  11320. //
  11321. if ((pBuffer == NULL) ||
  11322. ((((INT)*pBuffer) != WTSActive) &&
  11323. (((INT)*pBuffer) != WTSConnected))) {
  11324. //
  11325. // The specified session is not currently connected.
  11326. //
  11327. bResult = FALSE;
  11328. }
  11329. //
  11330. // Free the supplied buffer
  11331. //
  11332. if (pBuffer) {
  11333. fpWTSFreeMemory((PVOID)pBuffer);
  11334. }
  11335. }
  11336. } else {
  11337. //
  11338. // If the above TS entrypoints are not set, terminal services is not
  11339. // enabled. This must be session 0, and it must be connected.
  11340. //
  11341. ASSERT(ulSessionId == MAIN_SESSION);
  11342. }
  11343. return bResult;
  11344. } // IsSessionConnected
  11345. BOOL
  11346. IsSessionLocked(
  11347. IN ULONG ulSessionId
  11348. )
  11349. /*++
  11350. Routine Description:
  11351. Checks to see if the interactive windowstation for the specified session is
  11352. locked.
  11353. Arguments:
  11354. ulSessionId - The session to be checked.
  11355. Return Value:
  11356. Returns TRUE if the interactive windowstation for the specified session is
  11357. locked, FALSE otherwise.
  11358. --*/
  11359. {
  11360. BOOL bLocked = FALSE;
  11361. DWORD dwReturnLength;
  11362. if (ulSessionId == MAIN_SESSION) {
  11363. //
  11364. // For the main session, Terminal Services may or may not be available,
  11365. // so we just check our internal state variable.
  11366. //
  11367. bLocked = gbMainSessionLocked;
  11368. } else {
  11369. //
  11370. // If the specified session is not the main session, query Terminal
  11371. // Services for that session's WinStation information.
  11372. //
  11373. try {
  11374. if (!fpWinStationQueryInformationW(SERVERNAME_CURRENT,
  11375. ulSessionId,
  11376. WinStationLockedState,
  11377. (PVOID)&bLocked,
  11378. sizeof(bLocked),
  11379. &dwReturnLength)) {
  11380. bLocked = FALSE;
  11381. KdPrintEx((DPFLTR_PNPMGR_ID,
  11382. DBGF_WARNINGS,
  11383. "UMPNPMGR: WinStationQueryInformation failed for SessionId %d, "
  11384. "error = %d\n",
  11385. ulSessionId, GetLastError()));
  11386. }
  11387. } except(EXCEPTION_EXECUTE_HANDLER) {
  11388. bLocked = FALSE;
  11389. }
  11390. }
  11391. return bLocked;
  11392. } // IsSessionLocked
  11393. BOOL
  11394. IsConsoleSession(
  11395. IN ULONG ulSessionId
  11396. )
  11397. /*++
  11398. Routine Description:
  11399. Checks to see if the specified session is the "Console" session.
  11400. When Terminal Services Fast User Switching is enabled, this means that the
  11401. session is the session connected to the physical display. When Fast User
  11402. Switching is disabled, this means that the session is Session 0.
  11403. Arguments:
  11404. ulSessionId - The session to be checked.
  11405. Return Value:
  11406. Returns TRUE if the specified session should currently be considered the
  11407. "Console" session.
  11408. Notes:
  11409. Note that this routine may potentially wait in GetActiveConsoleSessionId(),
  11410. on the event we use to guard access to the active console session. Because
  11411. of that, this routine should not be called in cases where it prevents a
  11412. console connect or console disconnect from taking place, unless the event is
  11413. known to be set appropriately.
  11414. --*/
  11415. {
  11416. BOOL bFusEnabled;
  11417. bFusEnabled = IsFastUserSwitchingEnabled();
  11418. if ((!bFusEnabled && (ulSessionId == MAIN_SESSION)) ||
  11419. ( bFusEnabled && (ulSessionId == GetActiveConsoleSessionId()))) {
  11420. return TRUE;
  11421. } else {
  11422. return FALSE;
  11423. }
  11424. } // IsConsoleSession
  11425. ULONG
  11426. GetActiveConsoleSessionId(
  11427. VOID
  11428. )
  11429. /*++
  11430. Routine Description:
  11431. This routine returns the session id for the current active Console session.
  11432. If a Console session switch event is in progress, it will wait until it is
  11433. complete before returning.
  11434. Arguments:
  11435. None.
  11436. Return Value:
  11437. Session Id of the current active Console session.
  11438. --*/
  11439. {
  11440. DWORD dwWait;
  11441. ASSERT(ghActiveConsoleSessionEvent);
  11442. dwWait = WaitForSingleObject(ghActiveConsoleSessionEvent, INFINITE);
  11443. ASSERT(dwWait == WAIT_OBJECT_0);
  11444. ASSERT(gActiveConsoleSessionId != INVALID_SESSION);
  11445. return gActiveConsoleSessionId;
  11446. } // GetActiveConsoleSessionId
  11447. BOOL
  11448. GetSessionUserToken(
  11449. IN ULONG ulSessionId,
  11450. OUT LPHANDLE lphUserToken
  11451. )
  11452. /*++
  11453. Routine Description:
  11454. This routine returns a handle to the user access token for the user at the
  11455. Console session.
  11456. Arguments:
  11457. lphUserToken - Specifies the address to receive the handle to the user access
  11458. token. Note that if this routine was successful, the caller is
  11459. responsible for closing this handle.
  11460. Return Value:
  11461. Returns TRUE if successful, FALSE otherwise.
  11462. --*/
  11463. {
  11464. BOOL bResult = FALSE;
  11465. HANDLE hImpersonationToken = INVALID_HANDLE_VALUE;
  11466. RPC_STATUS rpcStatus;
  11467. //
  11468. // Verify that we were supplied a location to store the user token handle.
  11469. //
  11470. if (lphUserToken == NULL) {
  11471. KdPrintEx((DPFLTR_PNPMGR_ID,
  11472. DBGF_ERRORS,
  11473. "UMPNPMGR: NULL lphUserToken supplied to GetSessionUserToken!\n"));
  11474. return FALSE;
  11475. }
  11476. if (ulSessionId == MAIN_SESSION) {
  11477. //
  11478. // A logon to session 0 can't be dependent on termsrv.exe, so we always
  11479. // cache a handle to the user access token for that session during the
  11480. // call to PNP_ReportLogon for session 0. If we currently have a handle
  11481. // to the token, return it.
  11482. //
  11483. ASSERT(gTokenLock.LockHandles);
  11484. LockPrivateResource(&gTokenLock);
  11485. if (ghUserToken) {
  11486. //
  11487. // Duplicate the handle so that the caller can always safely close
  11488. // it, no matter where it came from.
  11489. //
  11490. bResult = DuplicateHandle(GetCurrentProcess(),
  11491. ghUserToken,
  11492. GetCurrentProcess(),
  11493. lphUserToken,
  11494. 0,
  11495. TRUE,
  11496. DUPLICATE_SAME_ACCESS);
  11497. if (!bResult) {
  11498. KdPrintEx((DPFLTR_PNPMGR_ID,
  11499. DBGF_ERRORS,
  11500. "UMPNPMGR: DuplicateHandle failed for ghUserToken for SessionId %d, error = %d\n",
  11501. ulSessionId, GetLastError()));
  11502. }
  11503. } else {
  11504. //
  11505. // If we don't have a handle to a user access token for session 0,
  11506. // there is probably not any user logged on to that session.
  11507. //
  11508. bResult = FALSE;
  11509. }
  11510. UnlockPrivateResource(&gTokenLock);
  11511. } else {
  11512. //
  11513. // If the specified session is some session other than session 0,
  11514. // Terminal Services must necessarily be available. Call
  11515. // GetWinStationUserToken to retrieve a handle to the user access token
  11516. // for this session.
  11517. //
  11518. bResult = GetWinStationUserToken(ulSessionId, &hImpersonationToken);
  11519. if (bResult) {
  11520. //
  11521. // The token retrieved by GetWinStationUserToken is an impersonation
  11522. // token. CreateProcessAsUser requires a primary token, so we must
  11523. // duplicate the impersonation token to get one. Create a primary
  11524. // token with the same access rights as the original token.
  11525. //
  11526. bResult = DuplicateTokenEx(hImpersonationToken,
  11527. 0,
  11528. NULL,
  11529. SecurityImpersonation,
  11530. TokenPrimary,
  11531. lphUserToken);
  11532. //
  11533. // Close the handle to the impersonation token.
  11534. //
  11535. CloseHandle(hImpersonationToken);
  11536. if (!bResult) {
  11537. KdPrintEx((DPFLTR_PNPMGR_ID,
  11538. DBGF_ERRORS,
  11539. "UMPNPMGR: DuplicateTokenEx failed, error = %d\n",
  11540. GetLastError()));
  11541. }
  11542. } else {
  11543. //
  11544. // Find out what the problem was.
  11545. //
  11546. rpcStatus = GetLastError();
  11547. if (rpcStatus == RPC_S_INVALID_BINDING) {
  11548. //
  11549. // This is some error related to the service not being
  11550. // available. Since we only call this for sessions other than
  11551. // the main session, termsrv should definitely be available.
  11552. //
  11553. KdPrintEx((DPFLTR_PNPMGR_ID,
  11554. DBGF_ERRORS,
  11555. "UMPNPMGR: GetWinStationUserToken returned error = %d for SessionId %d!!\n",
  11556. rpcStatus, ulSessionId));
  11557. ASSERT(FALSE);
  11558. } else {
  11559. //
  11560. // Some other error, the service may never be avaiable so bail
  11561. // out now.
  11562. //
  11563. KdPrintEx((DPFLTR_PNPMGR_ID,
  11564. DBGF_WARNINGS,
  11565. "UMPNPMGR: GetWinStationUserToken failed for SessionId %d, error = %d\n",
  11566. ulSessionId, rpcStatus));
  11567. }
  11568. }
  11569. }
  11570. //
  11571. // If successful, we should always be returning a valid handle.
  11572. //
  11573. ASSERT(!bResult || ((*lphUserToken != INVALID_HANDLE_VALUE) && (*lphUserToken != NULL)));
  11574. return bResult;
  11575. } // GetSessionUserToken
  11576. DWORD
  11577. CreateUserSynchEvent(
  11578. IN LPCWSTR lpName,
  11579. OUT HANDLE *phEvent
  11580. )
  11581. /*++
  11582. Routine Description:
  11583. This routine creates an event that anyone can synchronize with. This is
  11584. used so that we can communicate with the UI-only NewDev process running in
  11585. a non-privileged user's context.
  11586. Arguments:
  11587. lpName - Name of event to create.
  11588. phEvent - Supplies the address of a variable that will be filled in with
  11589. the event handle created.
  11590. Return Value:
  11591. If successful, the return value is NO_ERROR.
  11592. If failure, the return value is a Win32 error code indicating the cause of
  11593. failure.
  11594. --*/
  11595. {
  11596. DWORD Err;
  11597. ULONG ulSize = 0;
  11598. SECURITY_DESCRIPTOR secDesc;
  11599. SID_IDENTIFIER_AUTHORITY WorldAuthority = SECURITY_WORLD_SID_AUTHORITY;
  11600. SID_IDENTIFIER_AUTHORITY CreatorAuthority = SECURITY_CREATOR_SID_AUTHORITY;
  11601. PACL pDacl = NULL;
  11602. PSID pWorldSid = NULL;
  11603. PSID pCreatorSid = NULL;
  11604. SECURITY_ATTRIBUTES secAttributes;
  11605. //
  11606. // create the World SID
  11607. //
  11608. if (!AllocateAndInitializeSid( &WorldAuthority, 1,
  11609. SECURITY_WORLD_RID,
  11610. 0, 0, 0, 0, 0, 0, 0,
  11611. &pWorldSid)) {
  11612. Err = GetLastError();
  11613. ASSERT(Err != NO_ERROR);
  11614. if(Err == NO_ERROR) {
  11615. Err = ERROR_INVALID_DATA;
  11616. }
  11617. goto Clean0;
  11618. }
  11619. //
  11620. // create the Creator/Owner SID
  11621. //
  11622. if (!AllocateAndInitializeSid( &CreatorAuthority, 1,
  11623. SECURITY_CREATOR_OWNER_RID,
  11624. 0, 0, 0, 0, 0, 0, 0,
  11625. &pCreatorSid)) {
  11626. Err = GetLastError();
  11627. ASSERT(Err != NO_ERROR);
  11628. if(Err == NO_ERROR) {
  11629. Err = ERROR_INVALID_DATA;
  11630. }
  11631. goto Clean0;
  11632. }
  11633. //
  11634. // create a new absolute security descriptor and DACL
  11635. //
  11636. if (!InitializeSecurityDescriptor( &secDesc,
  11637. SECURITY_DESCRIPTOR_REVISION)) {
  11638. Err = GetLastError();
  11639. ASSERT(Err != NO_ERROR);
  11640. if(Err == NO_ERROR) {
  11641. Err = ERROR_INVALID_DATA;
  11642. }
  11643. goto Clean0;
  11644. }
  11645. ulSize = sizeof(ACL);
  11646. ulSize += sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(pWorldSid) - sizeof(DWORD);
  11647. ulSize += sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(pCreatorSid) - sizeof(DWORD);
  11648. //
  11649. // create and initialize the DACL
  11650. //
  11651. pDacl = HeapAlloc(ghPnPHeap, 0, ulSize);
  11652. if (pDacl == NULL) {
  11653. Err = ERROR_NOT_ENOUGH_MEMORY;
  11654. goto Clean0;
  11655. }
  11656. if (!InitializeAcl(pDacl, ulSize, ACL_REVISION)) {
  11657. Err = GetLastError();
  11658. ASSERT(Err != NO_ERROR);
  11659. if(Err == NO_ERROR) {
  11660. Err = ERROR_INVALID_DATA;
  11661. }
  11662. goto Clean0;
  11663. }
  11664. //
  11665. // Add a Creator-full ace to this DACL
  11666. //
  11667. if (!AddAccessAllowedAceEx( pDacl, ACL_REVISION,
  11668. CONTAINER_INHERIT_ACE, EVENT_ALL_ACCESS,
  11669. pCreatorSid)) {
  11670. Err = GetLastError();
  11671. ASSERT(Err != NO_ERROR);
  11672. if(Err == NO_ERROR) {
  11673. Err = ERROR_INVALID_DATA;
  11674. }
  11675. goto Clean0;
  11676. }
  11677. //
  11678. // Add a World-modify/synchronize ace to this DACL
  11679. //
  11680. if (!AddAccessAllowedAceEx( pDacl, ACL_REVISION,
  11681. CONTAINER_INHERIT_ACE, EVENT_MODIFY_STATE | SYNCHRONIZE,
  11682. pWorldSid)) {
  11683. Err = GetLastError();
  11684. ASSERT(Err != NO_ERROR);
  11685. if(Err == NO_ERROR) {
  11686. Err = ERROR_INVALID_DATA;
  11687. }
  11688. goto Clean0;
  11689. }
  11690. //
  11691. // Set the new DACL in the absolute security descriptor
  11692. //
  11693. if (!SetSecurityDescriptorDacl(&secDesc, TRUE, pDacl, FALSE)) {
  11694. Err = GetLastError();
  11695. ASSERT(Err != NO_ERROR);
  11696. if(Err == NO_ERROR) {
  11697. Err = ERROR_INVALID_DATA;
  11698. }
  11699. goto Clean0;
  11700. }
  11701. //
  11702. // validate the new security descriptor
  11703. //
  11704. if (!IsValidSecurityDescriptor(&secDesc)) {
  11705. Err = GetLastError();
  11706. ASSERT(Err != NO_ERROR);
  11707. if(Err == NO_ERROR) {
  11708. Err = ERROR_INVALID_DATA;
  11709. }
  11710. goto Clean0;
  11711. }
  11712. secAttributes.nLength = sizeof(secAttributes);
  11713. secAttributes.lpSecurityDescriptor = &secDesc;
  11714. secAttributes.bInheritHandle = FALSE;
  11715. //
  11716. // Create the manual-reset event with a nonsignaled initial state.
  11717. //
  11718. *phEvent = CreateEvent(&secAttributes, TRUE, FALSE, lpName);
  11719. if (*phEvent != NULL) {
  11720. Err = NO_ERROR;
  11721. } else {
  11722. Err = GetLastError();
  11723. ASSERT(Err != NO_ERROR);
  11724. if(Err == NO_ERROR) {
  11725. Err = ERROR_INVALID_DATA;
  11726. }
  11727. }
  11728. Clean0:
  11729. if (pWorldSid != NULL) {
  11730. FreeSid(pWorldSid);
  11731. }
  11732. if (pCreatorSid != NULL) {
  11733. FreeSid(pCreatorSid);
  11734. }
  11735. if (pDacl != NULL) {
  11736. HeapFree(ghPnPHeap, 0, pDacl);
  11737. }
  11738. return Err;
  11739. } // CreateUserSynchEvent
  11740. BOOL
  11741. CreateNoPendingInstallEvent(
  11742. VOID
  11743. )
  11744. /*++
  11745. Routine Description:
  11746. This routine creates the "PnP_No_Pending_Install_Events" global named event,
  11747. which is set and reset by the UMPNPMGR ThreadProc_DeviceInstall server-side
  11748. device install thread, and waited on by the CMP_WaitNoPendingInstalls
  11749. CFGMGR32 API, which allows clients to synchronize with the event to
  11750. determine when PNP is done actively installing any devices.
  11751. Arguments:
  11752. None.
  11753. Return Value:
  11754. Returns TRUE if successful, FALSE otherwise.
  11755. --*/
  11756. {
  11757. DWORD Err = NO_ERROR;
  11758. SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
  11759. PSID pLocalSystemSid = NULL;
  11760. PSID pAliasAdminsSid = NULL;
  11761. PSID pAliasUsersSid = NULL;
  11762. PACL pDacl = NULL;
  11763. ULONG ulAclSize;
  11764. SECURITY_DESCRIPTOR sd;
  11765. SECURITY_ATTRIBUTES sa;
  11766. //
  11767. // Retrieve the LocalSystem SID
  11768. //
  11769. if (!AllocateAndInitializeSid(
  11770. &NtAuthority, 1,
  11771. SECURITY_LOCAL_SYSTEM_RID,
  11772. 0, 0, 0, 0, 0, 0, 0,
  11773. &pLocalSystemSid)) {
  11774. Err = GetLastError();
  11775. goto Clean0;
  11776. }
  11777. ASSERT(IsValidSid(pLocalSystemSid));
  11778. //
  11779. // Retrieve the Administrators SID
  11780. //
  11781. if (!AllocateAndInitializeSid(
  11782. &NtAuthority, 2,
  11783. SECURITY_BUILTIN_DOMAIN_RID,
  11784. DOMAIN_ALIAS_RID_ADMINS,
  11785. 0, 0, 0, 0, 0, 0,
  11786. &pAliasAdminsSid)) {
  11787. Err = GetLastError();
  11788. goto Clean0;
  11789. }
  11790. ASSERT(IsValidSid(pAliasAdminsSid));
  11791. //
  11792. // Create the Users SID.
  11793. //
  11794. if (!AllocateAndInitializeSid(
  11795. &NtAuthority, 2,
  11796. SECURITY_BUILTIN_DOMAIN_RID,
  11797. DOMAIN_ALIAS_RID_USERS,
  11798. 0, 0, 0, 0, 0, 0,
  11799. &pAliasUsersSid)) {
  11800. Err = GetLastError();
  11801. goto Clean0;
  11802. }
  11803. ASSERT(IsValidSid(pAliasUsersSid));
  11804. //
  11805. // Determine the size required for the DACL
  11806. //
  11807. ulAclSize = sizeof(ACL);
  11808. ulAclSize += sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(pLocalSystemSid) - sizeof(DWORD);
  11809. ulAclSize += sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(pAliasAdminsSid) - sizeof(DWORD);
  11810. ulAclSize += sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(pAliasUsersSid) - sizeof(DWORD);
  11811. //
  11812. // Allocate and initialize the DACL
  11813. //
  11814. pDacl =
  11815. (PACL)HeapAlloc(
  11816. ghPnPHeap, 0, ulAclSize);
  11817. if (pDacl == NULL) {
  11818. Err = ERROR_NOT_ENOUGH_MEMORY;
  11819. goto Clean0;
  11820. }
  11821. if (!InitializeAcl(pDacl, ulAclSize, ACL_REVISION)) {
  11822. Err = GetLastError();
  11823. goto Clean0;
  11824. }
  11825. //
  11826. // Add an ACE to the DACL for LocalSystem EVENT_ALL_ACCESS
  11827. //
  11828. if (!AddAccessAllowedAceEx(
  11829. pDacl,
  11830. ACL_REVISION,
  11831. 0,
  11832. EVENT_ALL_ACCESS,
  11833. pLocalSystemSid)) {
  11834. Err = GetLastError();
  11835. goto Clean0;
  11836. }
  11837. //
  11838. // Add an ACE to the DACL for Administrators EVENT_QUERY_STATE and SYNCHRONIZE
  11839. //
  11840. if (!AddAccessAllowedAceEx(
  11841. pDacl,
  11842. ACL_REVISION,
  11843. 0,
  11844. EVENT_QUERY_STATE | SYNCHRONIZE,
  11845. pAliasAdminsSid)) {
  11846. Err = GetLastError();
  11847. goto Clean0;
  11848. }
  11849. //
  11850. // Add an ACE to the DACL for Users EVENT_QUERY_STATE and SYNCHRONIZE
  11851. //
  11852. if (!AddAccessAllowedAceEx(
  11853. pDacl,
  11854. ACL_REVISION,
  11855. 0,
  11856. EVENT_QUERY_STATE | SYNCHRONIZE,
  11857. pAliasUsersSid)) {
  11858. Err = GetLastError();
  11859. goto Clean0;
  11860. }
  11861. ASSERT(IsValidAcl(pDacl));
  11862. //
  11863. // Allocate and initialize the security descriptor
  11864. //
  11865. if (!InitializeSecurityDescriptor(
  11866. &sd, SECURITY_DESCRIPTOR_REVISION)) {
  11867. Err = GetLastError();
  11868. goto Clean0;
  11869. }
  11870. //
  11871. // Set the new DACL in the security descriptor
  11872. //
  11873. if (!SetSecurityDescriptorDacl(
  11874. &sd, TRUE, pDacl, FALSE)) {
  11875. Err = GetLastError();
  11876. goto Clean0;
  11877. }
  11878. ASSERT(IsValidSecurityDescriptor(&sd));
  11879. //
  11880. // Add the security descriptor to the security attributes
  11881. //
  11882. sa.nLength = sizeof(sa);
  11883. sa.lpSecurityDescriptor = &sd;
  11884. sa.bInheritHandle = FALSE;
  11885. //
  11886. // Create the manual-reset event with a nonsignaled initial state.
  11887. //
  11888. ghNoPendingInstalls =
  11889. CreateEvent(&sa, TRUE, FALSE, PNP_NO_INSTALL_EVENTS);
  11890. if (ghNoPendingInstalls == NULL) {
  11891. Err = GetLastError();
  11892. goto Clean0;
  11893. }
  11894. //
  11895. // Check that the named event did not already exist.
  11896. //
  11897. ASSERT(GetLastError() != ERROR_ALREADY_EXISTS);
  11898. if (GetLastError() == ERROR_ALREADY_EXISTS) {
  11899. Err = ERROR_ALREADY_EXISTS;
  11900. CloseHandle(ghNoPendingInstalls);
  11901. ghNoPendingInstalls = NULL;
  11902. goto Clean0;
  11903. }
  11904. Clean0:
  11905. //
  11906. // Cleanup.
  11907. //
  11908. if (pAliasUsersSid != NULL) {
  11909. FreeSid(pAliasUsersSid);
  11910. }
  11911. if (pAliasAdminsSid != NULL) {
  11912. FreeSid(pAliasAdminsSid);
  11913. }
  11914. if (pLocalSystemSid != NULL) {
  11915. FreeSid(pLocalSystemSid);
  11916. }
  11917. if (pDacl != NULL) {
  11918. HeapFree(ghPnPHeap, 0, pDacl);
  11919. }
  11920. SetLastError(Err);
  11921. return(Err == NO_ERROR);
  11922. } // CreateNoPendingInstallEvent
  11923. VOID
  11924. LogSurpriseRemovalEvent(
  11925. IN LPWSTR MultiSzList
  11926. )
  11927. /*++
  11928. Routine Description:
  11929. One or more non-SurpriseRemovalOK devices were removed without prior
  11930. warning. Record the removals in the event log.
  11931. Arguments:
  11932. MultiSz list of device instance paths.
  11933. Return Value:
  11934. None.
  11935. --*/
  11936. {
  11937. LPWSTR instancePath, friendlyName;
  11938. CONFIGRET configRet;
  11939. ULONG ulRegDataType, ulRemovalPolicy, ulVerifierFlags, ulTransferLen, ulLength;
  11940. HKEY hMmKey = NULL;
  11941. LONG lResult;
  11942. for(instancePath = MultiSzList;
  11943. ((*instancePath) != UNICODE_NULL);
  11944. instancePath += lstrlen(instancePath) + 1) {
  11945. ulTransferLen = ulLength = sizeof(ULONG);
  11946. configRet = PNP_GetDeviceRegProp(
  11947. NULL,
  11948. instancePath,
  11949. CM_DRP_REMOVAL_POLICY,
  11950. &ulRegDataType,
  11951. (LPBYTE) &ulRemovalPolicy,
  11952. &ulTransferLen,
  11953. &ulLength,
  11954. 0
  11955. );
  11956. if (configRet != CR_SUCCESS) {
  11957. continue;
  11958. }
  11959. if (ulRemovalPolicy == CM_REMOVAL_POLICY_EXPECT_SURPRISE_REMOVAL) {
  11960. //
  11961. // For devices which we expect surprise removal, we look to see if
  11962. // the verifier is enabled.
  11963. //
  11964. lResult = RegOpenKeyEx(
  11965. HKEY_LOCAL_MACHINE,
  11966. RegMemoryManagementKeyName,
  11967. 0,
  11968. KEY_QUERY_VALUE,
  11969. &hMmKey
  11970. );
  11971. if ( lResult == ERROR_SUCCESS ) {
  11972. ulLength = sizeof(ULONG);
  11973. lResult = RegQueryValueEx(
  11974. hMmKey,
  11975. RegVerifyDriverLevelValueName,
  11976. 0,
  11977. &ulRegDataType,
  11978. (LPBYTE) &ulVerifierFlags,
  11979. &ulLength
  11980. );
  11981. RegCloseKey(hMmKey);
  11982. //
  11983. // ADRIAO ISSUE 2001/02/14 -
  11984. // We don't yet have a BIOS verification flag yet, so even
  11985. // though the verifier may be targetted at a specific driver
  11986. // for a WHQL test, we will log an event log here.
  11987. //
  11988. if ((lResult != ERROR_SUCCESS) ||
  11989. (!(ulVerifierFlags & DRIVER_VERIFIER_ENHANCED_IO_CHECKING))) {
  11990. continue;
  11991. }
  11992. }
  11993. }
  11994. friendlyName = BuildFriendlyName(instancePath);
  11995. if (friendlyName) {
  11996. LogErrorEvent(
  11997. ERR_SURPRISE_REMOVAL_2,
  11998. 0,
  11999. 2,
  12000. friendlyName,
  12001. instancePath
  12002. );
  12003. HeapFree(ghPnPHeap, 0, friendlyName);
  12004. } else {
  12005. LogErrorEvent(
  12006. ERR_SURPRISE_REMOVAL_1,
  12007. 0,
  12008. 1,
  12009. instancePath
  12010. );
  12011. }
  12012. }
  12013. }
  12014. PWCHAR
  12015. BuildFriendlyName(
  12016. IN LPWSTR InstancePath
  12017. )
  12018. {
  12019. PWCHAR friendlyName;
  12020. CONFIGRET configRet;
  12021. ULONG ulLength, ulTransferLen;
  12022. WCHAR szBuffer[MAX_PATH];
  12023. ULONG ulRegDataType;
  12024. GUID classGuid;
  12025. handle_t hBinding;
  12026. hBinding = NULL;
  12027. //
  12028. // Try the registry for FRIENDLYNAME
  12029. //
  12030. ulLength = ulTransferLen = sizeof(szBuffer);
  12031. configRet = PNP_GetDeviceRegProp(
  12032. hBinding,
  12033. InstancePath,
  12034. CM_DRP_FRIENDLYNAME,
  12035. &ulRegDataType,
  12036. (LPBYTE) szBuffer,
  12037. &ulTransferLen,
  12038. &ulLength,
  12039. 0
  12040. );
  12041. if (configRet != CR_SUCCESS || !*szBuffer) {
  12042. //
  12043. // Try the registry for DEVICEDESC
  12044. //
  12045. ulLength = ulTransferLen = sizeof(szBuffer);
  12046. configRet = PNP_GetDeviceRegProp(
  12047. hBinding,
  12048. InstancePath,
  12049. CM_DRP_DEVICEDESC,
  12050. &ulRegDataType,
  12051. (LPBYTE) szBuffer,
  12052. &ulTransferLen,
  12053. &ulLength,
  12054. 0
  12055. );
  12056. if (configRet != CR_SUCCESS || !*szBuffer) {
  12057. //
  12058. // Initialize ClassGuid to GUID_NULL
  12059. //
  12060. CopyMemory(&classGuid, &GUID_NULL, sizeof(GUID));
  12061. //
  12062. // Try the registry for CLASSNAME
  12063. //
  12064. ulLength = ulTransferLen = sizeof(szBuffer);
  12065. configRet = PNP_GetDeviceRegProp(
  12066. hBinding,
  12067. InstancePath,
  12068. CM_DRP_CLASSGUID,
  12069. &ulRegDataType,
  12070. (LPBYTE) szBuffer,
  12071. &ulTransferLen,
  12072. &ulLength,
  12073. 0
  12074. );
  12075. if (configRet == CR_SUCCESS) {
  12076. GuidFromString(szBuffer, &classGuid);
  12077. }
  12078. if (!IsEqualGUID(&classGuid, &GUID_NULL) &&
  12079. !IsEqualGUID(&classGuid, &GUID_DEVCLASS_UNKNOWN)) {
  12080. ulLength = ulTransferLen = sizeof(szBuffer);
  12081. configRet = PNP_GetDeviceRegProp(
  12082. hBinding,
  12083. InstancePath,
  12084. CM_DRP_CLASS,
  12085. &ulRegDataType,
  12086. (LPBYTE) szBuffer,
  12087. &ulTransferLen,
  12088. &ulLength,
  12089. 0
  12090. );
  12091. } else {
  12092. configRet = CR_NO_SUCH_VALUE;
  12093. }
  12094. }
  12095. }
  12096. if (configRet == CR_SUCCESS && *szBuffer) {
  12097. friendlyName = HeapAlloc(ghPnPHeap, HEAP_ZERO_MEMORY, ulLength);
  12098. if (friendlyName) {
  12099. memcpy(friendlyName, szBuffer, ulLength);
  12100. }
  12101. } else {
  12102. friendlyName = NULL;
  12103. }
  12104. return friendlyName;
  12105. }
  12106. ENUM_ACTION
  12107. QueueInstallationCallback(
  12108. IN LPCWSTR DevInst,
  12109. IN OUT PVOID Context
  12110. )
  12111. /*++
  12112. Routine Description:
  12113. This routine is called back for each devnode in a given subtree. It places
  12114. each device node in that subtree into the installation queue so that it'll
  12115. be reinstalled *if* appropriate (the installation side code checked the
  12116. state of the devnode.)
  12117. Arguments:
  12118. DevInst InstancePath of current devnode.
  12119. Context A pointer to QI_CONTEXT data (needed to handle the single-level
  12120. enum case.)
  12121. Return Value:
  12122. ENUM_ACTION (Either EA_CONTINUE, EA_SKIP_SUBTREE, or EA_STOP_ENUMERATION)
  12123. --*/
  12124. {
  12125. PQI_CONTEXT pqiContext;
  12126. PPNP_INSTALL_ENTRY entry, current;
  12127. CONFIGRET status;
  12128. BOOL needsReinstall;
  12129. pqiContext = (PQI_CONTEXT) Context;
  12130. status = DevInstNeedsInstall(DevInst, &needsReinstall);
  12131. if (status != CR_SUCCESS) {
  12132. //
  12133. // The devnode disappeared out from under us. Skip it's subtree.
  12134. //
  12135. return EA_SKIP_SUBTREE;
  12136. }
  12137. if (needsReinstall) {
  12138. //
  12139. // This devnode needs installation. Allocate and initialize a new
  12140. // device install entry block.
  12141. //
  12142. entry = HeapAlloc(ghPnPHeap, 0, sizeof(PNP_INSTALL_ENTRY));
  12143. if (!entry) {
  12144. pqiContext->Status = CR_OUT_OF_MEMORY;
  12145. return EA_STOP_ENUMERATION;
  12146. }
  12147. lstrcpy(entry->szDeviceId, DevInst);
  12148. entry->Next = NULL;
  12149. entry->Flags = 0;
  12150. //
  12151. // Insert this entry in the device install list.
  12152. //
  12153. LockNotifyList(&InstallList.Lock);
  12154. current = (PPNP_INSTALL_ENTRY)InstallList.Next;
  12155. if (current == NULL) {
  12156. InstallList.Next = entry;
  12157. } else {
  12158. while ((PPNP_INSTALL_ENTRY)current->Next != NULL) {
  12159. current = (PPNP_INSTALL_ENTRY)current->Next;
  12160. }
  12161. current->Next = entry;
  12162. }
  12163. UnlockNotifyList(&InstallList.Lock);
  12164. SetEvent(InstallEvents[NEEDS_INSTALL_EVENT]);
  12165. //
  12166. // You might think we could skip the children if a parent is going to
  12167. // be reinstalled. However, setupapi might decide not to tear down the
  12168. // stack.
  12169. //
  12170. }
  12171. //
  12172. // If this is a single-level enumeration, we only want to touch the parent
  12173. // and his immediate children.
  12174. //
  12175. if (pqiContext->HeadNodeSeen && pqiContext->SingleLevelEnumOnly) {
  12176. return EA_SKIP_SUBTREE;
  12177. }
  12178. pqiContext->HeadNodeSeen = TRUE;
  12179. return EA_CONTINUE;
  12180. }
  12181. CONFIGRET
  12182. DevInstNeedsInstall(
  12183. IN LPCWSTR DevInst,
  12184. OUT BOOL *NeedsInstall
  12185. )
  12186. /*++
  12187. Routine Description:
  12188. This routine determines whether a particular DevInst needs to be passed off
  12189. to Setupapi for installation.
  12190. Arguments:
  12191. DevInst InstancePath of devnode to check.
  12192. NeedsInstall Recieves TRUE if the devnode is present and needs to be
  12193. installed, FALSE otherwise.
  12194. Return Value:
  12195. CONFIGRET (if the devnode isn't present, this will be CR_NO_SUCH_DEVINST.)
  12196. --*/
  12197. {
  12198. CONFIGRET status;
  12199. ULONG ulStatus, ulProblem, ulConfig;
  12200. //
  12201. // Preinit
  12202. //
  12203. *NeedsInstall = FALSE;
  12204. //
  12205. // Is the device present?
  12206. //
  12207. status = GetDeviceStatus(DevInst, &ulStatus, &ulProblem);
  12208. if (status == CR_SUCCESS) {
  12209. //
  12210. // Implementation note: In kernel-mode when we first process this
  12211. // device instance, if there is no ConfigFlag value present, then we
  12212. // set a problem of CM_PROB_NOT_CONFIGURED (this would always happen
  12213. // for brand new device instances). If there is already a ConfigFlag
  12214. // value of CONFIGFLAG_REINSTALL, then we set a problem of
  12215. // CM_PROB_REINSTALL. Either problem will trigger an installation of
  12216. // this device, the only difference is in how SetupDi routines handle
  12217. // a failed installation: If ConfigFlag is CONFIGFLAG_NOT_CONFIGURED,
  12218. // then a failed install will leave the ConfigFlag alone and set a
  12219. // problem of CM_PROB_FAILED_INSTALL. If there is no ConfigFlag, then
  12220. // ConfigFlag will be set to CONFIGFLAG_DISABLED.
  12221. //
  12222. ulConfig = GetDeviceConfigFlags(DevInst, NULL);
  12223. if((ulConfig & CONFIGFLAG_FINISH_INSTALL) ||
  12224. ((ulStatus & DN_HAS_PROBLEM) &&
  12225. ((ulProblem == CM_PROB_REINSTALL) || (ulProblem == CM_PROB_NOT_CONFIGURED)))) {
  12226. *NeedsInstall = TRUE;
  12227. }
  12228. } else if (!lstrcmpi(DevInst, REGSTR_VAL_ROOT_DEVNODE)) {
  12229. status = CR_SUCCESS;
  12230. }
  12231. return status;
  12232. }
  12233. PWSTR
  12234. BuildBlockedDriverList(
  12235. IN OUT LPGUID GuidList,
  12236. IN ULONG GuidCount
  12237. )
  12238. /*++
  12239. Routine Description:
  12240. This routine builds a multi-sz list of GUIDs, based on the array of GUIDs
  12241. supplied. If no GUIDs were supplied, this routine returns a list of all
  12242. drivers currently blocked by the system.
  12243. Arguments:
  12244. GuidList - Address of the array of blocked driver GUIDs to create the
  12245. multi-sz list from. This argument may be NULL to retrieve a
  12246. list of all drivers currently blocked by the system.
  12247. GuidCount - Specifies the number of GUIDs in the array. If GuidList is
  12248. NULL, this argument must be 0.
  12249. Return Value:
  12250. Returns a MultiSz list of blocked driver GUIDs, based on the supplied
  12251. parameters. Returns NULL if no GUIDs were supplied, and no GUIDs are
  12252. currently being blocked by the system.
  12253. If a multi-sz list was returned, the caller is responsible for freeing the
  12254. associated buffer.
  12255. --*/
  12256. {
  12257. CONFIGRET Status = STATUS_SUCCESS;
  12258. ULONG ulLength, ulTemp;
  12259. PBYTE Buffer = NULL;
  12260. PWSTR MultiSzList = NULL, p;
  12261. try {
  12262. //
  12263. // Validate parameters.
  12264. //
  12265. if (((!ARGUMENT_PRESENT(GuidList)) && (GuidCount != 0)) ||
  12266. ((ARGUMENT_PRESENT(GuidList)) && (GuidCount == 0))) {
  12267. Status = CR_FAILURE;
  12268. goto Clean0;
  12269. }
  12270. if (GuidCount == 0) {
  12271. //
  12272. // We were called without a list of GUIDs, so we need to get the
  12273. // list ourselves.
  12274. //
  12275. ASSERT(!ARGUMENT_PRESENT(GuidList));
  12276. ulLength = 0;
  12277. ulTemp = 0;
  12278. Status = PNP_GetBlockedDriverInfo(
  12279. NULL,
  12280. NULL,
  12281. &ulTemp,
  12282. &ulLength,
  12283. 0);
  12284. //
  12285. // If no drivers are currently being blocked, or we encountered some
  12286. // other failure, we have nothing to display, so just return.
  12287. //
  12288. if ((Status != CR_BUFFER_SMALL) || (ulLength == 0)) {
  12289. Status = CR_FAILURE;
  12290. goto Clean0;
  12291. }
  12292. //
  12293. // Allocate a buffer to retrieve the list of GUIDs.
  12294. //
  12295. Buffer = HeapAlloc(ghPnPHeap, 0, ulLength);
  12296. if (Buffer == NULL) {
  12297. Status = CR_FAILURE;
  12298. goto Clean0;
  12299. }
  12300. //
  12301. // Get the list of GUIDs for currently blocked drivers.
  12302. //
  12303. ulTemp = 0;
  12304. Status = PNP_GetBlockedDriverInfo(
  12305. NULL,
  12306. Buffer,
  12307. &ulTemp,
  12308. &ulLength,
  12309. 0);
  12310. //
  12311. // We thought there was a list when we checked before, so we better
  12312. // have one now.
  12313. //
  12314. ASSERT(Status != CR_BUFFER_SMALL);
  12315. ASSERT(ulLength != 0);
  12316. ASSERT(ulTemp != 0);
  12317. if (Status != CR_SUCCESS) {
  12318. goto Clean0;
  12319. }
  12320. //
  12321. // Use the list we just retrieved. Note that Buffer is non-NULL
  12322. // when we allocate our own buffer for the array, so make sure we
  12323. // free it below.
  12324. //
  12325. GuidCount = ulLength / sizeof(GUID);
  12326. GuidList = (LPGUID)Buffer;
  12327. }
  12328. //
  12329. // We must have a list of GUIDs to convert by this point.
  12330. //
  12331. ASSERT(GuidCount > 0);
  12332. ASSERT(GuidList != NULL);
  12333. //
  12334. // Allocate a buffer to hold the multi-sz list of stringified GUIDs.
  12335. //
  12336. ulLength = (GuidCount*MAX_GUID_STRING_LEN + 1) * sizeof(WCHAR);
  12337. MultiSzList = HeapAlloc(ghPnPHeap, 0, ulLength);
  12338. if (MultiSzList == NULL) {
  12339. Status = CR_FAILURE;
  12340. goto Clean0;
  12341. }
  12342. ZeroMemory(MultiSzList, ulLength);
  12343. //
  12344. // Traverse the list of GUIDs, converting to strings as we go.
  12345. //
  12346. for (p = MultiSzList, ulTemp = 0;
  12347. ulTemp < GuidCount;
  12348. ulTemp++, p+= lstrlen(p) + 1) {
  12349. if (StringFromGuid(
  12350. (LPGUID)&(GuidList[ulTemp]), p,
  12351. ((ulLength/sizeof(WCHAR)) - (ULONG)(p - MultiSzList))) != NO_ERROR) {
  12352. Status = CR_FAILURE;
  12353. goto Clean0;
  12354. }
  12355. }
  12356. *p = L'\0';
  12357. //
  12358. // Success!!
  12359. //
  12360. Status = CR_SUCCESS;
  12361. Clean0:
  12362. NOTHING;
  12363. } except(EXCEPTION_EXECUTE_HANDLER) {
  12364. KdPrintEx((DPFLTR_PNPMGR_ID,
  12365. DBGF_ERRORS,
  12366. "UMPNPMGR: Exception in BuildBlockedDriverList!\n"));
  12367. ASSERT(0);
  12368. Status = CR_FAILURE;
  12369. //
  12370. // Reference the following variables so the compiler will respect
  12371. // statement ordering w.r.t. their assignment.
  12372. //
  12373. Buffer = Buffer;
  12374. MultiSzList = MultiSzList;
  12375. }
  12376. //
  12377. // Free the GUID list buffer, if we allocated one.
  12378. //
  12379. if (Buffer != NULL) {
  12380. HeapFree(ghPnPHeap, 0, Buffer);
  12381. }
  12382. //
  12383. // Don't return a list if we were unsuccessful.
  12384. //
  12385. if ((Status != CR_SUCCESS) && (MultiSzList != NULL)) {
  12386. HeapFree(ghPnPHeap, 0, MultiSzList);
  12387. MultiSzList = NULL;
  12388. }
  12389. return MultiSzList;
  12390. } // BuildBlockedDriverList
  12391. CONFIGRET
  12392. PNP_GetServerSideDeviceInstallFlags(
  12393. IN handle_t hBinding,
  12394. PULONG pulSSDIFlags,
  12395. ULONG ulFlags
  12396. )
  12397. /*++
  12398. Routine Description:
  12399. This is the RPC server entry point for the CMP_GetServerSideDeviceInstallFlags
  12400. routine.
  12401. Arguments:
  12402. hBinding - RPC binding handle, not used.
  12403. pulSSDIFlags - A ULONG pointer, supplied by the caller. This is used
  12404. to pass back the following server side device install
  12405. flags:
  12406. SSDI_REBOOT_PENDING - A reboot is pending from a server
  12407. side device install.
  12408. ulFlags Not used, must be zero.
  12409. Return Value:
  12410. Return CR_SUCCESS if the function succeeds, otherwise it returns one of the
  12411. CR_* errors.
  12412. --*/
  12413. {
  12414. CONFIGRET Status = CR_SUCCESS;
  12415. UNREFERENCED_PARAMETER(hBinding);
  12416. try {
  12417. //
  12418. // Validate parameters
  12419. //
  12420. if (!ARGUMENT_PRESENT(pulSSDIFlags)) {
  12421. Status = CR_INVALID_POINTER;
  12422. goto Clean0;
  12423. }
  12424. if (INVALID_FLAGS(ulFlags, 0)) {
  12425. Status = CR_INVALID_FLAG;
  12426. goto Clean0;
  12427. }
  12428. *pulSSDIFlags = 0;
  12429. //
  12430. // SSDI_REBOOT_PENDING
  12431. // Determine if a server side device install reboot is pending.
  12432. //
  12433. if (gServerSideDeviceInstallRebootNeeded) {
  12434. *pulSSDIFlags |= SSDI_REBOOT_PENDING;
  12435. }
  12436. Clean0:
  12437. NOTHING;
  12438. } except(EXCEPTION_EXECUTE_HANDLER) {
  12439. Status = CR_FAILURE;
  12440. }
  12441. return Status;
  12442. } // PNP_GetServerSideDeviceInstallFlags