Link to home
Start Free TrialLog in
Avatar of matthew1471
matthew1471

asked on

Safely Remove Hardware

I have just spent a good hour or two googling for this and have returned varying results with varying words

I want to run whatever API the "Safely Remove Hardware" uses to remove devices... I know there is always "rundll32.exe  shell32.dll,Control_RunDLL hotplug.dll" and then I could use SendKeys.. I'll accept that as an answer if someone can maybe get that to work (removing a device using Sendkeys)

Top marks goes to whoever can somehow get me to perform the exact equivallant of "Safely Remove Hardware" using Visual Basic 6

I have a flash drive, I want to remove it so I can safely unplug it.

Many google results returned people asking the same question

Keywords Tried : Device Removal, API, "Safely Remove Hardware", Hotplug.dll, Eject Device, Unplug Device, Unplug Hardware, Flash Drive, Remove Device, Stop Device, shell32.dll,Control_RunDLL hotplug.dll, API Spy, API Viewer, devcon.exe, Programmatically Safely Remove Hardware, Remove Hardware from batch file ... etc etc
Avatar of 2Angel
2Angel

Avatar of matthew1471

ASKER

Hey 2Angel, Thanks for offering your assistance :)

Unfortunatly the first link only ejects a CD or device with data marked as removable, for a pen drive this doesnt work... it doesnt mark the hardware as now removable..

The second link requires me to know the code to send to the device...and it isn't really a device control to remove itself Im imagining, but I may be wrong

the close handle is way beyond my depth (aka I dont understand it).. It doesn't look useable but I may be mistaken

apparantly what im looking for is something like devcon.exe but that cant be redistributed and to aquire the source code to it I have to be an MSDN subscriber with access to the DDK (Driver Development Kit) very posh for something so simple :P

I tried using an API Spy, rather unsuccessfully though, is there maybe some way of easily working out what HotPlug.dll calls on? I know it interfaces with APIs I think..

Sorry to be a pain, this is asked about so much on the internet

Hi,

Well, I hope this is what you are looking for (because this is seems to be very simple so I am not sure this is what you looked for). Please place on a form a command button "Command1" and paste this code:

Private Sub Command1_Click()

    Shell "RUNDLL32.EXE shell32.dll,Control_RunDLL hotplug.dll"
    SendKeys "{DEL}"
   
End Sub


The first line is bringing up the "Safely Remove Hardware" window (using the HotPulg.dll).
The secund line is sending the key "Delete" to bring up the "Stop a Hardware Device" window to select the device to be stop from that window list.
Now you can add lines to send keys to select the device and "Press" OK (AltO).

Hope this is it.....

Good luck and have fun!
2Angel
Say I had 3 devices in there, how would It know which one to kill? I kind of wanted a way through APIs to hide it from the user.. (As HotPlug is just an interface for device manager)

Sorry if your not sure what im looking for, I'm not very good at explaining things... basically I have a PenDrive, I want to make a little manager for it, on the manager I want a button "Unload" when you click that button it then makes it "Safe" to just unplug the drive... If you "just unplug" when it's accessing data or you have it set in a certain way unsafely unplugging it can cause a blue screen.. I want the program to safely remove it without having to keep manually doing it from the tray.. I know HotPlug.dll is a helper app for DeviceManager, the process should be possible, but I don't know enough about device programming nor API spying to be able to find out how to do it..

There are many posts on Google asking to do this exact same thing (Marking a device safe for removal in windows) "Programmatically Using "Safely Remove Hardware" to remove a device"

Hi,
I have 3 Q:
1. What is your OS?
2. What is the name or/and the letter of the device?
3. Is it 3rd on the list of "Stop a Hardware Device"?

I'll try to help you with 2 diferent approach....



Best regards
1. Windows XP Professional SP2
2. It Varies but it's currently F:\
3. It is third, the card reader has gone first and the external HDD has gone second...

The HotPlug.dll must be calling some APIs maybe the "SetupAPI" but I can't for the life of me find out what or how (Most of Micrsofts DDK is based on C# programming anyway which I don't understand)
Hi,

I am sorry for time past I hadn't got the time...
I wanted to go for a different approach but again the time...

I thought to read the content of the window "Stop a Hardware Device" and then search the text for your device, determin in which line it is and then to point this line ( by SendKey "{DOWN}" ) and clikc the OK button.

Second thought I had, is to use the "Device Manager" window and to disable the device....

So, for now I have for you a "SendKey" solution that will work for you and maybe you will devlope a different approach.

Private Sub Command1_Click()

    Shell "RUNDLL32.EXE shell32.dll,Control_RunDLL hotplug.dll"
    SendKeys "{DEL}", True
    SendKeys "{TAB 2}", True
    SendKeys "{DOWN 3}", True
    SendKeys "{ENTER}", True

End Sub

