Button: Safety remove drive

This is a script for a Button or Menu to safety remove a drive.
To use this three external programs are required.
In DOpus program folder (which can be changed) create a folder named "Portable". This is the place where I put all portable programs I use a lot in DOpus.
Copy and Past the script into a Button or Menu, navigate into a ejectable drive and press the newly created button to safety remove this drive.

Parts of this script are based on another script which can be found here (unfortunately I don't find it just right now, but someone will find out the url...)

Requirements (Sources are available on the their pages):
EjectMedia.exe and RemoveDrive.exe http://www.uwe-sieber.de
NotifU.exe (Bubble notifier) http://www.paralint.com/projects/notifu/

NOTE: Please use 32Bit versions if you can. Every 32Bit Program runs also on 64Bit System, but not vice versa!




@script JScript

// This example below (the four line only) are working well
// @set driveletter = {sourcepath$|\}
// Go /mycomputer
// /home\Portable\RemoveDrive.exe "{$driveletter}" -h -e -b -a
// /home/Portable\ShowBalloonTip.exe 1 6 "Ejected drive "+{$driveletter } "Ejected drive "+{$driveletter }


function removeDrive(driveletter, command) {
   var wsh = new ActiveXObject("WScript.Shell");
   var fso = new ActiveXObject("Scripting.FileSystemObject");
   if (fso.DriveExists(driveletter + ":")) {
      //var dopusdatapath = DOpus.aliases("dopusdata").path;
	  var dopushome = DOpus.aliases("home").path;
      if (fso.getDrive(driveletter + ":").DriveType == 4) {
         // cdrom
         var result = wsh.Run("\"" + dopushome + "\\Portable\\EjectMedia.exe\" " + driveletter + ":", 0, true);
         if (result == 0) {
            command.RunCommand("\"/home\\Portable\\notifu.exe\" /p \"Media Eject Success (" + driveletter + ":)\" /m \"The media in drive " + driveletter + ": was successfully ejected.\"");
         } else {
            command.RunCommand("\"/home\\Portable\\notifu.exe\" /p \"Media Eject Error (" + driveletter + ":)\" /m \"The media in drive " + driveletter + ": could not be ejected (device probably in use by another application).\"");
         }
      } else {
         // other
         var result = wsh.Run("\"" + dopushome + "\\Portable\\RemoveDrive.exe\" " + driveletter + ":", 0, true);
         if (result == 0) {
            command.RunCommand("\"/home\\Portable\\notifu.exe\" /p \"Hardware Removal Success (" + driveletter + ":)\" /m \"The device that was connected to drive " + driveletter + ": was successfully dismounted.\"");
         } else {
            command.RunCommand("\"/home\\Portable\\notifu.exe\" /p \"Hardware Removal Error (" + driveletter + ":)\" /m \"The device that is connected to drive " + driveletter + ": could not be dismounted (device probably in use by another application).\"");
         }
      }
   } else {
      command.RunCommand("\"/home\\Portable\\notifu.exe\" /p \"Drive Access Error (" + driveletter + ":)\" /m \"The specified drive " + driveletter + ": can not be accessed or does not exist.\"");
   }
}


// removeDrive("D", clickData.func.command);

function OnClick(clickData) {
	found=false;
	for (var eListers = new Enumerator(DOpus.listers); !eListers.atEnd(); eListers.moveNext())
	{
	    for (var eTabs = new Enumerator(eListers.item().tabs); !eTabs.atEnd(); eTabs.moveNext())
	    {
			//A foreground lister is the active Lister Window
			// The source tab is the currently selected tab
			if (eListers.item().foreground==true && eTabs.item().source==true) {
				var sourcepath = eTabs.item().path;
				// The Drive number 1=A 2=B 3=C ...
				var drivenum = String(eTabs.item().path.drive);
				// ASCII 65=@. Thus for example Drive C is drivenum 3+64= ASCII 'C'
	  			var drive = String.fromCharCode(64+eTabs.item().path.drive);
				if (drivenum>0) {
					//clickData.func.command.RunCommand("\"/home\\Portable\\notifu.exe\" /t info /p \"Drive\" /m \"Drive: " + drive + "\"");
					clickData.func.command.RunCommand("Go /mycomputer");
					removeDrive( drive, clickData.func.command) ;
					found=true;
				}
			}
	   }
	}
	if (found==false) {
		clickData.func.command.RunCommand("\"/home\\Portable\\notifu.exe\" /t warn /p \"Drive not found\" /m \"Drive not ejected. Perhaps it is missing a drive letter, UNC Share or FTP-Connection. \"");
	}

}

SafetyRemove.zip (464 KB)

1 Like

To eject a drive, Opus has a built-in command (this example ejects the D drive):

Go D: EJECT

That is often enough to be able to safely unplug the device, unless it is a device with multiple drives (e.g. a card reader) where you may need to eject multiple drives and the Safely Remove option may still be preferable.

The tools and script above are useful for some situations (there's also a RemoveDrive.exe which I've found useful), such as when ejection isn't enough or when something is hanging on to the drive and won't let it be ejected. But if you just want basic ejection, the built-in command is easier and doesn't require any extra programs.

native DOpus script

option explicit

' Remove Drive
' (c) qiuqiu 2015
' 
' This is a script for Directory Opus.
' See http://www.gpsoft.com.au/DScripts/redirect.asp?page=scripts for development information.
' 
' 
' 
' Called by Directory Opus to initialize the script
Const UnknownType = 0
Const Removable = 1
Const Fixed = 2
Const Remote = 3
Const CDRom = 4
Const RamDisk = 5



Function OnInit(initData)
	initData.name = "RemoveDrive"
	initData.desc = "Remove Removable, CDROM Drive"
	initData.copyright = "(c) qiuqiu 2015"
	initData.version = "1.0"
	initData.default_enable = True

	Dim cmd

	Set cmd = initData.AddCommand
	cmd.name = initData.name
	cmd.method = "OnRemoveDrive"
	cmd.desc = initData.desc
	cmd.label = initData.name
	cmd.template = "Removable/S,CDRom/S"
End Function


' Implement the RemoveDrive command
Function OnRemoveDrive(scriptCmdData)
	Dim Dlg, fso, tDrives, tDrive, NoDrive, dChoice, dChecked, Ret, i
	
	Set fso = CreateObject("Scripting.Filesystemobject")
	Set tDrives = fso.Drives
	Set dChoice = DOpus.Create.Vector()
	Set dChecked = DOpus.Create.Vector()
	Set Dlg = scriptCmdData.Func.Dlg
	
	Dlg.Window = scriptCmdData.func.sourcetab.lister
	Dlg.Title = "Remove Drive"
	Dlg.Message = "Selection drive to remove"
	Dlg.buttons = "OK|Cancel"

	For Each tDrive In tDrives
		Select Case tDrive.DriveType
			Case Removable
				If (scriptCmdData.func.args.got_arg.Removable) Then
					dChoice.push_back tDrive.VolumeName & " (" & tDrive.DriveLetter & ":)" 
					dChecked.push_back False
				End If
			Case CDRom
				If (scriptCmdData.func.args.got_arg.CDRom) And tDrive.IsReady Then
					dChoice.push_back tDrive.VolumeName & " (" & tDrive.DriveLetter & ":)" 
					dChecked.push_back False
				End If
		End Select
	Next
	If dChoice.Size > 0 Then
		dlg.choices = dChoice
		dlg.list = dChecked
		Ret = dlg.Show
		If Ret <> 0 Then
			For i = 0 To dlg.choices.size - 1
				If dlg.list(i) Then
					scriptCmdData.Func.Command.RunCommand "GO " & Left(Right(dlg.choices(i), 3), 2) & " EJECT"
				End If
			Next
		End If
	End If


	Set fso = Nothing
	Set tDrives = Nothing
	Set dChoice = Nothing
	Set dChecked = Nothing
	Set Dlg = Nothing
End Function
1 Like

Can a wildcard be used, depending on which drive is selected in the Lister?

I'd say yes, in combination with the @ifpath modifier.

Go {sourcepath|\|noterm} EJECT will eject the current drive, if that's what you want. (Not sure if the noterm is needed but it doesn't hurt. You don't need to use @ifpath for this.)

1 Like

Cheers Leo, much appreciated.