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

1963 lines
56 KiB

  1. /*
  2. * Copyright (c) 1992-1997 Microsoft Corporation
  3. * hotplug routines
  4. *
  5. * 09-May-1997 Jonle , created
  6. *
  7. */
  8. #include "stdafx.h"
  9. #include <nt.h>
  10. #include <ntrtl.h>
  11. #include <nturtl.h>
  12. #include "systray.h"
  13. #include <setupapi.h>
  14. #include <cfgmgr32.h>
  15. #include <dbt.h>
  16. #include <initguid.h>
  17. #include <devguid.h>
  18. #include <ks.h>
  19. #include <ksmedia.h>
  20. #include <ntddstor.h>
  21. #include <strsafe.h>
  22. BOOL
  23. HotplugPlaySoundThisSession(
  24. VOID
  25. );
  26. //
  27. // Hardware application sound event names.
  28. //
  29. #define DEVICE_ARRIVAL_SOUND TEXT("DeviceConnect")
  30. #define DEVICE_REMOVAL_SOUND TEXT("DeviceDisconnect")
  31. #define DEVICE_FAILURE_SOUND TEXT("DeviceFail")
  32. //
  33. // Simple checks for console / remote TS sessions.
  34. //
  35. #define MAIN_SESSION ((ULONG)0)
  36. #define THIS_SESSION ((ULONG)NtCurrentPeb()->SessionId)
  37. #define CONSOLE_SESSION ((ULONG)USER_SHARED_DATA->ActiveConsoleId)
  38. #define IsConsoleSession() (BOOL)(THIS_SESSION == CONSOLE_SESSION)
  39. #define IsRemoteSession() (BOOL)(THIS_SESSION != CONSOLE_SESSION)
  40. #define IsPseudoConsoleSession() (BOOL)(THIS_SESSION == MAIN_SESSION)
  41. #define HPLUG_EJECT_EVENT TEXT("HPlugEjectEvent")
  42. typedef struct _HotPlugDevices {
  43. struct _HotPlugDevices *Next;
  44. DEVINST DevInst;
  45. WORD EjectMenuIndex;
  46. BOOLEAN PendingEvent;
  47. PTCHAR DevName;
  48. TCHAR DevInstanceId[1];
  49. } HOTPLUGDEVICES, *PHOTPLUGDEVICES;
  50. BOOL HotPlugInitialized = FALSE;
  51. BOOL ShowShellIcon = FALSE;
  52. HICON HotPlugIcon = NULL;
  53. BOOL ServiceEnabled = FALSE;
  54. HANDLE hEjectEvent = NULL; // Event to if we are in the process of ejecting a device
  55. HDEVINFO g_hCurrentDeviceInfoSet = INVALID_HANDLE_VALUE;
  56. HDEVINFO g_hRemovableDeviceInfoSet = INVALID_HANDLE_VALUE;
  57. extern HINSTANCE g_hInstance; // Global instance handle 4 this application.
  58. BOOL
  59. pDoesUserHavePrivilege(
  60. PCTSTR PrivilegeName
  61. )
  62. /*++
  63. Routine Description:
  64. This routine returns TRUE if the caller's process has
  65. the specified privilege. The privilege does not have
  66. to be currently enabled. This routine is used to indicate
  67. whether the caller has the potential to enable the privilege.
  68. Caller is NOT expected to be impersonating anyone and IS
  69. expected to be able to open their own process and process
  70. token.
  71. Arguments:
  72. Privilege - the name form of privilege ID (such as
  73. SE_SECURITY_NAME).
  74. Return Value:
  75. TRUE - Caller has the specified privilege.
  76. FALSE - Caller does not have the specified privilege.
  77. --*/
  78. {
  79. HANDLE Token;
  80. ULONG BytesRequired;
  81. PTOKEN_PRIVILEGES Privileges;
  82. BOOL b;
  83. DWORD i;
  84. LUID Luid;
  85. //
  86. // Open the process token.
  87. //
  88. if(!OpenProcessToken(GetCurrentProcess(),TOKEN_QUERY,&Token)) {
  89. return(FALSE);
  90. }
  91. b = FALSE;
  92. Privileges = NULL;
  93. //
  94. // Get privilege information.
  95. //
  96. if(!GetTokenInformation(Token,TokenPrivileges,NULL,0,&BytesRequired)
  97. && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
  98. && (Privileges = LocalAlloc(LPTR, BytesRequired))
  99. && GetTokenInformation(Token,TokenPrivileges,Privileges,BytesRequired,&BytesRequired)
  100. && LookupPrivilegeValue(NULL,PrivilegeName,&Luid)) {
  101. //
  102. // See if we have the requested privilege
  103. //
  104. for(i=0; i<Privileges->PrivilegeCount; i++) {
  105. if((Luid.LowPart == Privileges->Privileges[i].Luid.LowPart)
  106. && (Luid.HighPart == Privileges->Privileges[i].Luid.HighPart)) {
  107. b = TRUE;
  108. break;
  109. }
  110. }
  111. }
  112. //
  113. // Clean up and return.
  114. //
  115. if(Privileges) {
  116. LocalFree(Privileges);
  117. }
  118. CloseHandle(Token);
  119. return(b);
  120. }
  121. BOOL
  122. IsHotPlugDevice(
  123. DEVINST DevInst
  124. )
  125. /**+
  126. A device is considered a HotPlug device if the following are TRUE:
  127. - has Capability CM_DEVCAP_REMOVABLE
  128. - does NOT have Capability CM_DEVCAP_SURPRISEREMOVALOK
  129. - does NOT have Capability CM_DEVCAP_DOCKDEVICE
  130. - must be started (have the DN_STARTED devnode flag)
  131. - unless has capability CM_DEVCAP_EJECTSUPPORTED
  132. Returns:
  133. TRUE if this is a HotPlug device
  134. FALSE if this is not a HotPlug device.
  135. -**/
  136. {
  137. DWORD Capabilities;
  138. ULONG cbSize;
  139. DWORD Status, Problem;
  140. Capabilities = Status = Problem = 0;
  141. cbSize = sizeof(Capabilities);
  142. if (CM_Get_DevNode_Registry_Property(DevInst,
  143. CM_DRP_CAPABILITIES,
  144. NULL,
  145. (PVOID)&Capabilities,
  146. &cbSize,
  147. 0
  148. ) != CR_SUCCESS) {
  149. return FALSE;
  150. }
  151. if (CM_Get_DevNode_Status(&Status,
  152. &Problem,
  153. DevInst,
  154. 0
  155. ) != CR_SUCCESS) {
  156. return FALSE;
  157. }
  158. //
  159. // If this device is not removable, or it is surprise removal ok, or
  160. // it is a dock device, then it is not a hotplug device.
  161. //
  162. if ((!(Capabilities & CM_DEVCAP_REMOVABLE)) ||
  163. (Capabilities & CM_DEVCAP_SURPRISEREMOVALOK) ||
  164. (Capabilities & CM_DEVCAP_DOCKDEVICE)) {
  165. return FALSE;
  166. }
  167. //
  168. // We won't consider a device to be a hotplug device if it is not started,
  169. // unless it is an eject capable device.
  170. //
  171. // The reason for this test is that a bus driver might set the
  172. // CM_DEVCAP_REMOVABLE capability, but if the PDO doesn't get loaded then
  173. // it can't set the CM_DEVCAP_SURPRISEREMOVALOK. So we won't trust the
  174. // CM_DEVCAP_REMOVABLE capability if the PDO is not started.
  175. //
  176. if ((!(Capabilities & CM_DEVCAP_EJECTSUPPORTED)) &&
  177. (!(Status & DN_STARTED))) {
  178. return FALSE;
  179. }
  180. return TRUE;
  181. }
  182. BOOL
  183. IsRemovableDevice(
  184. IN DEVINST dnDevInst
  185. )
  186. /*++
  187. Routine Description:
  188. This routine determines whether a device is removable.
  189. Arguments:
  190. dnDevInst - Device instance.
  191. Return Value:
  192. Returns TRUE if the device is removable.
  193. --*/
  194. {
  195. ULONG ulPropertyData, ulDataSize, ulRegDataType;
  196. //
  197. // Validate parameters.
  198. //
  199. if (dnDevInst == 0) {
  200. return FALSE;
  201. }
  202. //
  203. // Get the capabilities for this device.
  204. //
  205. ulDataSize = sizeof(ulPropertyData);
  206. if (CM_Get_DevNode_Registry_Property_Ex(dnDevInst,
  207. CM_DRP_CAPABILITIES,
  208. &ulRegDataType,
  209. &ulPropertyData,
  210. &ulDataSize,
  211. 0,
  212. NULL) != CR_SUCCESS) {
  213. return FALSE;
  214. }
  215. //
  216. // Check if the device has the removable capability.
  217. //
  218. if ((ulPropertyData & CM_DEVCAP_REMOVABLE) == 0) {
  219. return FALSE;
  220. }
  221. return TRUE;
  222. } // IsRemovableDevice
  223. LPTSTR
  224. DevNodeToDriveLetter(
  225. DEVINST DevInst
  226. )
  227. {
  228. ULONG ulSize;
  229. TCHAR DeviceID[MAX_DEVICE_ID_LEN];
  230. LPTSTR DriveName = NULL;
  231. LPTSTR DeviceInterface = NULL;
  232. if (CM_Get_Device_ID_Ex(DevInst,
  233. DeviceID,
  234. ARRAYSIZE(DeviceID),
  235. 0,
  236. NULL
  237. ) != CR_SUCCESS) {
  238. return FALSE;
  239. }
  240. ulSize = 0;
  241. if ((CM_Get_Device_Interface_List_Size(&ulSize,
  242. (LPGUID)&VolumeClassGuid,
  243. DeviceID,
  244. 0) == CR_SUCCESS) &&
  245. (ulSize > 1) &&
  246. ((DeviceInterface = LocalAlloc(LPTR, ulSize*sizeof(TCHAR))) != NULL) &&
  247. (CM_Get_Device_Interface_List((LPGUID)&VolumeClassGuid,
  248. DeviceID,
  249. DeviceInterface,
  250. ulSize,
  251. 0
  252. ) == CR_SUCCESS) &&
  253. *DeviceInterface)
  254. {
  255. LPTSTR devicePath, p;
  256. TCHAR thisVolumeName[MAX_PATH];
  257. TCHAR enumVolumeName[MAX_PATH];
  258. TCHAR driveName[4];
  259. ULONG length;
  260. BOOL bResult;
  261. length = lstrlen(DeviceInterface);
  262. devicePath = LocalAlloc(LPTR, (length + 1) * sizeof(TCHAR) + sizeof(UNICODE_NULL));
  263. if (devicePath) {
  264. StringCchCopy(devicePath, length + 1, DeviceInterface);
  265. p = wcschr(&(devicePath[4]), TEXT('\\'));
  266. if (!p) {
  267. //
  268. // No refstring is present in the symbolic link; add a trailing
  269. // '\' char (as required by GetVolumeNameForVolumeMountPoint).
  270. //
  271. p = devicePath + length;
  272. *p = TEXT('\\');
  273. }
  274. p++;
  275. *p = UNICODE_NULL;
  276. thisVolumeName[0] = UNICODE_NULL;
  277. bResult = GetVolumeNameForVolumeMountPoint(devicePath,
  278. thisVolumeName,
  279. MAX_PATH
  280. );
  281. LocalFree(devicePath);
  282. if (bResult && thisVolumeName[0]) {
  283. driveName[1] = TEXT(':');
  284. driveName[2] = TEXT('\\');
  285. driveName[3] = TEXT('\0');
  286. for (driveName[0] = TEXT('A'); driveName[0] <= TEXT('Z'); driveName[0]++) {
  287. enumVolumeName[0] = TEXT('\0');
  288. GetVolumeNameForVolumeMountPoint(driveName, enumVolumeName, MAX_PATH);
  289. if (!lstrcmpi(thisVolumeName, enumVolumeName)) {
  290. driveName[2] = TEXT('\0');
  291. ulSize = (lstrlen(driveName) + 1) * sizeof(TCHAR);
  292. DriveName = LocalAlloc(LPTR, ulSize);
  293. if (DriveName) {
  294. StringCbCopy(DriveName, ulSize, driveName);
  295. }
  296. break;
  297. }
  298. }
  299. }
  300. }
  301. }
  302. if (DeviceInterface) {
  303. LocalFree(DeviceInterface);
  304. }
  305. return DriveName;
  306. }
  307. int
  308. CollectRelationDriveLetters(
  309. DEVINST DevInst,
  310. LPTSTR ListOfDrives,
  311. ULONG CchSizeListOfDrives
  312. )
  313. /*++
  314. This function looks at the removal relations of the specified DevInst and adds any drive
  315. letters associated with these removal relations to the ListOfDrives.
  316. Return:
  317. Number of drive letters added to the list.
  318. --*/
  319. {
  320. int NumberOfDrives = 0;
  321. LPTSTR SingleDrive = NULL;
  322. TCHAR szSeparator[32];
  323. DEVINST RelationDevInst;
  324. TCHAR DeviceInstanceId[MAX_DEVICE_ID_LEN];
  325. ULONG cchSize;
  326. PTCHAR DeviceIdRelations, CurrDevId;
  327. if (CM_Get_Device_ID(DevInst,
  328. DeviceInstanceId,
  329. ARRAYSIZE(DeviceInstanceId),
  330. 0
  331. ) == CR_SUCCESS) {
  332. cchSize = 0;
  333. if ((CM_Get_Device_ID_List_Size(&cchSize,
  334. DeviceInstanceId,
  335. CM_GETIDLIST_FILTER_REMOVALRELATIONS
  336. ) == CR_SUCCESS) &&
  337. (cchSize)) {
  338. DeviceIdRelations = LocalAlloc(LPTR, cchSize*sizeof(TCHAR));
  339. if (DeviceIdRelations) {
  340. *DeviceIdRelations = TEXT('\0');
  341. if ((CM_Get_Device_ID_List(DeviceInstanceId,
  342. DeviceIdRelations,
  343. cchSize,
  344. CM_GETIDLIST_FILTER_REMOVALRELATIONS
  345. ) == CR_SUCCESS) &&
  346. (*DeviceIdRelations)) {
  347. for (CurrDevId = DeviceIdRelations; *CurrDevId; CurrDevId += lstrlen(CurrDevId) + 1) {
  348. if (CM_Locate_DevNode(&RelationDevInst, CurrDevId, 0) == CR_SUCCESS) {
  349. SingleDrive = DevNodeToDriveLetter(RelationDevInst);
  350. if (SingleDrive) {
  351. NumberOfDrives++;
  352. //
  353. // If this is not the first drive the add a comma space separator
  354. //
  355. if (ListOfDrives[0] != TEXT('\0')) {
  356. LoadString(g_hInstance, IDS_SEPARATOR, szSeparator, sizeof(szSeparator)/sizeof(TCHAR));
  357. StringCchCat(ListOfDrives, CchSizeListOfDrives, szSeparator);
  358. }
  359. StringCchCat(ListOfDrives, CchSizeListOfDrives, SingleDrive);
  360. LocalFree(SingleDrive);
  361. }
  362. }
  363. }
  364. }
  365. LocalFree(DeviceIdRelations);
  366. }
  367. }
  368. }
  369. return NumberOfDrives;
  370. }
  371. int
  372. CollectDriveLettersForDevNodeWorker(
  373. DEVINST DevInst,
  374. LPTSTR ListOfDrives,
  375. ULONG CchSizeListOfDrives
  376. )
  377. {
  378. DEVINST ChildDevInst;
  379. DEVINST SiblingDevInst;
  380. int NumberOfDrives = 0;
  381. LPTSTR SingleDrive = NULL;
  382. TCHAR szSeparator[32];
  383. //
  384. // Enumerate through all of the siblings and children of this devnode
  385. //
  386. do {
  387. ChildDevInst = 0;
  388. SiblingDevInst = 0;
  389. CM_Get_Child(&ChildDevInst, DevInst, 0);
  390. CM_Get_Sibling(&SiblingDevInst, DevInst, 0);
  391. //
  392. // Only get the drive letter for this device if it is NOT a hotplug
  393. // device. If it is a hotplug device then it will have it's own
  394. // subtree that contains it's drive letters.
  395. //
  396. if (!IsHotPlugDevice(DevInst)) {
  397. SingleDrive = DevNodeToDriveLetter(DevInst);
  398. if (SingleDrive) {
  399. NumberOfDrives++;
  400. //
  401. // If this is not the first drive the add a comma space separator
  402. //
  403. if (ListOfDrives[0] != TEXT('\0')) {
  404. LoadString(g_hInstance, IDS_SEPARATOR, szSeparator, sizeof(szSeparator)/sizeof(TCHAR));
  405. StringCchCat(ListOfDrives, CchSizeListOfDrives, szSeparator);
  406. }
  407. StringCchCat(ListOfDrives, CchSizeListOfDrives, SingleDrive);
  408. LocalFree(SingleDrive);
  409. }
  410. //
  411. // Get the drive letters for any children of this devnode
  412. //
  413. if (ChildDevInst) {
  414. NumberOfDrives += CollectDriveLettersForDevNodeWorker(ChildDevInst, ListOfDrives, CchSizeListOfDrives);
  415. }
  416. //
  417. // Add the drive letters for any removal relations of this devnode
  418. //
  419. NumberOfDrives += CollectRelationDriveLetters(DevInst, ListOfDrives, CchSizeListOfDrives);
  420. }
  421. } while ((DevInst = SiblingDevInst) != 0);
  422. return NumberOfDrives;
  423. }
  424. LPTSTR
  425. CollectDriveLettersForDevNode(
  426. DEVINST DevInst
  427. )
  428. {
  429. TCHAR Format[MAX_PATH];
  430. TCHAR ListOfDrives[MAX_PATH];
  431. DEVINST ChildDevInst;
  432. int NumberOfDrives = 0;
  433. ULONG cbSize;
  434. LPTSTR SingleDrive = NULL;
  435. LPTSTR FinalDriveString = NULL;
  436. ListOfDrives[0] = TEXT('\0');
  437. //
  438. //First get any drive letter associated with this devnode
  439. //
  440. SingleDrive = DevNodeToDriveLetter(DevInst);
  441. if (SingleDrive) {
  442. NumberOfDrives++;
  443. StringCchCat(ListOfDrives, ARRAYSIZE(ListOfDrives), SingleDrive);
  444. LocalFree(SingleDrive);
  445. }
  446. //
  447. // Next add on any drive letters associated with the children
  448. // of this devnode
  449. //
  450. ChildDevInst = 0;
  451. CM_Get_Child(&ChildDevInst, DevInst, 0);
  452. if (ChildDevInst) {
  453. NumberOfDrives += CollectDriveLettersForDevNodeWorker(ChildDevInst,
  454. ListOfDrives,
  455. ARRAYSIZE(ListOfDrives));
  456. }
  457. //
  458. // Finally add on any drive letters associated with the removal relations
  459. // of this devnode
  460. //
  461. NumberOfDrives += CollectRelationDriveLetters(DevInst,
  462. ListOfDrives,
  463. ARRAYSIZE(ListOfDrives));
  464. if (ListOfDrives[0] != TEXT('\0')) {
  465. LoadString(g_hInstance,
  466. (NumberOfDrives > 1) ? IDS_DISKDRIVES : IDS_DISKDRIVE,
  467. Format,
  468. sizeof(Format)/sizeof(TCHAR)
  469. );
  470. cbSize = (lstrlen(ListOfDrives) + lstrlen(Format) + 1) * sizeof(TCHAR);
  471. FinalDriveString = LocalAlloc(LPTR, cbSize);
  472. if (FinalDriveString) {
  473. StringCbPrintf(FinalDriveString, cbSize, Format, ListOfDrives);
  474. }
  475. }
  476. return FinalDriveString;
  477. }
  478. ULONG
  479. RegistryDeviceName(
  480. DEVINST DevInst,
  481. PTCHAR Buffer,
  482. DWORD cbBuffer
  483. )
  484. {
  485. ULONG ulSize = 0;
  486. CONFIGRET ConfigRet;
  487. LPTSTR ListOfDrives = NULL;
  488. //
  489. // Get the list of drives
  490. //
  491. ListOfDrives = CollectDriveLettersForDevNode(DevInst);
  492. //
  493. // Try the registry for FRIENDLYNAME
  494. //
  495. ulSize = cbBuffer;
  496. *Buffer = TEXT('\0');
  497. ConfigRet = CM_Get_DevNode_Registry_Property(DevInst,
  498. CM_DRP_FRIENDLYNAME,
  499. NULL,
  500. Buffer,
  501. &ulSize,
  502. 0
  503. );
  504. if (ConfigRet != CR_SUCCESS || !(*Buffer)) {
  505. //
  506. // Try the registry for DEVICEDESC
  507. //
  508. ulSize = cbBuffer;
  509. *Buffer = TEXT('\0');
  510. ConfigRet = CM_Get_DevNode_Registry_Property(DevInst,
  511. CM_DRP_DEVICEDESC,
  512. NULL,
  513. Buffer,
  514. &ulSize,
  515. 0);
  516. }
  517. //
  518. // Concatonate on the list of drive letters if this device has drive
  519. // letters and there is enough space
  520. //
  521. if (ListOfDrives) {
  522. if ((ulSize + (lstrlen(ListOfDrives) * sizeof(TCHAR))) < cbBuffer) {
  523. StringCbCat(Buffer, cbBuffer, ListOfDrives);
  524. ulSize += (lstrlen(ListOfDrives) * sizeof(TCHAR));
  525. }
  526. LocalFree(ListOfDrives);
  527. }
  528. return ulSize;
  529. }
  530. BOOL
  531. IsDevInstInDeviceInfoSet(
  532. IN DEVINST DevInst,
  533. IN HDEVINFO hDeviceInfoSet,
  534. OUT PSP_DEVINFO_DATA DeviceInfoDataInSet OPTIONAL
  535. )
  536. {
  537. DWORD MemberIndex;
  538. SP_DEVINFO_DATA DeviceInfoData;
  539. BOOL bIsMember = FALSE;
  540. if (hDeviceInfoSet == INVALID_HANDLE_VALUE) {
  541. return FALSE;
  542. }
  543. DeviceInfoData.cbSize = sizeof(DeviceInfoData);
  544. MemberIndex = 0;
  545. while (SetupDiEnumDeviceInfo(hDeviceInfoSet,
  546. MemberIndex,
  547. &DeviceInfoData
  548. )) {
  549. if (DevInst == DeviceInfoData.DevInst) {
  550. bIsMember = TRUE;
  551. if (ARGUMENT_PRESENT(DeviceInfoDataInSet)) {
  552. ASSERT(DeviceInfoDataInSet->cbSize >= DeviceInfoData.cbSize);
  553. memcpy(DeviceInfoDataInSet, &DeviceInfoData, DeviceInfoDataInSet->cbSize);
  554. }
  555. break;
  556. }
  557. MemberIndex++;
  558. }
  559. return bIsMember;
  560. }
  561. BOOL
  562. AnyHotPlugDevices(
  563. IN HDEVINFO hRemovableDeviceInfoSet,
  564. IN HDEVINFO hOldDeviceInfoSet,
  565. OUT PBOOL bNewHotPlugDevice OPTIONAL
  566. )
  567. {
  568. SP_DEVINFO_DATA DeviceInfoData;
  569. DWORD dwMemberIndex;
  570. BOOL bAnyHotPlugDevices = FALSE;
  571. //
  572. // Initialize output parameters.
  573. //
  574. if (ARGUMENT_PRESENT(bNewHotPlugDevice)) {
  575. *bNewHotPlugDevice = FALSE;
  576. }
  577. if (hRemovableDeviceInfoSet == INVALID_HANDLE_VALUE) {
  578. return FALSE;
  579. }
  580. //
  581. // We already have an updated list of just removable devices, so we can just
  582. // enumerate those devices and see if any also meet the criteria for hotplug
  583. // devices.
  584. //
  585. DeviceInfoData.cbSize = sizeof(DeviceInfoData);
  586. dwMemberIndex = 0;
  587. while (SetupDiEnumDeviceInfo(hRemovableDeviceInfoSet,
  588. dwMemberIndex,
  589. &DeviceInfoData)) {
  590. if (IsHotPlugDevice(DeviceInfoData.DevInst)) {
  591. bAnyHotPlugDevices = TRUE;
  592. //
  593. // If the caller doesn't want to know if any new hotplug devices
  594. // have arrived then just break at this point.
  595. //
  596. if (!ARGUMENT_PRESENT(bNewHotPlugDevice)) {
  597. break;
  598. }
  599. //
  600. // If the caller wants to know if the hotplug device is new, we must
  601. // have a list of devices to check against. If we don't have a list
  602. // of devices to check against then just break at this point since
  603. // there is nothing left to do.
  604. //
  605. if (hOldDeviceInfoSet == INVALID_HANDLE_VALUE) {
  606. break;
  607. }
  608. //
  609. // The caller wants to know if we have any new hotplug devices. So,
  610. // we will compare this hotplug device to see if it is also in the
  611. // old current list of devices. If it is not then we have found a
  612. // new hotplug device.
  613. //
  614. if (!IsDevInstInDeviceInfoSet(DeviceInfoData.DevInst,
  615. hOldDeviceInfoSet,
  616. NULL)) {
  617. *bNewHotPlugDevice = TRUE;
  618. }
  619. }
  620. dwMemberIndex++;
  621. }
  622. return bAnyHotPlugDevices;
  623. }
  624. BOOL
  625. UpdateRemovableDeviceList(
  626. IN HDEVINFO hDeviceInfoSet,
  627. OUT PBOOL bRemovableDeviceAdded OPTIONAL,
  628. OUT PBOOL bRemovableDeviceRemoved OPTIONAL,
  629. OUT PBOOL bRemovableDeviceFailure OPTIONAL
  630. )
  631. {
  632. SP_DEVINFO_DATA DeviceInfoData;
  633. TCHAR DeviceInstanceId[MAX_DEVICE_ID_LEN];
  634. DWORD dwMemberIndex;
  635. ULONG ulDevStatus, ulDevProblem;
  636. //
  637. // Initialize output parameters.
  638. //
  639. if (ARGUMENT_PRESENT(bRemovableDeviceAdded)) {
  640. *bRemovableDeviceAdded = FALSE;
  641. }
  642. if (ARGUMENT_PRESENT(bRemovableDeviceRemoved)) {
  643. *bRemovableDeviceRemoved = FALSE;
  644. }
  645. if (ARGUMENT_PRESENT(bRemovableDeviceFailure)) {
  646. *bRemovableDeviceFailure = FALSE;
  647. }
  648. //
  649. // We at least need a current list of devices in the system.
  650. //
  651. if (hDeviceInfoSet == INVALID_HANDLE_VALUE) {
  652. return FALSE;
  653. }
  654. if (g_hRemovableDeviceInfoSet == INVALID_HANDLE_VALUE) {
  655. //
  656. // If we don't already have a global device info set for removable
  657. // devices in the system, create one now. No removable devices have
  658. // been removed in this case, because we didn't know about any prior to
  659. // this.
  660. //
  661. g_hRemovableDeviceInfoSet = SetupDiCreateDeviceInfoListEx(NULL,
  662. NULL,
  663. NULL,
  664. NULL);
  665. //
  666. // If we couldn't create a list to store removable devices, there's no
  667. // point in checking anything else here.
  668. //
  669. if (g_hRemovableDeviceInfoSet == INVALID_HANDLE_VALUE) {
  670. return FALSE;
  671. }
  672. } else {
  673. //
  674. // If we already had a list of removable devices, enumerate the devices
  675. // to see if any have been removed from the system since we last
  676. // checked.
  677. //
  678. DeviceInfoData.cbSize = sizeof(DeviceInfoData);
  679. dwMemberIndex = 0;
  680. while (SetupDiEnumDeviceInfo(g_hRemovableDeviceInfoSet,
  681. dwMemberIndex,
  682. &DeviceInfoData)) {
  683. if (!IsDevInstInDeviceInfoSet(DeviceInfoData.DevInst,
  684. hDeviceInfoSet,
  685. NULL)) {
  686. //
  687. // A removable device is missing from the system.
  688. //
  689. if (ARGUMENT_PRESENT(bRemovableDeviceRemoved)) {
  690. *bRemovableDeviceRemoved = TRUE;
  691. }
  692. #if DBG // DBG
  693. if (SetupDiGetDeviceInstanceId(g_hRemovableDeviceInfoSet,
  694. &DeviceInfoData,
  695. DeviceInstanceId,
  696. MAX_DEVICE_ID_LEN,
  697. NULL)) {
  698. KdPrintEx((DPFLTR_PNPMGR_ID,
  699. (0x00000010 | DPFLTR_MASK),
  700. "HPLUG: Removing device %ws from g_hRemovableDeviceInfoSet.\n",
  701. DeviceInstanceId));
  702. }
  703. #endif // DBG
  704. //
  705. // Remove the device from the global list of removable devices.
  706. //
  707. SetupDiDeleteDeviceInfo(g_hRemovableDeviceInfoSet,
  708. &DeviceInfoData);
  709. }
  710. //
  711. // Increment the enumeration index.
  712. //
  713. dwMemberIndex++;
  714. }
  715. }
  716. //
  717. // Enumerate the current list of devices and see if any removable devices
  718. // have been added to the system.
  719. //
  720. DeviceInfoData.cbSize = sizeof(DeviceInfoData);
  721. dwMemberIndex = 0;
  722. while (SetupDiEnumDeviceInfo(hDeviceInfoSet,
  723. dwMemberIndex,
  724. &DeviceInfoData)) {
  725. //
  726. // If this device is not already in the removable device list, and it's
  727. // removable, add it to the list.
  728. //
  729. if ((!IsDevInstInDeviceInfoSet(DeviceInfoData.DevInst,
  730. g_hRemovableDeviceInfoSet,
  731. NULL)) &&
  732. (IsRemovableDevice(DeviceInfoData.DevInst))) {
  733. //
  734. // A removable device was added to the system.
  735. //
  736. if (ARGUMENT_PRESENT(bRemovableDeviceAdded)) {
  737. *bRemovableDeviceAdded = TRUE;
  738. }
  739. //
  740. // Add the device to the global list of removable devices.
  741. //
  742. if (SetupDiGetDeviceInstanceId(hDeviceInfoSet,
  743. &DeviceInfoData,
  744. DeviceInstanceId,
  745. MAX_DEVICE_ID_LEN,
  746. NULL)) {
  747. KdPrintEx((DPFLTR_PNPMGR_ID,
  748. (0x00000010 | DPFLTR_MASK),
  749. "HPLUG: Adding device %ws to g_hRemovableDeviceInfoSet\n",
  750. DeviceInstanceId));
  751. SetupDiOpenDeviceInfo(g_hRemovableDeviceInfoSet,
  752. DeviceInstanceId,
  753. NULL,
  754. 0,
  755. NULL);
  756. }
  757. //
  758. // If the caller is also interested in device failures, check the
  759. // status of the new device.
  760. //
  761. if (ARGUMENT_PRESENT(bRemovableDeviceFailure)) {
  762. if (CM_Get_DevNode_Status_Ex(&ulDevStatus,
  763. &ulDevProblem,
  764. DeviceInfoData.DevInst,
  765. 0,
  766. NULL) == CR_SUCCESS) {
  767. if (((ulDevStatus & DN_HAS_PROBLEM) != 0) &&
  768. (ulDevProblem != CM_PROB_NOT_CONFIGURED) &&
  769. (ulDevProblem != CM_PROB_REINSTALL)) {
  770. *bRemovableDeviceFailure = TRUE;
  771. KdPrintEx((DPFLTR_PNPMGR_ID,
  772. (0x00000010 | DPFLTR_MASK),
  773. "HPLUG: Device %ws considered a failed insertion (Status = 0x%08lx, Problem = 0x%08lx)\n",
  774. DeviceInstanceId, ulDevStatus, ulDevProblem));
  775. }
  776. }
  777. }
  778. }
  779. //
  780. // Increment the enumeration index.
  781. //
  782. dwMemberIndex++;
  783. }
  784. return TRUE;
  785. }
  786. BOOL
  787. AddHotPlugDevice(
  788. DEVINST DeviceInstance,
  789. PHOTPLUGDEVICES *HotPlugDevicesList
  790. )
  791. {
  792. PHOTPLUGDEVICES HotPlugDevice;
  793. DWORD cbSize, cchDevName, cchDevInstanceId;
  794. CONFIGRET ConfigRet;
  795. TCHAR DevInstanceId[MAX_DEVICE_ID_LEN];
  796. TCHAR DevName[MAX_PATH];
  797. //
  798. // Retrieve the device instance id
  799. //
  800. *DevInstanceId = TEXT('\0');
  801. cchDevInstanceId = ARRAYSIZE(DevInstanceId);
  802. ConfigRet = CM_Get_Device_ID(DeviceInstance,
  803. (PVOID)DevInstanceId,
  804. cchDevInstanceId,
  805. 0);
  806. if (ConfigRet != CR_SUCCESS || !*DevInstanceId) {
  807. *DevInstanceId = TEXT('\0');
  808. cchDevInstanceId = 0;
  809. }
  810. cbSize = sizeof(HOTPLUGDEVICES) + cchDevInstanceId;
  811. HotPlugDevice = LocalAlloc(LPTR, cbSize);
  812. if (!HotPlugDevice) {
  813. return FALSE;
  814. }
  815. //
  816. // link it in
  817. //
  818. HotPlugDevice->Next = *HotPlugDevicesList;
  819. *HotPlugDevicesList = HotPlugDevice;
  820. HotPlugDevice->DevInst = DeviceInstance;
  821. //
  822. // copy in the names
  823. //
  824. StringCchCopy(HotPlugDevice->DevInstanceId, cchDevInstanceId, DevInstanceId);
  825. cchDevName = RegistryDeviceName(DeviceInstance, DevName, sizeof(DevName));
  826. HotPlugDevice->DevName = LocalAlloc(LPTR, cchDevName + sizeof(TCHAR));
  827. if (HotPlugDevice->DevName) {
  828. StringCchCopy(HotPlugDevice->DevName, cchDevName, DevName);
  829. }
  830. return TRUE;
  831. }
  832. BOOL
  833. AddHotPlugDevices(
  834. PHOTPLUGDEVICES *HotPlugDevicesList
  835. )
  836. {
  837. CONFIGRET ConfigRet;
  838. SP_DEVINFO_DATA DeviceInfoData;
  839. DWORD dwMemberIndex;
  840. //
  841. // Initialize output list of hotplug devices.
  842. //
  843. *HotPlugDevicesList = NULL;
  844. //
  845. // Enumerate the list of removable devices.
  846. //
  847. DeviceInfoData.cbSize = sizeof(DeviceInfoData);
  848. dwMemberIndex = 0;
  849. while (SetupDiEnumDeviceInfo(g_hRemovableDeviceInfoSet,
  850. dwMemberIndex,
  851. &DeviceInfoData)) {
  852. //
  853. // If any removable device also meets the criteria of a hotplug device,
  854. // add it to the linked list.
  855. //
  856. if (IsHotPlugDevice(DeviceInfoData.DevInst)) {
  857. AddHotPlugDevice(DeviceInfoData.DevInst, HotPlugDevicesList);
  858. }
  859. dwMemberIndex++;
  860. }
  861. return TRUE;
  862. }
  863. void
  864. FreeHotPlugDevicesList(
  865. PHOTPLUGDEVICES *HotPlugDevicesList
  866. )
  867. {
  868. PHOTPLUGDEVICES HotPlugDevices, HotPlugDevicesFree;
  869. HotPlugDevices = *HotPlugDevicesList;
  870. *HotPlugDevicesList = NULL;
  871. while (HotPlugDevices) {
  872. HotPlugDevicesFree = HotPlugDevices;
  873. HotPlugDevices = HotPlugDevicesFree->Next;
  874. if (HotPlugDevicesFree->DevName) {
  875. LocalFree(HotPlugDevicesFree->DevName);
  876. HotPlugDevicesFree->DevName = NULL;
  877. }
  878. LocalFree(HotPlugDevicesFree);
  879. }
  880. }
  881. /*
  882. * Shows or deletes the shell notify icon and tip
  883. */
  884. void
  885. HotPlugShowNotifyIcon(
  886. HWND hWnd,
  887. BOOL bShowIcon
  888. )
  889. {
  890. TCHAR HotPlugTip[64];
  891. ShowShellIcon = bShowIcon;
  892. if (bShowIcon) {
  893. LoadString(g_hInstance,
  894. IDS_HOTPLUGTIP,
  895. HotPlugTip,
  896. sizeof(HotPlugTip)/sizeof(TCHAR)
  897. );
  898. HotPlugIcon = LoadImage(g_hInstance,
  899. MAKEINTRESOURCE(IDI_HOTPLUG),
  900. IMAGE_ICON,
  901. 16,
  902. 16,
  903. 0
  904. );
  905. SysTray_NotifyIcon(hWnd, STWM_NOTIFYHOTPLUG, NIM_ADD, HotPlugIcon, HotPlugTip);
  906. } else {
  907. SysTray_NotifyIcon(hWnd, STWM_NOTIFYHOTPLUG, NIM_DELETE, NULL, NULL);
  908. if (HotPlugIcon) {
  909. DestroyIcon(HotPlugIcon);
  910. }
  911. }
  912. }
  913. //
  914. // first time intialization of Hotplug module.
  915. //
  916. BOOL
  917. HotPlugInit(
  918. HWND hWnd
  919. )
  920. {
  921. HDEVINFO hNewDeviceInfoSet;
  922. BOOL bAnyHotPlugDevices;
  923. LARGE_INTEGER liDelayTime;
  924. //
  925. // Get a new "current" list of all devices present in the system.
  926. //
  927. hNewDeviceInfoSet = SetupDiGetClassDevs(NULL,
  928. NULL,
  929. NULL,
  930. DIGCF_ALLCLASSES | DIGCF_PRESENT);
  931. //
  932. // Update the list of removable devices, don't play any sounds.
  933. //
  934. UpdateRemovableDeviceList(hNewDeviceInfoSet,
  935. NULL,
  936. NULL,
  937. NULL);
  938. //
  939. // Find out whether there are any HotPlug devices in the list of removable
  940. // devices. We're just deciding whether the icon needs to be enabled or
  941. // not, so we don't care if there are any new hotplug devices or not (we
  942. // won't even look at g_hCurrentDeviceInfoSet).
  943. //
  944. bAnyHotPlugDevices = AnyHotPlugDevices(g_hRemovableDeviceInfoSet,
  945. g_hCurrentDeviceInfoSet,
  946. NULL);
  947. //
  948. // Delete the old current list of devices and set it
  949. // (g_hCurrentDeviceInfoSet) to the new current list.
  950. //
  951. if (g_hCurrentDeviceInfoSet != INVALID_HANDLE_VALUE) {
  952. SetupDiDestroyDeviceInfoList(g_hCurrentDeviceInfoSet);
  953. }
  954. //
  955. // Update the global list of devices currently in the system.
  956. //
  957. g_hCurrentDeviceInfoSet = hNewDeviceInfoSet;
  958. //
  959. // If hotplug was previously initialized, we don't need to create the events
  960. // and timers below.
  961. //
  962. if (HotPlugInitialized) {
  963. return bAnyHotPlugDevices;
  964. }
  965. hEjectEvent = CreateEvent(NULL, TRUE, TRUE, HPLUG_EJECT_EVENT);
  966. HotPlugInitialized = TRUE;
  967. return bAnyHotPlugDevices;
  968. }
  969. BOOL
  970. HotPlug_CheckEnable(
  971. HWND hWnd,
  972. BOOL bSvcEnabled
  973. )
  974. /*++
  975. Routine Description:
  976. Called at init time and whenever services are enabled/disabled.
  977. Hotplug is always alive to receive device change notifications.
  978. The shell notify icon is enabled\disabled depending on:
  979. - systray registry setting for services,
  980. AND
  981. - availability of removable devices.
  982. Arguments:
  983. hwnd - Our Window handle
  984. bSvcEnabled - TRUE Service is being enabled.
  985. Return Value:
  986. BOOL Returns TRUE if active.
  987. --*/
  988. {
  989. BOOL EnableShellIcon;
  990. HANDLE hHotplugBalloonEvent = NULL;
  991. //
  992. // If we are being enabled and we are already enabled, or we
  993. // are being disabled and we are already disabled then just
  994. // return since we have nothing to do.
  995. //
  996. if (ServiceEnabled == bSvcEnabled) {
  997. return ServiceEnabled;
  998. }
  999. ServiceEnabled = bSvcEnabled;
  1000. //
  1001. // There are some special checks we need to make if we are enabling the
  1002. // hotplug service.
  1003. //
  1004. if (bSvcEnabled) {
  1005. //
  1006. // If this is a remote session and the user does not have the
  1007. // SE_LOAD_DRIVER_NAME privileges then we won't enable the service
  1008. // since they do not have the privileges to stop any hotplug devices.
  1009. //
  1010. if (GetSystemMetrics(SM_REMOTESESSION) &&
  1011. !pDoesUserHavePrivilege((PCTSTR)SE_LOAD_DRIVER_NAME)) {
  1012. ServiceEnabled = FALSE;
  1013. } else {
  1014. //
  1015. // hotplug.dll will disable the hotplug service when it is
  1016. // displaying a balloon for a safe removal event. When it is
  1017. // displaying it's balloon we don't want to enable our service
  1018. // because then there will be two hotplug icons in the tray.
  1019. // So if it's named event is set then we will ignore any attempts
  1020. // to enable our service. Once hotplug.dll's balloon has gone
  1021. // away then it will automatically enable the hotplug service.
  1022. //
  1023. hHotplugBalloonEvent = CreateEvent(NULL,
  1024. FALSE,
  1025. TRUE,
  1026. TEXT("Local\\HotPlug_TaskBarIcon_Event")
  1027. );
  1028. if (hHotplugBalloonEvent) {
  1029. if (WaitForSingleObject(hHotplugBalloonEvent, 0) != WAIT_OBJECT_0) {
  1030. ServiceEnabled = FALSE;
  1031. }
  1032. CloseHandle(hHotplugBalloonEvent);
  1033. }
  1034. }
  1035. }
  1036. EnableShellIcon = ServiceEnabled && HotPlugInit(hWnd);
  1037. HotPlugShowNotifyIcon(hWnd, EnableShellIcon);
  1038. return EnableShellIcon;
  1039. }
  1040. DWORD
  1041. HotPlugEjectDevice_Thread(
  1042. LPVOID pThreadParam
  1043. )
  1044. {
  1045. DEVNODE DevNode = (DEVNODE)(ULONG_PTR)pThreadParam;
  1046. CONFIGRET ConfigRet;
  1047. ConfigRet = CM_Request_Device_Eject_Ex(DevNode,
  1048. NULL,
  1049. NULL,
  1050. 0,
  1051. 0,
  1052. NULL);
  1053. //
  1054. // Set the hEjectEvent so that the right-click popup menu will work again
  1055. // now that we are finished ejecting/stopping the device.
  1056. //
  1057. SetEvent(hEjectEvent);
  1058. SetLastError(ConfigRet);
  1059. return (ConfigRet == CR_SUCCESS);
  1060. }
  1061. void
  1062. HotPlugEjectDevice(
  1063. HWND hwnd,
  1064. DEVNODE DevNode
  1065. )
  1066. {
  1067. DWORD ThreadId;
  1068. //
  1069. // Reset the hEjectEvent so that the user can't bring up the right-click
  1070. // popup menu when we are in the process of ejecting/stopping a device.
  1071. //
  1072. ResetEvent(hEjectEvent);
  1073. //
  1074. // We need to have stobject.dll eject/stop the device on a separate
  1075. // thread because if we remove a device that stobject.dll listens for
  1076. // (battery, sound, ect.) we will cause a large delay and the eject/stop
  1077. // could end up getting vetoed because the stobject.dll code could not be
  1078. // processed and release it's handles because we were locking up the main
  1079. // thread.
  1080. //
  1081. CreateThread(NULL,
  1082. 0,
  1083. (LPTHREAD_START_ROUTINE)HotPlugEjectDevice_Thread,
  1084. (LPVOID)(ULONG_PTR)DevNode,
  1085. 0,
  1086. &ThreadId
  1087. );
  1088. }
  1089. void
  1090. HotPlug_Timer(
  1091. HWND hwnd
  1092. )
  1093. /*++
  1094. Routine Description:
  1095. Hotplug Timer msg handler, used to invoke hmenuEject for single Left click
  1096. Arguments:
  1097. hDlg - Our Window handle
  1098. Return Value:
  1099. BOOL Returns TRUE if active.
  1100. --*/
  1101. {
  1102. POINT pt;
  1103. UINT MenuIndex;
  1104. PHOTPLUGDEVICES HotPlugDevicesList;
  1105. PHOTPLUGDEVICES SingleHotPlugDevice;
  1106. TCHAR MenuDeviceName[MAX_PATH+64];
  1107. TCHAR Format[64];
  1108. KillTimer(hwnd, HOTPLUG_TIMER_ID);
  1109. if (!HotPlugInitialized) {
  1110. PostMessage(hwnd, STWM_ENABLESERVICE, 0, TRUE);
  1111. return;
  1112. }
  1113. //
  1114. // We only want to create the popup menu if the hEjectEvent is signaled.
  1115. // If it is not signaled then we are in the middle of ejecting/stopping
  1116. // a device on a separate thread and don't want to allow the user to
  1117. // bring up the menu until we are finished with that device.
  1118. //
  1119. if (!hEjectEvent ||
  1120. WaitForSingleObject(hEjectEvent, 0) == WAIT_OBJECT_0) {
  1121. //
  1122. // We are not in the middle of ejecting/stopping a device so we should
  1123. // display the popup menu.
  1124. //
  1125. HMENU hmenuEject = CreatePopupMenu();
  1126. if (hmenuEject) {
  1127. SetForegroundWindow(hwnd);
  1128. GetCursorPos(&pt);
  1129. //
  1130. // Add each of the removable devices in the list to the menu.
  1131. //
  1132. if (!AddHotPlugDevices(&HotPlugDevicesList)) {
  1133. DestroyMenu(hmenuEject);
  1134. return;
  1135. }
  1136. SingleHotPlugDevice = HotPlugDevicesList;
  1137. //
  1138. // Add a title and separator at the top of the menu.
  1139. //
  1140. LoadString(g_hInstance,
  1141. IDS_HPLUGMENU_REMOVE,
  1142. Format,
  1143. sizeof(Format)/sizeof(TCHAR)
  1144. );
  1145. MenuIndex = 1;
  1146. while (SingleHotPlugDevice) {
  1147. StringCchPrintf(MenuDeviceName,
  1148. ARRAYSIZE(MenuDeviceName),
  1149. Format,
  1150. SingleHotPlugDevice->DevName);
  1151. AppendMenu(hmenuEject, MF_STRING, MenuIndex, MenuDeviceName);
  1152. SingleHotPlugDevice->EjectMenuIndex = MenuIndex++;
  1153. SingleHotPlugDevice = SingleHotPlugDevice->Next;
  1154. }
  1155. MenuIndex = TrackPopupMenu(hmenuEject,
  1156. TPM_LEFTBUTTON | TPM_RETURNCMD | TPM_NONOTIFY,
  1157. pt.x,
  1158. pt.y,
  1159. 0,
  1160. hwnd,
  1161. NULL
  1162. );
  1163. SingleHotPlugDevice = HotPlugDevicesList;
  1164. while (SingleHotPlugDevice) {
  1165. if (MenuIndex == SingleHotPlugDevice->EjectMenuIndex) {
  1166. DEVNODE DevNode;
  1167. if (CM_Locate_DevNode(&DevNode,
  1168. SingleHotPlugDevice->DevInstanceId,
  1169. 0) == CR_SUCCESS) {
  1170. HotPlugEjectDevice(hwnd, DevNode);
  1171. }
  1172. break;
  1173. }
  1174. SingleHotPlugDevice = SingleHotPlugDevice->Next;
  1175. }
  1176. if (!SingleHotPlugDevice) {
  1177. SetIconFocus(hwnd, STWM_NOTIFYHOTPLUG);
  1178. }
  1179. FreeHotPlugDevicesList(&HotPlugDevicesList);
  1180. }
  1181. DestroyMenu(hmenuEject);
  1182. }
  1183. return;
  1184. }
  1185. void
  1186. HotPlugContextMenu(
  1187. HWND hwnd
  1188. )
  1189. {
  1190. POINT pt;
  1191. HMENU ContextMenu;
  1192. UINT MenuIndex;
  1193. TCHAR Buffer[MAX_PATH];
  1194. ContextMenu = CreatePopupMenu();
  1195. if (!ContextMenu) {
  1196. return;
  1197. }
  1198. SetForegroundWindow(hwnd);
  1199. GetCursorPos(&pt);
  1200. LoadString(g_hInstance, IDS_HPLUGMENU_PROPERTIES, Buffer, sizeof(Buffer)/sizeof(TCHAR));
  1201. AppendMenu(ContextMenu, MF_STRING,IDS_HPLUGMENU_PROPERTIES, Buffer);
  1202. SetMenuDefaultItem(ContextMenu, IDS_HPLUGMENU_PROPERTIES, FALSE);
  1203. MenuIndex = TrackPopupMenu(ContextMenu,
  1204. TPM_RIGHTBUTTON | TPM_RETURNCMD | TPM_NONOTIFY,
  1205. pt.x,
  1206. pt.y,
  1207. 0,
  1208. hwnd,
  1209. NULL
  1210. );
  1211. switch (MenuIndex) {
  1212. case IDS_HPLUGMENU_PROPERTIES:
  1213. SysTray_RunProperties(IDS_RUNHPLUGPROPERTIES);
  1214. break;
  1215. }
  1216. DestroyMenu(ContextMenu);
  1217. SetIconFocus(hwnd, STWM_NOTIFYHOTPLUG);
  1218. return;
  1219. }
  1220. void
  1221. HotPlug_Notify(
  1222. HWND hwnd,
  1223. WPARAM wParam,
  1224. LPARAM lParam
  1225. )
  1226. {
  1227. switch (lParam) {
  1228. case WM_RBUTTONUP:
  1229. HotPlugContextMenu(hwnd);
  1230. break;
  1231. case WM_LBUTTONDOWN:
  1232. SetTimer(hwnd, HOTPLUG_TIMER_ID, GetDoubleClickTime()+100, NULL);
  1233. break;
  1234. case WM_LBUTTONDBLCLK:
  1235. KillTimer(hwnd, HOTPLUG_TIMER_ID);
  1236. SysTray_RunProperties(IDS_RUNHPLUGPROPERTIES);
  1237. break;
  1238. }
  1239. return;
  1240. }
  1241. int
  1242. HotPlug_DeviceChangeTimer(
  1243. HWND hDlg
  1244. )
  1245. {
  1246. BOOL bAnyHotPlugDevices, bNewHotPlugDevice;
  1247. BOOL bRemovableDeviceAdded, bRemovableDeviceRemoved, bRemovableDeviceFailure;
  1248. HDEVINFO hNewDeviceInfoSet;
  1249. KillTimer(hDlg, HOTPLUG_DEVICECHANGE_TIMERID);
  1250. //
  1251. // If the service is not enabled then don't bother because the icon will NOT
  1252. // be shown, sounds will not be played, etc. (see notes for
  1253. // HotplugPlaySoundThisSession).
  1254. //
  1255. if (!ServiceEnabled) {
  1256. goto Clean0;
  1257. }
  1258. //
  1259. // Get a new "current" list of all devices present in the system.
  1260. //
  1261. hNewDeviceInfoSet = SetupDiGetClassDevs(NULL,
  1262. NULL,
  1263. NULL,
  1264. DIGCF_ALLCLASSES | DIGCF_PRESENT);
  1265. //
  1266. // Update the list of removable devices, based on the new current list.
  1267. //
  1268. UpdateRemovableDeviceList(hNewDeviceInfoSet,
  1269. &bRemovableDeviceAdded,
  1270. &bRemovableDeviceRemoved,
  1271. &bRemovableDeviceFailure);
  1272. //
  1273. // If we should play sounds in this session, check if any removable devices
  1274. // were either added or removed.
  1275. //
  1276. if (HotplugPlaySoundThisSession()) {
  1277. //
  1278. // We'll only play one sound at a time, so if we discover that multiple
  1279. // events have happened simultaneously, let failure override arrival,
  1280. // which overrides removal. This way the user receives notification of
  1281. // the most important event.
  1282. //
  1283. if (bRemovableDeviceFailure) {
  1284. PlaySound(DEVICE_FAILURE_SOUND, NULL, SND_ASYNC|SND_NODEFAULT);
  1285. } else if (bRemovableDeviceAdded) {
  1286. PlaySound(DEVICE_ARRIVAL_SOUND, NULL, SND_ASYNC|SND_NODEFAULT);
  1287. } else if (bRemovableDeviceRemoved) {
  1288. PlaySound(DEVICE_REMOVAL_SOUND, NULL, SND_ASYNC|SND_NODEFAULT);
  1289. }
  1290. }
  1291. //
  1292. // Let's see if we have any hot plug devices, which means we need to
  1293. // show the systray icon. We also want to know about new hotplug
  1294. // devices that just arrived, so we compare the set of removable devices
  1295. // (which we just updated) against the old current set of devices in the
  1296. // system.
  1297. //
  1298. bAnyHotPlugDevices = AnyHotPlugDevices(g_hRemovableDeviceInfoSet,
  1299. g_hCurrentDeviceInfoSet,
  1300. &bNewHotPlugDevice);
  1301. if (bAnyHotPlugDevices) {
  1302. //
  1303. // We have some hotplug devices so make sure the icon is shown
  1304. //
  1305. if (!ShowShellIcon) {
  1306. HotPlugShowNotifyIcon(hDlg, TRUE);
  1307. }
  1308. } else {
  1309. //
  1310. // There are NOT any hot plug devices so if the icon is still being
  1311. // shown, then hide it.
  1312. //
  1313. if (ShowShellIcon) {
  1314. HotPlugShowNotifyIcon(hDlg, FALSE);
  1315. }
  1316. }
  1317. //
  1318. // Delete the old current list of devices and set it
  1319. // (g_hCurrentDeviceInfoSet) to the new current list.
  1320. //
  1321. if (g_hCurrentDeviceInfoSet != INVALID_HANDLE_VALUE) {
  1322. SetupDiDestroyDeviceInfoList(g_hCurrentDeviceInfoSet);
  1323. }
  1324. g_hCurrentDeviceInfoSet = hNewDeviceInfoSet;
  1325. Clean0:
  1326. return 0;
  1327. }
  1328. void
  1329. HotPlug_DeviceChange(
  1330. HWND hwnd,
  1331. WPARAM wParam,
  1332. LPARAM lParam
  1333. )
  1334. /*++
  1335. Routine Description:
  1336. Handle WM_DEVICECHANGE messages.
  1337. Arguments:
  1338. hDlg - Window handle of Dialog
  1339. wParam - DBT Event
  1340. lParam - DBT event notification type.
  1341. Return Value:
  1342. --*/
  1343. {
  1344. LARGE_INTEGER liDelayTime;
  1345. NOTIFYICONDATA nid;
  1346. BOOL bPresent;
  1347. switch(wParam) {
  1348. case DBT_DEVNODES_CHANGED:
  1349. //
  1350. // To avoid deadlock with CM, a timer is started and the timer
  1351. // message handler does the real work.
  1352. //
  1353. SetTimer(hwnd, HOTPLUG_DEVICECHANGE_TIMERID, 100, NULL);
  1354. break;
  1355. case DBT_CONFIGCHANGED:
  1356. //
  1357. // A docking event (dock, undock, surprise undock, etc) has
  1358. // occured. Play a sound for hardware profile changes if we're
  1359. // supposed to.
  1360. //
  1361. if (HotplugPlaySoundThisSession()) {
  1362. if ((CM_Is_Dock_Station_Present(&bPresent) == CR_SUCCESS) &&
  1363. (bPresent)) {
  1364. //
  1365. // If there is a dock present, we most-likely just docked
  1366. // (though we may have just ejected one of many docks), so
  1367. // play an arrival.
  1368. //
  1369. PlaySound(DEVICE_ARRIVAL_SOUND, NULL, SND_ASYNC|SND_NODEFAULT);
  1370. } else {
  1371. //
  1372. // If no dock is present we just undocked, so play a
  1373. // removal.
  1374. //
  1375. PlaySound(DEVICE_REMOVAL_SOUND, NULL, SND_ASYNC|SND_NODEFAULT);
  1376. }
  1377. }
  1378. break;
  1379. default:
  1380. break;
  1381. }
  1382. return;
  1383. }
  1384. void
  1385. HotPlug_WmDestroy(
  1386. HWND hWnd
  1387. )
  1388. {
  1389. if (hEjectEvent) {
  1390. CloseHandle(hEjectEvent);
  1391. }
  1392. if (g_hCurrentDeviceInfoSet != INVALID_HANDLE_VALUE) {
  1393. SetupDiDestroyDeviceInfoList(g_hCurrentDeviceInfoSet);
  1394. g_hCurrentDeviceInfoSet = INVALID_HANDLE_VALUE;
  1395. }
  1396. if (g_hRemovableDeviceInfoSet != INVALID_HANDLE_VALUE) {
  1397. SetupDiDestroyDeviceInfoList(g_hRemovableDeviceInfoSet);
  1398. g_hRemovableDeviceInfoSet = INVALID_HANDLE_VALUE;
  1399. }
  1400. }
  1401. void
  1402. HotPlug_SessionChange(
  1403. HWND hWnd,
  1404. WPARAM wParam,
  1405. LPARAM lParam
  1406. )
  1407. {
  1408. //
  1409. // If our console session is getting disconnected then disable our service
  1410. // since we don't need to do any work if no UI is being displayed.
  1411. //
  1412. // If our console session is getting connected then re-enable our service.
  1413. //
  1414. if ((wParam == WTS_CONSOLE_CONNECT) ||
  1415. (wParam == WTS_REMOTE_CONNECT)) {
  1416. HotPlug_CheckEnable(hWnd, TRUE);
  1417. } else if ((wParam == WTS_CONSOLE_DISCONNECT) ||
  1418. (wParam == WTS_REMOTE_DISCONNECT)) {
  1419. HotPlug_CheckEnable(hWnd, FALSE);
  1420. }
  1421. }
  1422. BOOL
  1423. IsFastUserSwitchingEnabled(
  1424. VOID
  1425. )
  1426. /*++
  1427. Routine Description:
  1428. Checks to see if Terminal Services Fast User Switching is enabled. This is
  1429. to check if we should use the physical console session for UI dialogs, or
  1430. always use session 0.
  1431. Fast User Switching exists only on workstation product version, where terminal
  1432. services are available, when AllowMultipleTSSessions is set.
  1433. On server and above, or when multiple TS users are not allowed, session 0
  1434. can only be attached remotely be special request, in which case it should be
  1435. considered the "Console" session.
  1436. Arguments:
  1437. None.
  1438. Return Value:
  1439. Returns TRUE if Fast User Switching is currently enabled, FALSE otherwise.
  1440. --*/
  1441. {
  1442. static BOOL bVerified = FALSE;
  1443. static BOOL bIsTSWorkstation = FALSE;
  1444. HKEY hKey;
  1445. ULONG ulSize, ulValue;
  1446. BOOL bFusEnabled;
  1447. //
  1448. // Verify the product version if we haven't already.
  1449. //
  1450. if (!bVerified) {
  1451. OSVERSIONINFOEX osvix;
  1452. DWORDLONG dwlConditionMask = 0;
  1453. ZeroMemory(&osvix, sizeof(OSVERSIONINFOEX));
  1454. osvix.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
  1455. osvix.wProductType = VER_NT_WORKSTATION;
  1456. VER_SET_CONDITION(dwlConditionMask, VER_PRODUCT_TYPE, VER_LESS_EQUAL);
  1457. osvix.wSuiteMask = VER_SUITE_TERMINAL | VER_SUITE_SINGLEUSERTS;
  1458. VER_SET_CONDITION(dwlConditionMask, VER_SUITENAME, VER_OR);
  1459. if (VerifyVersionInfo(&osvix,
  1460. VER_PRODUCT_TYPE | VER_SUITENAME,
  1461. dwlConditionMask)) {
  1462. bIsTSWorkstation = TRUE;
  1463. }
  1464. bVerified = TRUE;
  1465. }
  1466. //
  1467. // Fast user switching (FUS) only applies to the Workstation product where
  1468. // Terminal Services are enabled (i.e. Personal, Professional).
  1469. //
  1470. if (!bIsTSWorkstation) {
  1471. return FALSE;
  1472. }
  1473. //
  1474. // Check if multiple TS sessions are currently allowed. We can't make this
  1475. // info static because it can change dynamically.
  1476. //
  1477. if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  1478. TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon"),
  1479. 0,
  1480. KEY_READ,
  1481. &hKey) != ERROR_SUCCESS) {
  1482. return FALSE;
  1483. }
  1484. ulValue = 0;
  1485. ulSize = sizeof(ulValue);
  1486. bFusEnabled = FALSE;
  1487. if (RegQueryValueEx(hKey,
  1488. TEXT("AllowMultipleTSSessions"),
  1489. NULL,
  1490. NULL,
  1491. (LPBYTE)&ulValue,
  1492. &ulSize) == ERROR_SUCCESS) {
  1493. bFusEnabled = (ulValue != 0);
  1494. }
  1495. RegCloseKey(hKey);
  1496. return bFusEnabled;
  1497. } // IsFastUserSwitchingEnabled
  1498. BOOL
  1499. HotplugPlaySoundThisSession(
  1500. VOID
  1501. )
  1502. /*++
  1503. Routine Description:
  1504. This routine determines whether a sound should be played in the current
  1505. session.
  1506. Arguments:
  1507. None.
  1508. Return Value:
  1509. Returns TRUE if sounds should be played in this session.
  1510. Notes:
  1511. The user-mode plug and play manager (umpnpmgr.dll) implements the following
  1512. behavior for UI dialogs:
  1513. * When Fast User Switching is enabled, only the physical Console session
  1514. is used for UI dialogs.
  1515. * When Fast User Switching is not enabled, only Session 0 is used for UI
  1516. dialogs.
  1517. Since sound events require no user interaction there is no problem with
  1518. multiple sessions responding to these events simultaneously.
  1519. We should *always* play a sound on the physical console when possible, and
  1520. adopt a behavior similar to umpnpmgr for for the non-Fast User Switching
  1521. case, such that session 0 will also play sound events when possible because
  1522. it should be treated somewhat special in the non-FUS case...
  1523. ... BUT, since we disable the service altogether if the session is remote
  1524. and the user doesn't have permission to eject hotplug devices (so we don't
  1525. show the icon), we won't even respond to DBT_DEVNODES_CHANGED events, and
  1526. consequently won't play sound. We could actually turn this on just by
  1527. allowing those events to be processed when the services is disabled, but
  1528. this function is successful. Since the idea of allowing hardware events on
  1529. remote session 0 without FUS is really just for remote management, then it's
  1530. probably ok that we don't play sounds for a user that can't manage hardware.
  1531. --*/
  1532. {
  1533. //
  1534. // Always play sound events on the physical console.
  1535. //
  1536. if (IsConsoleSession()) {
  1537. return TRUE;
  1538. }
  1539. //
  1540. // If fast user switching is not enabled, play sound events on the
  1541. // pseudo-console (Session 0) also.
  1542. //
  1543. if ((IsPseudoConsoleSession()) &&
  1544. (!IsFastUserSwitchingEnabled())) {
  1545. return TRUE;
  1546. }
  1547. //
  1548. // Otherwise, no sound.
  1549. //
  1550. return FALSE;
  1551. } // HotplugPlaySoundThisSession
  1552.