Thats working for sure and stops the 3rd device....


Ones again I'm sorry and if I will have the time for it I will send to you additional solution.


Have fun & luck!
Hi there sorry for the time taken to reply, i'm still testing your method, but i'm having a job getting it to fully work..

1. There might not be more than 3 devices there might be more
2. It wont for the life of it accept and escape or a ALT & F4 command with either waits on or off...

However I found this VERY interesting, (http://www.webtropy.com/articles/dll-api.aspx?dll=hotplug)

[   ] HOTPLUG.DLL

     Import  Ordinal      Hint         Function                         Entry Point
     ------  -----------  -----------  -------------------------------  -----------

     Export  Ordinal      Hint         Function                         Entry Point
     ------  -----------  -----------  -------------------------------  -----------
     [C  ]    1 (0x0001)   0 (0x0000)  CPlApplet                        0x0000534D
     [C  ]    2 (0x0002)   1 (0x0001)  CreateLocalServerW               0x000026A6
     [C  ]   12 (0x000C)   2 (0x0002)  DllCanUnloadNow                  0x000025BF
     [C  ]   13 (0x000D)   3 (0x0003)  DllGetClassObject                0x000025CB
     [C  ]   14 (0x000E)   4 (0x0004)  DllRegisterServer                0x000025E4
     [C  ]   15 (0x000F)   5 (0x0005)  DllUnregisterServer              0x000025F3
     [C  ]    3 (0x0003)   6 (0x0006)  HotPlugDeviceTree                0x00004FD0
     [C  ]    4 (0x0004)   7 (0x0007)  HotPlugDriverBlockedW            0x000052C1
     [C  ]    5 (0x0005)   8 (0x0008)  HotPlugEjectDevice               0x0000504B
     [C  ]    6 (0x0006)   9 (0x0009)  HotPlugEjectVetoedW              0x00005410
     [C  ]    7 (0x0007)  10 (0x000A)  HotPlugHibernateVetoedW          0x0000542C
     [C  ]    8 (0x0008)  11 (0x000B)  HotPlugRemovalVetoedW            0x00005402
     [C  ]    9 (0x0009)  12 (0x000C)  HotPlugSafeRemovalNotificationW  0x00005141
     [C  ]   10 (0x000A)  13 (0x000D)  HotPlugStandbyVetoedW            0x0000541E
     [C  ]   11 (0x000B)  14 (0x000E)  HotPlugWarmEjectVetoedW          0x0000543A



***************************| Module Dependency Tree |*********
*                                                                              *
* Legend: F  Forwarded Module   ?  Missing Module        6  64-bit Module      *
*         D  Delay Load Module  !  Invalid Module                              *
*         *  Dynamic Module     E  Import/Export Mismatch or Load Failure      *
*                               ^  Duplicate Module                            *
*                                                                              *
*         O  Ordinal Function   E  Import/Export Error   F  Forwarded Function *
*         C  C Function         R  Called At Least Once  *  Dynamic Function   *
*         +  C++ Function                                                      *
*                                                                              *
********************************************************

can someone PLEASE tell me how to call the "HotPlugEjectDevice" API in its correct syntax
The solution provided only works if I have exactly 3 devices (or however many page downs I sent) This is not useful in a production enviroment (or any other enviroment where I may have a different number of USB devices plugged in)

I was hoping for a solution whereby the keys would be sent to the program and it would shut down that exact device, the solution provided is more of a manual way...

Could an administrator please advise me on what to do? Should I accept one or class it as a problem which is not yet fully solved (Seen as it wont work in any cases where It doesnt go to the bottom of the list)?
quote:
>>>>I could use SendKeys.. I'll accept that as an answer if someone can maybe get that to work (removing a device using Sendkeys)

well, it's worknig - but somtimes there are things that can't be perfect...
I accept it does work, but if I add another device to the system it refuses to work.. if I use someone elses' computer to run the pen drive program it will refuse to work...

However it does work provided I know the order in the list it is at which is why I hope to award you points for your effort :)

I hope this has not caused bad feeling and that you understand that I am (although not having found the perfect solution) I am very greatful you submitted your comments..

Is there maybe a way of getting the program to "Read off" the screen as such, e.g. in the window "Safely Remove Hardware", locate the text "USB Mass Storage Device" click it and hit enter? This would then be our solution

Matt
ASKER CERTIFIED SOLUTION
Avatar of 2Angel
2Angel

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Sorry for the delay, I looked at the above and it looks impressive :) but understanding it is another problem :P

Thanks for your help anyway, I feel so bad not using your solution but don't feel disheartened, hundreds of users on the internet have asked this question and virtually no1 has a valid answer, I was taking a peek at some C# source code (I can't program a single thing in C# but was willing to maybe try and hack up a simple EXE to do it)

However the C# wouldnt compile because I havn't spent a few grand and bought the Microsoft Drivers Developer kit..

