Page 1 of 2

Eject USB drive

Posted: Wed Jan 13, 2010 2:03 am
by donSHAYA
How do you eject a removeable USB drive?

I tried to search and only found this: http://purebasic.fr/english/viewtopic.p ... move+drive

The codes in that link removes all removeable drives. But I only need to remove one specified drive (like D:\). Thank you.

Another question:
So when you just ejcted the specified drive, and the user didn't remove physically the drive (USB) - how can you "install" it back, so it is available?

Thank you

Re: Eject USB drive

Posted: Wed Jan 13, 2010 8:03 am
by Helle
Hello,
You can test the devcon.exe (download from Microsoft):

Code: Select all

;- Switch On-Off USB-Devices
;- "Helle" Klaus Helbing, 13.01.2010, PB v.4.40 x86, WinXP Prof. 32-Bit
;- Use the devcon.exe from Microsoft
;- Adjust the times (Delay(2000)) if necessary!

USBR$ = "USB\ROOT"
Activ$ = "running"
Dis$ = "disabled"

Structure USB
  Name$
  VID$
  PID$
  Status$
EndStructure

If OpenWindow(0, 0, 0, 400, 200, "Switch On-Off USB-Devices (Mouse LeftDoubleClick)", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  ListIconGadget(0, 10, 10, 380, 180, "Geräte-Name", 180, #PB_ListIcon_GridLines | #PB_ListIcon_FullRowSelect)
  AddGadgetColumn(0, 1, "VID", 65)
  AddGadgetColumn(0, 2, "PID", 65)
  AddGadgetColumn(0, 3, "Status", 60)
  ;notice the path of devcon.exe
  ProgID = RunProgram("devcon", "find usb\*", "", #PB_Program_Open | #PB_Program_Read | #PB_Program_Hide)
  Delay(2000)                               ;muss sein! Bei Bedarf höher!
  Laenge = AvailableProgramOutput(ProgID)
  Buffer = AllocateMemory(Laenge)
  ReadProgramData(ProgID, Buffer, Laenge)

  Dim Geraet.USB(Laenge)                    ;gaaaanz sicher! Kann man natürlich verkleinern

  For i = 0 To Laenge - 1
    If PeekB(Buffer + i) = 13
      USB$ = PeekS(Buffer + j, i-j, #PB_Ascii)
      j = i
      If FindString(USB$, USBR$, 1) = 0
        AddGadgetItem(0, -1, "")
        If FindString(USB$, "VID", 1) > 0   ;sonst Käse bei kein Gerät 
          USB$ = RTrim(USB$)                ;zeigt hier auch ausgeschaltete Geräte an!
          Geraet(z)\VID$ = Mid(USB$, 7, 8)  ;8 Zeichen lang, Position ermittelt
          SetGadgetItemText(0, z, Geraet(z)\VID$, 1)
          Geraet(z)\PID$ = Mid(USB$, 16, 8)
          SetGadgetItemText(0, z, Geraet(z)\PID$, 2)
          k = FindString(USB$, ":", 20)     ;oder mehr als 20
          Geraet(z)\Name$ = Mid(USB$, k+2, i-k)  ;oder bei bekannter VID/PID eigenen Namen setzen
          SetGadgetItemText(0, z, Geraet(z)\Name$, 0)   
          ProgID2 = RunProgram("devcon", "status usb\"+Geraet(z)\VID$+Chr(38)+Geraet(z)\PID$, "", #PB_Program_Open | #PB_Program_Read | #PB_Program_Hide)
          Delay(2000)
          Laenge2 = AvailableProgramOutput(ProgID2)
          Buffer2 = AllocateMemory(Laenge2)
          ReadProgramData(ProgID2, Buffer2, Laenge2)
          Act$ = PeekS(Buffer2, Laenge2, #PB_Ascii)
          KillProgram(ProgID2)
          CloseProgram(ProgID2)      
          FreeMemory(Buffer2)
          If FindString(Act$, Activ$, 1) <> 0
            Geraet(z)\Status$ = Activ$
           Else
            Geraet(z)\Status$ = Dis$
          EndIf 
          SetGadgetItemText(0, z, Geraet(z)\Status$, 3)
          z + 1   
          If i > Laenge - 50                ;50 oder so in etwa
            Break
          EndIf
        EndIf 
      EndIf 
    EndIf
  Next 

  KillProgram(ProgID)                       ;sicher ist sicher
  CloseProgram(ProgID)

  Repeat
    Repeat
      Event = WaitWindowEvent()
      If Event = #PB_Event_CloseWindow
        End 
      EndIf   
    Until EventType() = #PB_EventType_LeftDoubleClick 

  z = GetGadgetState(0)

  If z >= 0
    ;Status switchen
    If Geraet(z)\Status$ = Activ$
      If FindString(Geraet(z)\Name$, "USB-HID", 1) = 0     ;Not Keyboard and Mouse - too dangerous!
        ProgID2 = RunProgram("devcon", "disable usb\" + Geraet(z)\VID$ + Chr(38) + Geraet(z)\PID$, "", #PB_Program_Open | #PB_Program_Hide)
      EndIf
     Else 
      ProgID2 = RunProgram("devcon", "enable usb\" + Geraet(z)\VID$ + Chr(38) + Geraet(z)\PID$, "", #PB_Program_Open | #PB_Program_Hide)
    EndIf 

    ;aktuellen Status des geswitchten Gerätes abfragen
    Delay(2000)                                ;evtl. anpassen 
    ProgID3 = RunProgram("devcon", "status usb\"+Geraet(z)\VID$+Chr(38)+Geraet(z)\PID$, "", #PB_Program_Open | #PB_Program_Read | #PB_Program_Hide)
    Delay(2000)                          
    Laenge3 = AvailableProgramOutput(ProgID3)
    Buffer3 = AllocateMemory(Laenge3)
    ReadProgramData(ProgID3, Buffer3, Laenge3)
    Act$ = PeekS(Buffer3, Laenge3, #PB_Ascii)
    KillProgram(ProgID3)
    CloseProgram(ProgID3)      
    FreeMemory(Buffer3)
    If FindString(Act$, Activ$, 1) <> 0
      Geraet(z)\Status$ = Activ$
     Else
      Geraet(z)\Status$ = Dis$
    EndIf 
    SetGadgetItemText(0, z, Geraet(z)\Status$, 3)
  EndIf 
  Until WaitWindowEvent() = #PB_Event_CloseWindow
EndIf 
Have fun!
Helle

P.S.: Don´t change the HID´s!!!
Edit: HID´s not switchable - too dangerous!

Re: Eject USB drive

Posted: Wed Jan 13, 2010 10:17 am
by GeBonet
Hi,

One day I found this code I do not know who :cry: , but he does his work :wink:

Code: Select all

; -------------------------------------
;  Author unknown
; --------------------------------------
;  USB Device remove... !!!
;
#IOCTL_STORAGE_EJECT_MEDIA  = $2D4808
;
Procedure EjectCD(LW.s)
    Protected hLwStatus.l
    hLwStatus = CreateFile_("\\.\"+LW,#GENERIC_READ|#GENERIC_WRITE, 0, 0, #OPEN_EXISTING, 0, 0)
    If hLwStatus
      DeviceIoControl_(hLwStatus,#IOCTL_STORAGE_EJECT_MEDIA,0,0,0,0,@Ret,0)
      CloseHandle_(hLwStatus)
      ProcedureReturn #True 
    EndIf
EndProcedure
;-------------------------------------------------- 
If EjectCD("H:") = #True
     MessageRequester("USB","device can be safely removed.",#MB_ICONINFORMATION)
Else
    MessageRequester("USB","The device can be disconnected !.",#MB_ICONEXCLAMATION)
EndIf

Re: Eject USB drive

Posted: Sun Jan 17, 2010 1:43 pm
by doctorized
GeBonet wrote:One day I found this code I do not know who :cry: , but he does his work :wink:
Your code seems to work fine but after the messagerequester appears saying "device can be safely removed.",
the device is still listed on My Computer (without having removed it). Only when I take the device away, Windows
remove it from the list. I must say that it is not so big deal but I just refer it.

Re: Eject USB drive

Posted: Sun Jan 17, 2010 2:48 pm
by eJan
I've found 'Remove all USBSTORs' - which should be updated to 4.4

Code: Select all

; bingo: http://www.purebasic.fr/english/viewtopic.php?p=135750#135750

#DIGCF_PRESENT = 2
#DIGCF_ALLCLASSES = 4

hDeviceInfoSet = SetupDiGetClassDevs_(0, 0, 0, #DIGCF_PRESENT | #DIGCF_ALLCLASSES)

Structure SP_DEVINFO_DATA
  cbSize.l
  ClassGuid.GUID
  DevInst.l
  Reserved.l
EndStructure

Global DeviceInfoData.SP_DEVINFO_DATA ;global für funktion
DeviceInfoData\cbSize = SizeOf(DeviceInfoData)

#SPDRP_SERVICE = 4

*MemoryID = AllocateMemory(500)

i.l

OpenLibrary(1, "setupapi.dll")

*F1 = GetFunction(1, "SetupDiGetDeviceInstanceIdA")
*F2 = GetFunction(1, "SetupDiGetDeviceRegistryPropertyA")
*F3 = GetFunction(1, "CM_Locate_DevNodeA")
*F4 = GetFunction(1, "CM_Request_Device_Eject_ExA")

While SetupDiEnumDeviceInfo_(hDeviceInfoSet, i, @DeviceInfoData)
  i + 1
  CallFunctionFast(*F2, hDeviceInfoSet, DeviceInfoData, #SPDRP_SERVICE, 0, *MemoryID, 500, 0)

  If UCase(PeekS(*MemoryID)) = "USBSTOR"
    CallFunctionFast(*F1, hDeviceInfoSet, DeviceInfoData, *MemoryID, 500, 0)
    If CallFunctionFast(*F3, @devinst.l, PeekS(*MemoryID), 0) = 0
      CallFunctionFast(*F4, devinst, 0, 0, 0, 0, 0)
    EndIf
  EndIf

Wend

SetupDiDestroyDeviceInfoList_(hDeviceInfoSet)

CloseLibrary(1)

FreeMemory(*MemoryID)

Re: Eject USB drive

Posted: Sun Jan 17, 2010 3:01 pm
by doctorized
eJan wrote:

Code: Select all

If CallFunctionFast(*F3, @devinst.l, PeekS(*MemoryID), 0) = 0
Bad parameter type, number expected instead of string. Try this:

Code: Select all

temp.s = PeekS(*MemoryID)
If CallFunctionFast(*F3, @devinst.l, @temp, 0) = 0
I tried the code with two flash drives. The code removed them both. The point should be the removal of one, user specific, drive.
The bad with this code is parameter i at:

Code: Select all

While SetupDiEnumDeviceInfo_(hDeviceInfoSet, i, @DeviceInfoData)
In first case i was 175 and in the second was 176. I think that there is no easy way to know the handle of the drive just before running the above command.

Re: Eject USB drive

Posted: Sun Jan 17, 2010 4:12 pm
by RASHAD
@doctorized
Will you please check the next snippet?

Code: Select all


#IOCTL_STORAGE_EJECT_MEDIA = $2D4808
#IOCTL_STORAGE_LOAD_MEDIA = $2D480C

Global CtrlCode.l,ReturnedBs.l,Status.l

Procedure EjectUSB(USBpath$,CtrlCode )
   ;obtain a handle to the device
   hDevice = CreateFile_("\\.\"+ USBpath$,#GENERIC_READ,#FILE_SHARE_READ|#FILE_SHARE_WRITE,#Null,#OPEN_EXISTING,0,0)

   If hDevice <> #INVALID_HANDLE_VALUE
      Status = DeviceIoControl_(hDevice,CtrlCode,0,0,0,0,@ReturnedBs,0)
   EndIf
   CloseHandle_(hDevice)
   If Status <> 0
   MessageRequester("","Now it is Safe to Remove the Media")
   EndIf

EndProcedure

For i = 67 To 90
VolumeName$ = Space(14)
Str$ = Space(32)
CDPath$ = Chr(i)+":\"
DriveType = GetDriveType_(CDPath$)
If DriveType = #DRIVE_REMOVABLE
 GetVolumeInformation_(CDPath$,@VolumeName$,Len(VolumeName$),0,0,0,@Str$,Len(Str$))
 Result = MessageRequester("", "Do you want to eject "+ CDPath$ + "  " + VolumeName$, #PB_MessageRequester_YesNo)
     If Result = #PB_MessageRequester_Yes
       EjectUSB(Left(CDPath$,2), #IOCTL_STORAGE_EJECT_MEDIA)
     EndIf
EndIf
Next

Updated

Re: Eject USB drive

Posted: Sun Jan 17, 2010 4:57 pm
by GeBonet
Hello,
doctorized wrote:
GeBonet wrote:One day I found this code I do not know who :cry: , but he does his work :wink:
Your code seems to work fine but after the messagerequester appears saying "device can be safely removed.",
the device is still listed on My Computer (without having removed it). Only when I take the device away, Windows
remove it from the list. I must say that it is not so big deal but I just refer it.
This is normal if the key is still there, the message from Windows warned you disconnected software (writing the buffet on the key if necessary) and only after physically removing it disappears!
I have no problem with this procedure works.

Re: Eject USB drive

Posted: Sun Jan 17, 2010 10:32 pm
by Psychophanta
eJan wrote:I've found 'Remove all USBSTORs' - which should be updated to 4.4
Updated:

Code: Select all

Prototype SetupDiGetDeviceInstanceIdA(a,b,c,d,e)
Prototype SetupDiGetDeviceRegistryPropertyA(a,b,c,d,e,f,g)
Prototype CM_Locate_DevNodeA(a,b.p-ascii,c)
Prototype CM_Request_Device_Eject_ExA(a,b,c,d,e,f)
hDeviceInfoSet=SetupDiGetClassDevs_(0,0,0,#DIGCF_PRESENT|#DIGCF_ALLCLASSES)
Global DeviceInfoData.SP_DEVICE_INTERFACE_DATA ;global für funktion
DeviceInfoData\cbSize=SizeOf(DeviceInfoData)
#SPDRP_SERVICE=4
If OpenLibrary(1,"setupapi.dll")=0:End:EndIf
*MemoryID=AllocateMemory(500)
F1.SetupDiGetDeviceInstanceIdA=GetFunction(1,"SetupDiGetDeviceInstanceIdA")
F2.SetupDiGetDeviceRegistryPropertyA=GetFunction(1,"SetupDiGetDeviceRegistryPropertyA")
F3.CM_Locate_DevNodeA=GetFunction(1,"CM_Locate_DevNodeA")
F4.CM_Request_Device_Eject_ExA=GetFunction(1,"CM_Request_Device_Eject_ExA")
While SetupDiEnumDeviceInfo_(hDeviceInfoSet,i.l,@DeviceInfoData)
  i.l+1
  F2(hDeviceInfoSet,DeviceInfoData,#SPDRP_SERVICE,0,*MemoryID,500,0)
  If UCase(PeekS(*MemoryID))="USBSTOR"
    F1(hDeviceInfoSet,DeviceInfoData,*MemoryID,500,0)
    If F3(@devinst.l,PeekS(*MemoryID,-1,#PB_Ascii),0)=0
      F4(devinst,0,0,0,0,0)
    EndIf
  EndIf
Wend
SetupDiDestroyDeviceInfoList_(hDeviceInfoSet)
CloseLibrary(1)
FreeMemory(*MemoryID)

Re: Eject USB drive

Posted: Sun Jan 17, 2010 10:38 pm
by donSHAYA
I have tried all those codes, none of them works for ejecting one specified device.

Re: Eject USB drive

Posted: Sun Jan 17, 2010 11:04 pm
by Psychophanta
donSHAYA wrote:I have tried all those codes, none of them works for ejecting one specified device.
Yes, that's true. I've tried a little bit , but i am not able to get a relationship between the number 'devinst' reported by CM_Locate_DevNodeA() function and the windows letter. :?

Re: Eject USB drive

Posted: Mon Jan 18, 2010 3:13 pm
by doctorized
RASHAD wrote:@doctorized
Will you please check the next snippet?
Updated
The code is asking for every drive if I want to remove it, but when I select yes, the drive is still in the list.

Re: Eject USB drive

Posted: Mon Jan 18, 2010 3:54 pm
by RASHAD
@doctorized Hi
Thank you very much for responding
Yes it remove the device from Explorer but not from My Computer
I am working now on another approach and I will inform you if I succedded

Thanks again

Re: Eject USB drive

Posted: Tue Jan 19, 2010 10:16 am
by Michael Vogel

Code: Select all

Procedure EjectUSBDrive(Drive.s, EjectNow.l=#True)

	#SPDRP_CAPABILITIES = $F
	#CM_DEVCAP_REMOVABLE  = $4

	DataSection
		GUID_DEVINTERFACE_FLOPPY:
		Data.l $53F56311
		Data.w $B6BF, $11D0
		Data.b $94, $F2, $00, $A0, $C9, $1E, $FB, $8B
		GUID_DEVINTERFACE_DISK:
		Data.l $53F56307
		Data.w $B6BF, $11D0
		Data.b $94, $F2, $00, $A0, $C9, $1E, $FB, $8B
		GUID_DEVINTERFACE_CDROM:
		Data.l $53F56308
		Data.w $B6BF, $11D0
		Data.b $94, $F2, $00, $A0, $C9, $1E, $FB, $8B
		GUID_DEVINTERFACE_VOLUME:
		Data.l  $53F5630D
		Data.w $B6BF, $11D0
		Data.b $94, $F2, $00, $A0, $C9, $1E, $FB, $8B
	EndDataSection

	Structure PSP_DEVICE_INTERFACE_DETAIL_DATA
		cbSize.l
		DevicePath.s{255}
	EndStructure

	Protected Index, Retour, *VolumeName, *DeviceInstanceId
	Protected DevicePath_Length.l, DeviceLetter.s, DeviceVolume.s, DeviceInstance.l, DeviceID.s
	Protected hDevice.l, *Guid.l, pspdidd.PSP_DEVICE_INTERFACE_DETAIL_DATA, spdid.SP_DEVICE_INTERFACE_DATA, spdd.SP_DEVINFO_DATA
	Protected Removable_hDevice, Removable_Index, Removable_spdd.SP_DEVINFO_DATA, Removable_DeviceCapabilities.l, Removable_Service.s, RemovableDevice.l, Removable_DeviceID.s
	Protected DeviceCapabilities.l
	Protected DriveVolume.s

	dll_Setupapi = OpenLibrary(#PB_Any, "setupapi.dll")
	If dll_Setupapi
		dll_Kernel32 = OpenLibrary(#PB_Any, "Kernel32.dll")
		If dll_Kernel32
			*CM_Locate_DevNode = GetFunction(dll_Setupapi, "CM_Locate_DevNodeA")
			*CM_Request_Device_Eject = GetFunction(dll_Setupapi, "CM_Request_Device_Eject_ExA")
			*CM_Get_Parent = GetFunction(dll_Setupapi, "CM_Get_Parent")
			*CM_Get_Device_ID = GetFunction(dll_Setupapi, "CM_Get_Device_IDA")
			*GetVolumeNameForVolumeMountPoint = GetFunction(dll_Kernel32, "GetVolumeNameForVolumeMountPointA")

			*VolumeName = AllocateMemory(255)
			CallFunctionFast(*GetVolumeNameForVolumeMountPoint, Drive + "\", *VolumeName, 255)
			DriveVolume=  PeekS(*VolumeName)
			FreeMemory(*VolumeName)

			*Guid = ?GUID_DEVINTERFACE_VOLUME
			hDevice = SetupDiGetClassDevs_(*Guid, 0, 0, #DIGCF_PRESENT|#DIGCF_DEVICEINTERFACE)

			If hDevice <> #INVALID_HANDLE_VALUE

				spdid\cbSize = SizeOf(SP_DEVICE_INTERFACE_DATA)

				Index= 0
				While SetupDiEnumDeviceInterfaces_(hDevice, 0, *Guid, Index, @spdid)

					SetupDiGetDeviceInterfaceDetail_(hDevice, @spdid, 0, 0, @DevicePath_Length, 0)
					If DevicePath_Length <> 0 And DevicePath_Length < 255

						pspdidd\cbSize = 5
						spdd\cbSize = SizeOf(SP_DEVINFO_DATA)

						If SetupDiGetDeviceInterfaceDetail_(hDevice, @spdid, @pspdidd, DevicePath_Length, 0, @spdd)

							*VolumeName = AllocateMemory(255)
							CallFunctionFast(*GetVolumeNameForVolumeMountPoint, pspdidd\DevicePath + "\", *VolumeName, 255)
							DeviceVolume =  PeekS(*VolumeName)
							FreeMemory(*VolumeName)

							If DriveVolume = DeviceVolume

								DeviceInstance = spdd\DevInst
								Repeat
									If CallFunctionFast(*CM_Get_Parent, @DeviceParent, DeviceInstance, 0) = 0
										DeviceInstance = DeviceParent
										*DeviceInstanceId = AllocateMemory(255)
										CallFunctionFast(*CM_Get_Device_ID, DeviceInstance, *DeviceInstanceId, 255, 0)
										DeviceID = PeekS(*DeviceInstanceId)
										FreeMemory(*DeviceInstanceId)

										RemovableDevice = 0
										Removable_hDevice = SetupDiGetClassDevs_(0, 0, 0, #DIGCF_PRESENT | #DIGCF_ALLCLASSES)
										If Removable_hDevice <> #INVALID_HANDLE_VALUE
											Removable_spdd\cbSize = SizeOf(SP_DEVINFO_DATA)
											Removable_Index = 0
											While SetupDiEnumDeviceInfo_(Removable_hDevice, Removable_Index, @Removable_spdd)
												*DeviceInstanceId = AllocateMemory(255)
												SetupDiGetDeviceInstanceId_(Removable_hDevice, Removable_spdd, *DeviceInstanceId, 255, 0)
												Removable_DeviceID = PeekS(*DeviceInstanceId)
												FreeMemory(*DeviceInstanceId)
												If Removable_DeviceID = DeviceID
													SetupDiGetDeviceRegistryProperty_(Removable_hDevice, Removable_spdd, #SPDRP_CAPABILITIES, 0, @DeviceCapabilities, 4, 0)
													If DeviceCapabilities & #CM_DEVCAP_REMOVABLE
														RemovableDevice = 1
														Break
													EndIf
												EndIf
												Removable_Index + 1
											Wend
											SetupDiDestroyDeviceInfoList_(Removable_hDevice)
										EndIf

										If RemovableDevice
											Retour = 1
											If EjectNow

												If CallFunctionFast(*CM_Locate_DevNode, @DeviceInstance.l, DeviceID, 0) = 0
													CallFunctionFast(*CM_Request_Device_Eject, DeviceInstance, 0, 0, 0, 0, 0)
												EndIf

											EndIf
											Break 2
										EndIf

									EndIf
								Until DeviceInstance <> DeviceParent

							EndIf
						EndIf
					EndIf

					Index + 1
				Wend

				SetupDiDestroyDeviceInfoList_(hDevice)
			EndIf
			CloseLibrary(dll_Kernel32)
		EndIf
		CloseLibrary(dll_Setupapi)
	EndIf

	ProcedureReturn Retour
EndProcedure

Re: Eject USB drive

Posted: Tue Jan 19, 2010 1:29 pm
by doctorized
@ RASHAD: No need to thank me. I am always here to help!

@ Michael Vogel: your code returns error: "Line 32: Structure not found: SP_DEVINFO_DATA."