hplug.c
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 37k
Category:

Windows Kernel

Development Platform:

Visual C++

  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. #define HPLUG_EJECT_EVENT       TEXT("HPlugEjectEvent")
  22. //
  23. // setupapi private exports
  24. //
  25. DWORD
  26. pSetupGuidFromString(
  27.   PWCHAR GuidString,
  28.   LPGUID Guid
  29.   );
  30. typedef struct _HotPlugDevices {
  31.      struct _HotPlugDevices *Next;
  32.      DEVINST DevInst;
  33.      DWORD   DevNodeStatus;
  34.      DWORD   Capabilities;
  35.      WORD    EjectMenuIndex;
  36.      BOOLEAN PendingEvent;
  37.      GUID    ClassGuid;
  38.      PTCHAR  DevName;
  39.      TCHAR   ClassGuidString[MAX_GUID_STRING_LEN];
  40.      TCHAR   DevInstanceId[1];
  41. } HOTPLUGDEVICES, *PHOTPLUGDEVICES;
  42. CONFIGRET InitialConfigRet = CR_SUCCESS;
  43. BOOL HotPlugInitialized = FALSE;
  44. BOOL ShowShellIcon = FALSE;
  45. HICON HotPlugIcon = NULL;
  46. BOOL ServiceEnabled = FALSE;
  47. HANDLE hEjectEvent = NULL;          // Event to if we are in the process of ejecting a device
  48. extern HINSTANCE g_hInstance;       //  Global instance handle 4 this application.
  49. BOOL
  50. IsHotPlugDevice(
  51.     DEVINST DevInst,
  52.     HMACHINE hMachine
  53.     )
  54. /**+
  55.     A device is considered a HotPlug device if the following are TRUE:
  56.         - Does NOT have problem CM_PROB_DEVICE_NOT_THERE
  57.         - has Capability CM_DEVCAP_REMOVABLE
  58.         - does NOT have Capability CM_DEVCAP_SURPRISEREMOVALOK
  59.         - does NOT have Capability CM_DEVCAP_DOCKDEVICE
  60.         
  61. Returns:
  62.     TRUE if this is a HotPlug device
  63.     FALSE if this is not a HotPlug device.        
  64. -**/
  65. {
  66.     DWORD Capabilities;
  67.     DWORD Len;
  68.     DWORD Status, Problem;
  69.     Capabilities = Status = Problem = 0;
  70.     Len = sizeof(Capabilities);
  71.     if ((CM_Get_DevNode_Registry_Property_Ex(DevInst,
  72.                                              CM_DRP_CAPABILITIES,
  73.                                              NULL,
  74.                                              (PVOID)&Capabilities,
  75.                                              &Len,
  76.                                              0,
  77.                                              hMachine) == CR_SUCCESS) &&
  78.         (CM_Get_DevNode_Status_Ex(&Status,
  79.                                   &Problem,
  80.                                   DevInst,
  81.                                   0,
  82.                                   hMachine) == CR_SUCCESS) &&
  83.         ((CM_PROB_DEVICE_NOT_THERE != Problem) &&
  84.          (Capabilities & CM_DEVCAP_REMOVABLE) &&
  85.          !(Capabilities & CM_DEVCAP_SURPRISEREMOVALOK) &&
  86.          !(Capabilities & CM_DEVCAP_DOCKDEVICE))) {
  87.         return TRUE;
  88.     }
  89.     return FALSE;
  90. }
  91. LPTSTR
  92. DevNodeToDriveLetter(
  93.     DEVINST DevInst
  94.     )
  95. {
  96.     BOOL Result = FALSE;
  97.     ULONG ulSize;
  98.     TCHAR DeviceID[MAX_DEVICE_ID_LEN];
  99.     LPTSTR DriveName = NULL;
  100.     LPTSTR DeviceInterface = NULL;
  101.     if (CM_Get_Device_ID_Ex(DevInst,
  102.                             DeviceID,
  103.                             sizeof(DeviceID)/sizeof(TCHAR),
  104.                             0,
  105.                             NULL
  106.                             ) != CR_SUCCESS) {
  107.         return FALSE;
  108.     }
  109.     
  110.     ulSize = 0;
  111.     if ((CM_Get_Device_Interface_List_Size(&ulSize,
  112.                                            (LPGUID)&VolumeClassGuid,
  113.                                            DeviceID,
  114.                                            0)  == CR_SUCCESS) &&
  115.         (ulSize > 1) &&
  116.         ((DeviceInterface = LocalAlloc(LPTR, ulSize*sizeof(TCHAR))) != NULL) &&
  117.         (CM_Get_Device_Interface_List((LPGUID)&VolumeClassGuid,
  118.                                       DeviceID,
  119.                                       DeviceInterface,
  120.                                       ulSize,
  121.                                       0
  122.                                       )  == CR_SUCCESS) &&
  123.         *DeviceInterface)
  124.     {
  125.         LPTSTR devicePath, p;
  126.         TCHAR thisVolumeName[MAX_PATH];
  127.         TCHAR enumVolumeName[MAX_PATH];
  128.         TCHAR driveName[4];
  129.         ULONG length;
  130.         BOOL bResult;
  131.         length = lstrlen(DeviceInterface);
  132.         devicePath = LocalAlloc(LPTR, (length + 1) * sizeof(TCHAR) + sizeof(UNICODE_NULL));
  133.         if (devicePath) {
  134.             lstrcpyn(devicePath, DeviceInterface, length + 1);
  135.             p = wcschr(&(devicePath[4]), TEXT('\'));
  136.             if (!p) {
  137.                 //
  138.                 // No refstring is present in the symbolic link; add a trailing
  139.                 // '' char (as required by GetVolumeNameForVolumeMountPoint).
  140.                 //
  141.                 p = devicePath + length;
  142.                 *p = TEXT('\');
  143.             }
  144.             p++;
  145.             *p = UNICODE_NULL;
  146.             thisVolumeName[0] = UNICODE_NULL;
  147.             bResult = GetVolumeNameForVolumeMountPoint(devicePath,
  148.                                                        thisVolumeName,
  149.                                                        MAX_PATH
  150.                                                        );
  151.             LocalFree(devicePath);
  152.             if (bResult && thisVolumeName[0]) {
  153.                 driveName[1] = TEXT(':');
  154.                 driveName[2] = TEXT('\');
  155.                 driveName[3] = TEXT('');
  156.                 for (driveName[0] = TEXT('A'); driveName[0] <= TEXT('Z'); driveName[0]++) {
  157.                     enumVolumeName[0] = TEXT('');
  158.                     GetVolumeNameForVolumeMountPoint(driveName, enumVolumeName, MAX_PATH);
  159.                     if (!lstrcmpi(thisVolumeName, enumVolumeName)) {
  160.                         driveName[2] = TEXT('');
  161.                         DriveName = LocalAlloc(LPTR, (lstrlen(driveName) + 1) * sizeof(TCHAR));
  162.                         
  163.                         if (DriveName) {
  164.                             lstrcpy(DriveName, driveName);
  165.                         }
  166.                         
  167.                         break;
  168.                     }
  169.                 }
  170.             }
  171.         }
  172.     }
  173.     if (DeviceInterface) {
  174.         LocalFree(DeviceInterface);
  175.     }
  176.     
  177.     return DriveName;
  178. }
  179. int
  180. CollectRelationDriveLetters(
  181.     DEVINST DevInst,
  182.     LPTSTR ListOfDrives,
  183.     HMACHINE hMachine
  184.     )
  185. /*++
  186.     This function looks at the removal relations of the specified DevInst and adds any drive
  187.     letters associated with these removal relations to the ListOfDrives.
  188.     
  189. Return:
  190.     Number of drive letters added to the list.    
  191. --*/
  192. {
  193.     int NumberOfDrives = 0;
  194.     LPTSTR SingleDrive = NULL;
  195.     TCHAR szSeparator[32];
  196.     DEVINST RelationDevInst;
  197.     TCHAR DeviceInstanceId[MAX_DEVICE_ID_LEN];
  198.     ULONG Len;
  199.     PTCHAR DeviceIdRelations, CurrDevId;
  200.     if (CM_Get_Device_ID_Ex(DevInst,
  201.                             DeviceInstanceId,
  202.                             sizeof(DeviceInstanceId) * sizeof(TCHAR),
  203.                             0,
  204.                             hMachine) == CR_SUCCESS) {
  205.         Len = 0;
  206.         if ((CM_Get_Device_ID_List_Size_Ex(&Len,
  207.                                            DeviceInstanceId,
  208.                                            CM_GETIDLIST_FILTER_REMOVALRELATIONS,
  209.                                            hMachine) == CR_SUCCESS) &&
  210.             (Len)) {
  211.             DeviceIdRelations = LocalAlloc(LPTR, Len*sizeof(TCHAR));
  212.             *DeviceIdRelations = TEXT('');
  213.             if (DeviceIdRelations) {
  214.                 if ((CM_Get_Device_ID_List_Ex(DeviceInstanceId,
  215.                                               DeviceIdRelations,
  216.                                               Len,
  217.                                               CM_GETIDLIST_FILTER_REMOVALRELATIONS,
  218.                                               hMachine) == CR_SUCCESS) &&
  219.                     (*DeviceIdRelations)) {
  220.                     for (CurrDevId = DeviceIdRelations; *CurrDevId; CurrDevId += lstrlen(CurrDevId) + 1) {
  221.                         if (CM_Locate_DevNode_Ex(&RelationDevInst, CurrDevId, 0, hMachine) == CR_SUCCESS) {
  222.                             SingleDrive = DevNodeToDriveLetter(RelationDevInst);
  223.                             if (SingleDrive) {
  224.                                 NumberOfDrives++;
  225.                                 //
  226.                                 // If this is not the first drive the add a comma space separator
  227.                                 //
  228.                                 if (ListOfDrives[0] != TEXT('')) {
  229.                                     LoadString(g_hInstance, IDS_SEPARATOR, szSeparator, sizeof(szSeparator)/sizeof(TCHAR));
  230.                                     lstrcat(ListOfDrives, szSeparator);
  231.                                 } 
  232.                                 lstrcat(ListOfDrives, SingleDrive);
  233.                                 LocalFree(SingleDrive);
  234.                             }
  235.                         }
  236.                     }
  237.                 }
  238.                 LocalFree(DeviceIdRelations);
  239.             }
  240.         }
  241.     }
  242.     return NumberOfDrives;
  243. }
  244. int
  245. CollectDriveLettersForDevNodeWorker(
  246.     DEVINST DevInst,
  247.     LPTSTR ListOfDrives,
  248.     HMACHINE hMachine
  249.     )
  250. {
  251.     DEVINST ChildDevInst;
  252.     DEVINST SiblingDevInst;
  253.     int NumberOfDrives = 0;
  254.     LPTSTR SingleDrive = NULL;
  255.     TCHAR szSeparator[32];
  256.     //
  257.     // Enumerate through all of the siblings and children of this devnode
  258.     //
  259.     do {
  260.         ChildDevInst = 0;
  261.         SiblingDevInst = 0;
  262.         CM_Get_Child_Ex(&ChildDevInst, DevInst, 0, hMachine);
  263.         CM_Get_Sibling_Ex(&SiblingDevInst, DevInst, 0, hMachine);
  264.         //
  265.         // Only get the drive letter for this device if it is NOT a hotplug
  266.         // device.  If it is a hotplug device then it will have it's own
  267.         // subtree that contains it's drive letters.
  268.         //
  269.         if (!IsHotPlugDevice(DevInst, hMachine)) {
  270.         
  271.             SingleDrive = DevNodeToDriveLetter(DevInst);
  272.     
  273.             if (SingleDrive) {
  274.     
  275.                 NumberOfDrives++;
  276.                 
  277.                 //
  278.                 // If this is not the first drive the add a comma space separator
  279.                 //
  280.                 if (ListOfDrives[0] != TEXT('')) {
  281.                     LoadString(g_hInstance, IDS_SEPARATOR, szSeparator, sizeof(szSeparator)/sizeof(TCHAR));
  282.     
  283.                     lstrcat(ListOfDrives, szSeparator);
  284.                 } 
  285.     
  286.                 lstrcat(ListOfDrives, SingleDrive);
  287.     
  288.                 LocalFree(SingleDrive);
  289.             }
  290.     
  291.             //
  292.             // Get the drive letters for any children of this devnode
  293.             //
  294.             if (ChildDevInst) {
  295.     
  296.                 NumberOfDrives += CollectDriveLettersForDevNodeWorker(ChildDevInst, ListOfDrives, hMachine);
  297.             }
  298.             //
  299.             // Add the drive letters for any removal relations of this devnode
  300.             //
  301.             NumberOfDrives += CollectRelationDriveLetters(DevInst, ListOfDrives, hMachine);
  302.         }
  303.     } while ((DevInst = SiblingDevInst) != 0);
  304.     return NumberOfDrives;
  305. }
  306. LPTSTR
  307. CollectDriveLettersForDevNode(
  308.     DEVINST DevInst,
  309.     HMACHINE hMachine
  310.     )
  311. {
  312.     TCHAR Format[MAX_PATH];
  313.     TCHAR ListOfDrives[MAX_PATH];
  314.     DEVINST ChildDevInst;
  315.     int NumberOfDrives = 0;
  316.     LPTSTR SingleDrive = NULL;
  317.     LPTSTR FinalDriveString = NULL;
  318.     ListOfDrives[0] = TEXT('');
  319.     //
  320.     //First get any drive letter associated with this devnode
  321.     //
  322.     SingleDrive = DevNodeToDriveLetter(DevInst);
  323.     
  324.     if (SingleDrive) {
  325.         NumberOfDrives++;
  326.         lstrcat(ListOfDrives, SingleDrive);
  327.         LocalFree(SingleDrive);
  328.     }
  329.     //
  330.     // Next add on any drive letters associated with the children
  331.     // of this devnode
  332.     //
  333.     ChildDevInst = 0;
  334.     CM_Get_Child_Ex(&ChildDevInst, DevInst, 0, hMachine);
  335.     if (ChildDevInst) {
  336.     
  337.         NumberOfDrives += CollectDriveLettersForDevNodeWorker(ChildDevInst, ListOfDrives, hMachine);
  338.     }
  339.     //
  340.     // Finally add on any drive letters associated with the removal relations
  341.     // of this devnode
  342.     //
  343.     NumberOfDrives += CollectRelationDriveLetters(DevInst, ListOfDrives, hMachine);
  344.     
  345.     if (ListOfDrives[0] != TEXT('')) {
  346.         LoadString(g_hInstance, 
  347.                    (NumberOfDrives > 1) ? IDS_DISKDRIVES : IDS_DISKDRIVE,
  348.                    Format,
  349.                    sizeof(Format)/sizeof(TCHAR)
  350.                    );
  351.         FinalDriveString = LocalAlloc(LPTR, (lstrlen(ListOfDrives) + lstrlen(Format) + 1) * sizeof(TCHAR));
  352.         if (FinalDriveString) {
  353.         
  354.             wsprintf(FinalDriveString, Format, ListOfDrives);
  355.         }
  356.     }
  357.     return FinalDriveString;
  358. }
  359. DWORD
  360. GetHotPlugFlags(
  361.    void
  362.    )
  363. {
  364.     HKEY hKey;
  365.     LONG Error;
  366.     DWORD HotPlugFlags, cbHotPlugFlags;
  367.     Error = RegCreateKey(HKEY_CURRENT_USER, REGSTR_PATH_SYSTRAY, &hKey);
  368.     
  369.     if (Error == ERROR_SUCCESS) {
  370.         cbHotPlugFlags = sizeof(HotPlugFlags);
  371.         Error = RegQueryValueEx(hKey,
  372.                                 TEXT("HotPlugFlags"),
  373.                                 NULL,
  374.                                 NULL,
  375.                                 (PVOID)&HotPlugFlags,
  376.                                 &cbHotPlugFlags
  377.                                 );
  378.         RegCloseKey(hKey);
  379.         if (Error == ERROR_SUCCESS) {
  380.             
  381.             return HotPlugFlags;
  382.         }
  383.     }
  384.     return 0;
  385. }
  386. ULONG
  387. RegistryDeviceName(
  388.     DEVINST DevInst,
  389.     PTCHAR  Buffer,
  390.     DWORD   cbBuffer
  391.     )
  392. {
  393.     ULONG ulSize = 0;
  394.     CONFIGRET ConfigRet;
  395.     LPTSTR ListOfDrives = NULL;
  396.     //
  397.     // Get the list of drives
  398.     //
  399.     ListOfDrives = CollectDriveLettersForDevNode(DevInst, NULL);
  400.     //
  401.     // Try the registry for FRIENDLYNAME
  402.     //
  403.     ulSize = cbBuffer;
  404.     *Buffer = TEXT('');
  405.     ConfigRet = CM_Get_DevNode_Registry_Property(DevInst,
  406.                                                  CM_DRP_FRIENDLYNAME,
  407.                                                  NULL,
  408.                                                  Buffer,
  409.                                                  &ulSize,
  410.                                                  0);
  411.     if (ConfigRet != CR_SUCCESS || !(*Buffer)) {
  412.         
  413.         //
  414.         // Try the registry for DEVICEDESC
  415.         //
  416.     
  417.         ulSize = cbBuffer;
  418.         *Buffer = TEXT('');
  419.         ConfigRet = CM_Get_DevNode_Registry_Property(DevInst,
  420.                                                      CM_DRP_DEVICEDESC,
  421.                                                      NULL,
  422.                                                      Buffer,
  423.                                                      &ulSize,
  424.                                                      0);
  425.     
  426.         if (ConfigRet != CR_SUCCESS || !(*Buffer)) {
  427.             
  428.         
  429.             //
  430.             // try classname
  431.             //
  432.         
  433.             ulSize = cbBuffer;
  434.             *Buffer = TEXT('');
  435.             ConfigRet = CM_Get_DevNode_Registry_Property(DevInst,
  436.                                                          CM_DRP_CLASS,
  437.                                                          NULL,
  438.                                                          Buffer,
  439.                                                          &ulSize,
  440.                                                          0);
  441.         
  442.             if (ConfigRet != CR_SUCCESS || !(*Buffer)) {
  443.                 
  444.                 ulSize = 0;;
  445.             }
  446.         }
  447.     }
  448.     //
  449.     // Concatonate on the list of drive letters if this device has drive 
  450.     // letters and there is enough space
  451.     //
  452.     if (ListOfDrives) {
  453.         if ((ulSize + (lstrlen(ListOfDrives) * sizeof(TCHAR))) < cbBuffer) {
  454.             lstrcat(Buffer, ListOfDrives);
  455.             ulSize += (lstrlen(ListOfDrives) * sizeof(TCHAR));
  456.         }
  457.         LocalFree(ListOfDrives);
  458.     } 
  459.             
  460.     return ulSize;
  461. }
  462. BOOL
  463. _AnyHotPlugDevices(
  464.     DEVINST      DeviceInstance
  465.     )
  466. {
  467.     ULONG      Len;
  468.     DWORD      Capabilities, Status, Problem;
  469.     CONFIGRET  ConfigRet;
  470.     DEVINST    ChildDeviceInstance;
  471.     do {
  472.         //
  473.         // Device capabilities
  474.         //
  475.         Len = sizeof(Capabilities);
  476.         ConfigRet = CM_Get_DevNode_Registry_Property(DeviceInstance,
  477.                                                      CM_DRP_CAPABILITIES,
  478.                                                      NULL,
  479.                                                      (PVOID)&Capabilities,
  480.                                                      &Len,
  481.                                                      0
  482.                                                      );
  483.         if (ConfigRet != CR_SUCCESS) {
  484.             
  485.             Capabilities = 0;
  486.         }
  487.         ConfigRet = CM_Get_DevNode_Status(&Status,
  488.                                           &Problem,
  489.                                           DeviceInstance,
  490.                                           0
  491.                                           );
  492.         if (ConfigRet != CR_SUCCESS) {
  493.             Status = 0;
  494.             Problem = 0;
  495.         }
  496.         //
  497.         // Only show devices that:
  498.         //  -> do NOT have problem CM_PROB_DEVICE_NOT_THERE
  499.         //  -> are removable
  500.         //  -> are NOT surprise removable
  501.         //  -> are NOT dock devices
  502.         //
  503.         if ((Problem != CM_PROB_DEVICE_NOT_THERE) &&
  504.             (Capabilities & CM_DEVCAP_REMOVABLE) &&
  505.             !(Capabilities & CM_DEVCAP_SURPRISEREMOVALOK) &&
  506.             !(Capabilities & CM_DEVCAP_DOCKDEVICE)) {
  507.             //
  508.             // As soon as we find one device then we need to show the icon, so no need
  509.             // to continue the search.
  510.             //
  511.             return TRUE;
  512.         }
  513.         //
  514.         // If this devinst has children, then recurse to fill in its
  515.         // child sibling list.
  516.         //
  517.         ConfigRet = CM_Get_Child(&ChildDeviceInstance,
  518.                                  DeviceInstance,
  519.                                  0
  520.                                  );
  521.         if (ConfigRet == CR_SUCCESS) {
  522.             
  523.             if (_AnyHotPlugDevices(ChildDeviceInstance)) {
  524.                 
  525.                 return TRUE;
  526.             }
  527.         }
  528.         //
  529.         // Next sibling ...
  530.         //
  531.         ConfigRet = CM_Get_Sibling(&DeviceInstance,
  532.                                       DeviceInstance,
  533.                                       0
  534.                                       );
  535.     } while (ConfigRet == CR_SUCCESS);
  536.     return FALSE;
  537. }
  538. BOOL
  539. AnyHotPlugDevices(
  540.     void
  541.     )
  542. {
  543.     CONFIGRET ConfigRet;
  544.     DEVNODE DeviceInstance, ChildDeviceInstance;
  545.     ConfigRet = CM_Locate_DevNode(&DeviceInstance,
  546.                                  NULL,
  547.                                  CM_LOCATE_DEVNODE_NORMAL
  548.                                  );
  549.     
  550.     InitialConfigRet = ConfigRet;
  551.     
  552.     if (ConfigRet != CR_SUCCESS) {
  553.         
  554.         return FALSE;
  555.     }
  556.     ConfigRet = CM_Get_Child(&ChildDeviceInstance,
  557.                              DeviceInstance,
  558.                              0
  559.                              );
  560.     if (ConfigRet != CR_SUCCESS) {
  561.         
  562.         return FALSE;
  563.     }
  564.     return(_AnyHotPlugDevices(ChildDeviceInstance));
  565. }
  566. BOOL
  567. _AddHotPlugDevices(
  568.     DEVINST      DeviceInstance,
  569.     PHOTPLUGDEVICES *HotPlugDevicesList
  570.     )
  571. {
  572.     PHOTPLUGDEVICES HotPlugDevice;
  573.     DWORD      Len, LenDevName, LenDevInstanceId;
  574.     DWORD      Capabilities;
  575.     CONFIGRET  ConfigRet;
  576.     ULONG      Problem, DevNodeStatus;
  577.     DEVINST    ChildDeviceInstance;
  578.     TCHAR      DevInstanceId[MAX_DEVICE_ID_LEN];
  579.     TCHAR      DevName[MAX_PATH];
  580.     do {
  581.         //
  582.         // Device capabilities
  583.         //
  584.         Len = sizeof(Capabilities);
  585.         ConfigRet = CM_Get_DevNode_Registry_Property(DeviceInstance,
  586.                                                      CM_DRP_CAPABILITIES,
  587.                                                      NULL,
  588.                                                      (PVOID)&Capabilities,
  589.                                                      &Len,
  590.                                                      0
  591.                                                      );
  592.         if (ConfigRet != CR_SUCCESS) {
  593.             
  594.             Capabilities = 0;
  595.         }
  596.         ConfigRet = CM_Get_DevNode_Status(&DevNodeStatus,
  597.                                           &Problem,
  598.                                           DeviceInstance,
  599.                                           0
  600.                                           );
  601.         if (ConfigRet != CR_SUCCESS) {
  602.             
  603.             DevNodeStatus = 0;
  604.             Problem = 0;
  605.         }
  606.         //
  607.         // Only show devices that:
  608.         //  -> do NOT have problem CM_PROB_DEVICE_NOT_THERE
  609.         //  -> are removable
  610.         //  -> are NOT surprise removable
  611.         //  -> are NOT dock devices
  612.         //
  613.         if ((Problem != CM_PROB_DEVICE_NOT_THERE) &&
  614.             (Capabilities & CM_DEVCAP_REMOVABLE) &&
  615.             !(Capabilities & CM_DEVCAP_SURPRISEREMOVALOK) &&
  616.             !(Capabilities & CM_DEVCAP_DOCKDEVICE)) {
  617.             *DevInstanceId = TEXT('');
  618.             LenDevInstanceId = sizeof(DevInstanceId);
  619.             ConfigRet = CM_Get_Device_ID(DeviceInstance,
  620.                                          (PVOID)DevInstanceId,
  621.                                          LenDevInstanceId,
  622.                                          0
  623.                                          );
  624.             if (ConfigRet != CR_SUCCESS || !*DevInstanceId) {
  625.                 
  626.                 *DevInstanceId = TEXT('');
  627.                 LenDevInstanceId = 0;
  628.             }
  629.             Len = sizeof(HOTPLUGDEVICES) + LenDevInstanceId;
  630.             HotPlugDevice = LocalAlloc(LPTR, Len);
  631.             
  632.             if (!HotPlugDevice) {
  633.                 
  634.                 return FALSE;
  635.             }
  636.             //
  637.             // link it in
  638.             //
  639.             HotPlugDevice->Next = *HotPlugDevicesList;
  640.             *HotPlugDevicesList = HotPlugDevice;
  641.             HotPlugDevice->DevInst = DeviceInstance;
  642.             HotPlugDevice->DevNodeStatus = DevNodeStatus;
  643.             HotPlugDevice->Capabilities = Capabilities;
  644.             //
  645.             // Fetch the ClassGuid
  646.             //
  647.             Len = sizeof(HotPlugDevice->ClassGuidString);
  648.             ConfigRet = CM_Get_DevNode_Registry_Property_Ex(DeviceInstance,
  649.                                                             CM_DRP_CLASSGUID,
  650.                                                             NULL,
  651.                                                             HotPlugDevice->ClassGuidString,
  652.                                                             &Len,
  653.                                                             0,
  654.                                                             0
  655.                                                             );
  656.             if (ConfigRet != CR_SUCCESS) {
  657.                 
  658.                 *HotPlugDevice->ClassGuidString = TEXT('');
  659.             }
  660.             //
  661.             // copy in the names
  662.             //
  663.             memcpy(HotPlugDevice->DevInstanceId, DevInstanceId, LenDevInstanceId);
  664.             LenDevName = RegistryDeviceName(DeviceInstance, DevName, sizeof(DevName));
  665.             HotPlugDevice->DevName = LocalAlloc(LPTR, LenDevName + sizeof(TCHAR));
  666.             
  667.             if (HotPlugDevice->DevName) {
  668.                 
  669.                 memcpy(HotPlugDevice->DevName, DevName, LenDevName);
  670.             }
  671.         }
  672.         //
  673.         // If this devinst has children, then recurse to fill in its
  674.         // child sibling list.
  675.         //
  676.         ConfigRet = CM_Get_Child(&ChildDeviceInstance,
  677.                                  DeviceInstance,
  678.                                  0
  679.                                  );
  680.         if (ConfigRet == CR_SUCCESS) {
  681.             
  682.             if (!_AddHotPlugDevices(ChildDeviceInstance, HotPlugDevicesList)) {
  683.                 
  684.                 return FALSE;
  685.             }
  686.         }
  687.         //
  688.         // Next sibling ...
  689.         //
  690.         ConfigRet = CM_Get_Sibling(&DeviceInstance,
  691.                                       DeviceInstance,
  692.                                       0
  693.                                       );
  694.     } while (ConfigRet == CR_SUCCESS);
  695.     return TRUE;
  696. }
  697. BOOL
  698. AddHotPlugDevices(
  699.     PHOTPLUGDEVICES *HotPlugDevicesList
  700.     )
  701. {
  702.     CONFIGRET ConfigRet;
  703.     DEVNODE DeviceInstance, ChildDeviceInstance;
  704.     *HotPlugDevicesList = NULL;
  705.     ConfigRet = CM_Locate_DevNode(&DeviceInstance,
  706.                                  NULL,
  707.                                  CM_LOCATE_DEVNODE_NORMAL
  708.                                  );
  709.     
  710.     InitialConfigRet = ConfigRet;
  711.     
  712.     if (ConfigRet != CR_SUCCESS) {
  713.         
  714.         return FALSE;
  715.     }
  716.     ConfigRet = CM_Get_Child(&ChildDeviceInstance,
  717.                              DeviceInstance,
  718.                              0
  719.                              );
  720.     if (ConfigRet != CR_SUCCESS) {
  721.         
  722.         return FALSE;
  723.     }
  724.     return (_AddHotPlugDevices(ChildDeviceInstance, HotPlugDevicesList));
  725. }
  726. void
  727. FreeHotPlugDevicesList(
  728.     PHOTPLUGDEVICES *HotPlugDevicesList
  729.     )
  730. {
  731.     PHOTPLUGDEVICES HotPlugDevices, HotPlugDevicesFree;
  732.     HotPlugDevices = *HotPlugDevicesList;
  733.     *HotPlugDevicesList = NULL;
  734.     while (HotPlugDevices) {
  735.         HotPlugDevicesFree = HotPlugDevices;
  736.         HotPlugDevices = HotPlugDevicesFree->Next;
  737.         
  738.         if (HotPlugDevicesFree->DevName) {
  739.            
  740.            LocalFree(HotPlugDevicesFree->DevName);
  741.            HotPlugDevicesFree->DevName = NULL;
  742.         }
  743.         LocalFree(HotPlugDevicesFree);
  744.     }
  745. }
  746. /*
  747.  *  Shows or deletes the shell notify icon and tip
  748.  */
  749. void
  750. HotPlugShowNotifyIcon(
  751.     HWND hWnd,
  752.     BOOL bShowIcon
  753.     )
  754. {
  755.     TCHAR HotPlugTip[64];
  756.     ShowShellIcon = bShowIcon;
  757.     if (bShowIcon) {
  758.         
  759.         LoadString(g_hInstance,
  760.                    IDS_HOTPLUGTIP,
  761.                    HotPlugTip,
  762.                    sizeof(HotPlugTip)/sizeof(TCHAR)
  763.                    );
  764.         HotPlugIcon = LoadImage(g_hInstance,
  765.                                 MAKEINTRESOURCE(IDI_HOTPLUG),
  766.                                 IMAGE_ICON,
  767.                                 16,
  768.                                 16,
  769.                                 0
  770.                                 );
  771.         SysTray_NotifyIcon(hWnd, STWM_NOTIFYHOTPLUG, NIM_ADD, HotPlugIcon, HotPlugTip);
  772.     }
  773.     
  774.     else {
  775.         
  776.         SysTray_NotifyIcon(hWnd, STWM_NOTIFYHOTPLUG, NIM_DELETE, NULL, NULL);
  777.         
  778.         if (HotPlugIcon) {
  779.             
  780.             DestroyIcon(HotPlugIcon);
  781.         }
  782.     }
  783. }
  784. //
  785. // first time intialization of Hotplug module.
  786. //
  787. BOOL
  788. HotPlugInit(
  789.     HWND hWnd
  790.     )
  791. {
  792.     DEV_BROADCAST_DEVICEINTERFACE dbi;
  793.     CONFIGRET ConfigRet;
  794.     INT WaitStatus;
  795.     HINSTANCE hUsbWatch;
  796.     FARPROC proc;
  797.     
  798.     //
  799.     // If we are already initialized then just return whether there are any HotPlug
  800.     // devices or not.
  801.     //
  802.     if (HotPlugInitialized) {
  803.         
  804.         return (AnyHotPlugDevices());
  805.     }
  806.     
  807.     //
  808.     // Load the Usb Watch utility which pops up whenever there's an error on the 
  809.     // Universal Serial Bus
  810.     //
  811.     hUsbWatch = LoadLibrary(TEXT("usbuidll.dll"));
  812.     
  813.     if (hUsbWatch) {
  814.         
  815.         proc = GetProcAddress( hUsbWatch, "USBErrorMessagesInit" );
  816.         
  817.         if (proc) {
  818.             proc ();
  819.         }
  820.     }
  821.     hEjectEvent = CreateEvent(NULL, TRUE, TRUE, HPLUG_EJECT_EVENT);
  822.     
  823.     HotPlugInitialized = TRUE;
  824.     return (AnyHotPlugDevices());
  825. }
  826. BOOL
  827. HotPlug_CheckEnable(
  828.     HWND hWnd,
  829.     BOOL bSvcEnabled
  830.     )
  831. /*++
  832. Routine Description:
  833.    Called at init time and whenever services are enabled/disabled.
  834.    Hotplug is always alive to receive device change notifications.
  835.    The shell notify icon is enableddisabled depending on:
  836.    - systray registry setting for services,
  837.         AND
  838.    - availability of removable devices.
  839. Arguments:
  840.    hwnd - Our Window handle
  841.    bSvcEnabled - TRUE Service is being enabled.
  842. Return Value:
  843.    BOOL Returns TRUE if active.
  844. --*/
  845. {
  846.     BOOL EnableShellIcon;
  847.     //
  848.     // If we are being enabled and we are already enabled, or we
  849.     // are being disabled and we are already disabled then just
  850.     // return since we have nothing to do.
  851.     //
  852.     if (ServiceEnabled == bSvcEnabled) {
  853.         return ServiceEnabled;
  854.     }
  855.     ServiceEnabled = bSvcEnabled;
  856.     EnableShellIcon = bSvcEnabled && HotPlugInit(hWnd);
  857.     HotPlugShowNotifyIcon(hWnd, EnableShellIcon);
  858.     return EnableShellIcon;
  859. }
  860. DWORD
  861. HotPlugEjectDevice_Thread(
  862.    PTCHAR DeviceInstanceId
  863.    )
  864. {
  865.     DEVNODE DevNode;
  866.     CONFIGRET ConfigRet;
  867.     if ((ConfigRet = CM_Locate_DevNode(&DevNode,
  868.                                        DeviceInstanceId,
  869.                                        0)) == CR_SUCCESS) {
  870.         ConfigRet = CM_Request_Device_Eject_Ex(DevNode,
  871.                                                NULL,
  872.                                                NULL,
  873.                                                0,
  874.                                                0,
  875.                                                NULL);
  876.     }
  877.     //
  878.     // Set the hEjectEvent so that the right-click popup menu will work again now that we are finished
  879.     // ejecting/stopping the device.
  880.     //
  881.     SetEvent(hEjectEvent);
  882.     SetLastError(ConfigRet);
  883.     return (ConfigRet == CR_SUCCESS);
  884. }
  885. void
  886. HotPlugEjectDevice(
  887.     HWND hwnd,
  888.     PTCHAR DeviceInstanceId
  889.     )
  890. {
  891.     DWORD ThreadId;
  892.     //
  893.     // Reset the hEjectEvent so that the user can't bring up the right-click popup menu when
  894.     // we are in the process of ejecting/stopping a device.
  895.     //
  896.     ResetEvent(hEjectEvent);
  897.     //
  898.     // We need to have stobject.dll eject/stop the device on a separate thread because if
  899.     // we remove a device that stobject.dll listens for (battery, sound, ect.) we will cause
  900.     // a large delay and the eject/stop could end up getting vetoed because the stobject.dll
  901.     // code could not be processed and release it's handles because we were locking up the main
  902.     // thread.
  903.     //
  904.     CreateThread(NULL,
  905.                  0,
  906.                  (LPTHREAD_START_ROUTINE)HotPlugEjectDevice_Thread,
  907.                  (LPVOID)DeviceInstanceId,
  908.                  0,
  909.                  &ThreadId
  910.                  );
  911. }
  912. void
  913. HotPlug_Timer(
  914.    HWND hwnd
  915.    )
  916. /*++
  917. Routine Description:
  918.    Hotplug Timer msg handler, used to invoke EjectMenu for single Left click
  919. Arguments:
  920.    hDlg - Our Window handle
  921. Return Value:
  922.    BOOL Returns TRUE if active.
  923. --*/
  924. {
  925.     POINT pt;
  926.     HMENU EjectMenu;
  927.     UINT MenuIndex;
  928.     PHOTPLUGDEVICES HotPlugDevicesList;
  929.     PHOTPLUGDEVICES SingleHotPlugDevice;
  930.     TCHAR  MenuDeviceName[MAX_PATH+64];
  931.     TCHAR  Format[64];
  932.     KillTimer(hwnd, HOTPLUG_TIMER_ID);
  933.     if (!HotPlugInitialized) {
  934.         PostMessage(hwnd, STWM_ENABLESERVICE, 0, TRUE);
  935.         return;
  936.     }
  937.     //
  938.     // We only want to create the popup menu if the hEjectEvent is signaled.  If it is not
  939.     // signaled then we are in the middle of ejecting/stopping a device on a separate thread
  940.     // and don't want to allow the user to bring up the menu until we are finished with that 
  941.     // device.
  942.     //
  943.     if (!hEjectEvent ||
  944.         WaitForSingleObject(hEjectEvent, 0) == WAIT_OBJECT_0) {
  945.     
  946.         //
  947.         // We are not in the middle of ejecting/stopping a device so we should display the popup
  948.         // menu.
  949.         //
  950.         EjectMenu = CreatePopupMenu();
  951.        
  952.         if (!EjectMenu) {
  953.     
  954.             return;
  955.         }
  956.     
  957.         SetForegroundWindow(hwnd);
  958.         GetCursorPos(&pt);
  959.     
  960.     
  961.         //
  962.         // Add each of the removable devices in the list to the menu.
  963.         //
  964.     
  965.         if (!AddHotPlugDevices(&HotPlugDevicesList)) {
  966.     
  967.             return;
  968.         }
  969.     
  970.         SingleHotPlugDevice = HotPlugDevicesList;
  971.     
  972.     
  973.         //
  974.         // Add a title and separator at the top of the menu.
  975.         //
  976.     
  977.         LoadString(g_hInstance,
  978.                    IDS_HPLUGMENU_REMOVE,
  979.                    Format,
  980.                    sizeof(Format)/sizeof(TCHAR)
  981.                    );
  982.     
  983.     
  984.         MenuIndex = 1;
  985.        
  986.         while (SingleHotPlugDevice) {
  987.            
  988.             wsprintf(MenuDeviceName, Format, SingleHotPlugDevice->DevName);
  989.             AppendMenu(EjectMenu, MF_STRING, MenuIndex, MenuDeviceName);
  990.             SingleHotPlugDevice->EjectMenuIndex = MenuIndex++;
  991.             SingleHotPlugDevice = SingleHotPlugDevice->Next;
  992.         }
  993.     
  994.     
  995.         MenuIndex = TrackPopupMenu(EjectMenu,
  996.                                    TPM_LEFTBUTTON | TPM_RETURNCMD | TPM_NONOTIFY,
  997.                                    pt.x,
  998.                                    pt.y,
  999.                                    0,
  1000.                                    hwnd,
  1001.                                    NULL
  1002.                                    );
  1003.     
  1004.     
  1005.         // now do an eject!
  1006.     
  1007.         SingleHotPlugDevice = HotPlugDevicesList;
  1008.         
  1009.         while (SingleHotPlugDevice) {
  1010.            
  1011.             if (MenuIndex == SingleHotPlugDevice->EjectMenuIndex) {
  1012.                
  1013.                 HotPlugEjectDevice(hwnd, SingleHotPlugDevice->DevInstanceId);
  1014.                 break;
  1015.             }
  1016.     
  1017.             SingleHotPlugDevice = SingleHotPlugDevice->Next;
  1018.         }
  1019.     
  1020.     
  1021.         DestroyMenu(EjectMenu);
  1022.     
  1023.         if (!SingleHotPlugDevice) {
  1024.     
  1025.             SetIconFocus(hwnd, STWM_NOTIFYHOTPLUG);
  1026.         }
  1027.     
  1028.         FreeHotPlugDevicesList(&HotPlugDevicesList);
  1029.     }
  1030.     
  1031.     return;
  1032. }
  1033. void
  1034. HotPlugContextMenu(
  1035.    HWND hwnd
  1036.    )
  1037. {
  1038.    POINT pt;
  1039.    HMENU ContextMenu;
  1040.    UINT MenuIndex;
  1041.    TCHAR Buffer[MAX_PATH];
  1042.    ContextMenu = CreatePopupMenu();
  1043.    if (!ContextMenu) {
  1044.        return;
  1045.        }
  1046.    SetForegroundWindow(hwnd);
  1047.    GetCursorPos(&pt);
  1048.    LoadString(g_hInstance, IDS_HPLUGMENU_PROPERTIES, Buffer, sizeof(Buffer)/sizeof(TCHAR));
  1049.    AppendMenu(ContextMenu, MF_STRING,IDS_HPLUGMENU_PROPERTIES, Buffer);
  1050.    SetMenuDefaultItem(ContextMenu, IDS_HPLUGMENU_PROPERTIES, FALSE);
  1051.    MenuIndex = TrackPopupMenu(ContextMenu,
  1052.                               TPM_RIGHTBUTTON | TPM_RETURNCMD | TPM_NONOTIFY,
  1053.                               pt.x,
  1054.                               pt.y,
  1055.                               0,
  1056.                               hwnd,
  1057.                               NULL
  1058.                               );
  1059.    switch (MenuIndex) {
  1060.        case IDS_HPLUGMENU_PROPERTIES:
  1061.             SysTray_RunProperties(IDS_RUNHPLUGPROPERTIES);
  1062.             break;
  1063.        }
  1064.    DestroyMenu(ContextMenu);
  1065.    SetIconFocus(hwnd, STWM_NOTIFYHOTPLUG);
  1066.    return;
  1067. }
  1068. void
  1069. HotPlug_Notify(
  1070.    HWND hwnd,
  1071.    WPARAM wParam,
  1072.    LPARAM lParam
  1073.    )
  1074. {
  1075.     switch (lParam) {
  1076.        
  1077.     case WM_RBUTTONUP:
  1078.         HotPlugContextMenu(hwnd);
  1079.         break;
  1080.     case WM_LBUTTONDOWN:
  1081.         SetTimer(hwnd, HOTPLUG_TIMER_ID, GetDoubleClickTime()+100, NULL);
  1082.         break;
  1083.     case WM_LBUTTONDBLCLK:
  1084.         KillTimer(hwnd, HOTPLUG_TIMER_ID);
  1085.         SysTray_RunProperties(IDS_RUNHPLUGPROPERTIES);
  1086.         break;
  1087.     }
  1088.     return;
  1089. }
  1090. int
  1091. HotPlug_DeviceChangeTimer(
  1092.    HWND hDlg
  1093.    )
  1094. {
  1095.     BOOL bAnyHotPlugDevices;
  1096.     KillTimer(hDlg, HOTPLUG_DEVICECHANGE_TIMERID);
  1097.     //
  1098.     // Let's see if we have any hot plug devices, which means we
  1099.     // need to show the systray icon.
  1100.     //
  1101.     bAnyHotPlugDevices = AnyHotPlugDevices();
  1102.     //
  1103.     // If the service is not enabled then don't bother because the icon will
  1104.     // NOT be shown
  1105.     //
  1106.     if (ServiceEnabled) {
  1107.         //
  1108.         // If there are now some hot plug devices and the icon is not being shown,
  1109.         // then show it.
  1110.         //
  1111.         if (bAnyHotPlugDevices && !ShowShellIcon) {
  1112.         
  1113.             HotPlugShowNotifyIcon(hDlg, TRUE);
  1114.         }
  1115.         //
  1116.         // If there are NOT any hot plug devices and the icon is still being shown,
  1117.         // then hide it.
  1118.         //
  1119.         else if (!bAnyHotPlugDevices && ShowShellIcon) {
  1120.             HotPlugShowNotifyIcon(hDlg, FALSE);
  1121.         }
  1122.     }
  1123.     return 0;
  1124. }
  1125. void
  1126. HotPlug_DeviceChange(
  1127.    HWND hwnd,
  1128.    WPARAM wParam,
  1129.    LPARAM lParam
  1130.    )
  1131. /*++
  1132. Routine Description:
  1133.    To avoid deadlock with CM, a timer is started and the timer message handler does 
  1134.    the real work.
  1135. Arguments:
  1136.    hDlg        - Window handle of Dialog
  1137.    wParam  - DBT Event
  1138.    lParam  - DBT event notification type.
  1139. Return Value:
  1140. --*/
  1141. {
  1142.     if (wParam == DBT_DEVNODES_CHANGED) {
  1143.         SetTimer(hwnd, HOTPLUG_DEVICECHANGE_TIMERID, 1000, NULL);
  1144.     }
  1145.     return;
  1146. }
  1147. void
  1148. HotPlug_WmDestroy(
  1149.     HWND hWnd
  1150.     )
  1151. {
  1152.     if (hEjectEvent) {
  1153.         CloseHandle(hEjectEvent);
  1154.     }
  1155. }