Thank you !

Good luck....

Dear Matthew:

If it's ok with you to use VB, I found the perfect solution(!). Here it is:

Place the following code into the general declarations area of a bas module:

Option Explicit

Public Const IOCTL_STORAGE_EJECT_MEDIA As Long = &H2D4808
Public Const IOCTL_STORAGE_LOAD_MEDIA As Long = &H2D480C
Public Const DRIVE_REMOVABLE = 2
Public Const DRIVE_CDROM = 5
Public Const INVALID_HANDLE_VALUE As Long = -1&
Public Const GENERIC_READ As Long = &H80000000
Public Const FILE_SHARE_READ As Long = &H1
Public Const FILE_SHARE_WRITE As Long = &H2
Public Const FILE_ANY_ACCESS As Long = &H0
Public Const FILE_READ_ACCESS  As Long = &H1
Public Const FILE_WRITE_ACCESS As Long = &H2
Public Const OPEN_EXISTING As Long = 3
Public Const IOCTL_STORAGE_MEDIA_REMOVAL As Long = &H2D4804

Public Type PREVENT_MEDIA_REMOVAL
   PreventMediaRemoval As Byte
End Type

Public Declare Function GetLogicalDriveStrings Lib "kernel32" _
   Alias "GetLogicalDriveStringsA" _
  (ByVal nBufferLength As Long, _
   ByVal lpBuffer As String) As Long
 
Public Declare Function GetDriveType Lib "kernel32" _
   Alias "GetDriveTypeA" _
  (ByVal lpRootPathName As String) As Long
 
Public Declare Function DeviceIoControl Lib "kernel32" _
  (ByVal hDevice As Long, _
   ByVal dwIoControlCode As Long, _
   lpInBuffer As Any, _
   ByVal nInBufferSize As Long, _
   lpOutBuffer As Any, _
   ByVal nOutBufferSize As Long, _
   lpBytesReturned As Long, _
   lpOverlapped As Any) As Long

Public Declare Function CreateFile Lib "kernel32" _
   Alias "CreateFileA" _
  (ByVal lpFileName As String, _
   ByVal dwDesiredAccess As Long, _
   ByVal dwShareMode As Long, _
   lpSecurityAttributes As Any, _
   ByVal dwCreationDisposition As Long, _
   ByVal dwFlagsAndAttributes As Long, _
   ByVal hTemplateFile As Long) As Long

Public Declare Function CloseHandle Lib "kernel32" _
   (ByVal hObject As Long) As Long

Public Function DeviceLock(sDrive As String, fLock As Boolean) As Boolean

   Dim hDevice As Long
   Dim PMR As PREVENT_MEDIA_REMOVAL
   Dim bytesReturned As Long
   Dim success As Long
 
  'the drive letter has to be passed
  'to CreateFile without a trailing slash (ie 'G:')
   sDrive = UnQualifyPath(sDrive)
   
  'obtain a handle to the device
  'using the correct device syntax
   hDevice = CreateFile("\\.\" & sDrive, _
                        GENERIC_READ, _
                        FILE_SHARE_READ Or FILE_SHARE_WRITE, _
                        ByVal 0&, _
                        OPEN_EXISTING, _
                        0&, 0&)

   
   If hDevice <> INVALID_HANDLE_VALUE Then

     'assign the fLock value to the
     'PREVENT_MEDIA_REMOVAL type
      PMR.PreventMediaRemoval = CByte(Abs(fLock))
 
     'If the operation succeeds,
     'DeviceIoControl returns a nonzero value
      success = DeviceIoControl(hDevice, _
                                IOCTL_STORAGE_MEDIA_REMOVAL, _
                                PMR, _
                                Len(PMR), _
                                ByVal 0&, _
                                0&, _
                                bytesReturned, _
                                ByVal 0&)
   
   End If
                       
   Call CloseHandle(hDevice)
   DeviceLock = success
 
End Function


Private Function UnQualifyPath(ByVal sPath As String) As String

  'removes any trailing slash from the path
   sPath = Trim$(sPath)
   
   If Right$(sPath, 1) = "\" Then
      UnQualifyPath = Left$(sPath, Len(sPath) - 1)
   Else
      UnQualifyPath = sPath
   End If
   
End Function


Public Function DeviceSetMedia(sDrive As String, ctrlCode As Long) As Boolean

   Dim hDevice As Long
   Dim bytesReturned As Long
   Dim success As Long
 
  'the drive letter has to be passed
  'without a trailing slash (ie 'G:')
   sDrive = UnQualifyPath(sDrive)
   
  'obtain a handle to the device
   hDevice = CreateFile("\\.\" & sDrive, _
                        GENERIC_READ, _
                        FILE_SHARE_READ Or FILE_SHARE_WRITE, _
                        ByVal 0&, _
                        OPEN_EXISTING, _
                        0&, 0&)
   
   If hDevice <> INVALID_HANDLE_VALUE Then
 
     'If the operation succeeds,
     'DeviceIoControl returns zero
      success = DeviceIoControl(hDevice, _
                                ctrlCode, _
                                0&, _
                                0&, _
                                ByVal 0&, _
                                0&, _
                                bytesReturned, _
                                ByVal 0&) = 0

   End If
   
   Call CloseHandle(hDevice)
   DeviceSetMedia = success

End Function


Add a listbox (List1), a command button array (Command1(0), Command1(1)), and a label (Label1) to a form. A third button (End) is optional. add two additional command buttons in a control array (Command2(0), Command2(1)).
Add the following code to the form:

Option Explicit

Private Sub Form_Load()

  'load the removable drives and
  'disable the command buttons until
  'a drive is selected
   LoadAvailableDrives List1
   Command1(0).Enabled = False
   Command1(1).Enabled = False
   
End Sub


Private Sub List1_Click()

   Command1(0).Enabled = List1.ListIndex > -1
   Command1(1).Enabled = List1.ListIndex > -1
   
End Sub


Private Sub Command1_Click(Index As Integer)

   Dim fLock As Boolean
   Dim result As Boolean
   Dim sDrive As String
   
  'nothing to do if a drive's not selected
   If List1.ListIndex > -1 Then
   
     'get the selected drive
      sDrive = List1.List(List1.ListIndex)
   
     'DeviceIoControl requires the
     'IOCTL_STORAGE_MEDIA_REMOVAL
     'control code and a Boolean
     'indicating the lock state.
     'Passing False unlocks the device;
     'passing True locks it. This handily
     'corresponds to the 0/1 indices of
     'the Command button array.
      fLock = CBool(Index)
      result = DeviceLock(sDrive, fLock)
         
     'display result
      If result Then
     
         Select Case Index
            Case 0: Label1.Caption = "Device " & sDrive & " is unlocked."
            Case 1: Label1.Caption = "Device " & sDrive & " is locked."
         End Select
         
      Else
         Label1.Caption = "Call failed - perhaps no media in device."
      End If
   
   End If
   
End Sub


Private Sub LoadAvailableDrives(lst As ListBox)

   Dim lpBuffer As String
   Dim drvType As Long
   Dim currDrive As String

  'get list of available drives
   lpBuffer = GetDriveString()

  'Separate the drive strings
  'and add to the combo. StripNulls
  'will continually shorten the
  'string. Loop until a single
  'remaining terminating null is
  'encountered.
   Do Until lpBuffer = Chr(0)
 
  'strip off one drive item
  'and check for removable (or CD) status,
  'and add to the combo
   currDrive = StripNulls(lpBuffer)
   drvType = GetDriveType(currDrive)
   
   If (drvType = DRIVE_CDROM) Or _
      (drvType = DRIVE_REMOVABLE) Then
     
      lst.AddItem currDrive
     
   End If
   
   Loop
 
End Sub


Private Function StripNulls(startstr As String) As String

 'Take a string separated by chr$(0)
 'and split off 1 item, shortening the
 'string so next item is ready for removal.
  Dim pos As Long

  pos = InStr(startstr, Chr$(0))
 
  If pos Then
     
      StripNulls = Mid$(startstr, 1, pos - 1)
      startstr = Mid$(startstr, pos + 1, Len(startstr))
   
  End If

End Function


Private Function GetDriveString() As String

  'returns of available drives each
  'separated by a null
   Dim sBuffer As String
   
  'possible 26 drives, three characters each, plus trailing null
   sBuffer = Space$(26 * 4)
 
  If GetLogicalDriveStrings(Len(sBuffer), sBuffer) Then

     'trim string but do not
     'remove trailing null
      GetDriveString = Trim$(sBuffer)
     
   End If

End Function


Private Sub Command2_Click(Index As Integer)

   Dim sDrive As String

   If List1.ListIndex > -1 Then
   
      sDrive = List1.List(List1.ListIndex)

      Select Case Index
     
         Case 0: Call DeviceSetMedia(sDrive, IOCTL_STORAGE_EJECT_MEDIA)
         Case 1: Call DeviceSetMedia(sDrive, IOCTL_STORAGE_LOAD_MEDIA)
         
      End Select
   
   End If
   
End Sub




Works perfect for me...........               Let me know

Have fun
Angel
Hi,

Did you checked the above solution?

Tks
"Call failed - perhaps no media in device." when I try to lock the pendrive,

The program you provided is excellent in locking and unlocking devices so media can't be removed, but it doesnt actually remove them

Thanks anyway though for your suggestion :)
Hi,

It's working fine for me.
I don't have a pendrive but for all other devices I do have it's "doing the job"...



Well,
BeyBey