21

I was looking for a short way to eject USB-devices via C#-code, so I coded a little class myself, yet it simply doesn't work. Since there's no popup that says "Lock success!" I assume that the problem relies within the "LockVolume"-function, but I don't know where.

Does anybody see the mistake I made?

class USBEject
{
    [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    private static extern IntPtr CreateFile(
         string lpFileName,
         uint dwDesiredAccess,
         uint dwShareMode,
         IntPtr SecurityAttributes,
         uint dwCreationDisposition,
         uint dwFlagsAndAttributes,
         IntPtr hTemplateFile
    );

    [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)]
    private static extern bool DeviceIoControl(
        IntPtr hDevice, 
        uint dwIoControlCode,
        IntPtr lpInBuffer, 
        uint nInBufferSize,
        IntPtr lpOutBuffer, 
        uint nOutBufferSize,
        out uint lpBytesReturned, 
        IntPtr lpOverlapped
    );

    [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)]
    private static extern bool DeviceIoControl(
        IntPtr hDevice, 
        uint dwIoControlCode,
        byte[] lpInBuffer, 
        uint nInBufferSize,
        IntPtr lpOutBuffer, 
        uint nOutBufferSize,
        out uint lpBytesReturned, 
        IntPtr lpOverlapped
    );

    [DllImport("kernel32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool CloseHandle(IntPtr hObject);

    private IntPtr handle = IntPtr.Zero;

    const int GENERIC_READ = 0x80000000;
    const int GENERIC_WRITE = 0x40000000;
    const int FILE_SHARE_READ = 0x1;
    const int FILE_SHARE_WRITE = 0x2;
    const int FSCTL_LOCK_VOLUME = 0x00090018;
    const int FSCTL_DISMOUNT_VOLUME = 0x00090020;
    const int IOCTL_STORAGE_EJECT_MEDIA = 0x2D4808;
    const int IOCTL_STORAGE_MEDIA_REMOVAL = 0x002D4804;

    /// <summary>
    /// Constructor for the USBEject class
    /// </summary>
    /// <param name="driveLetter">This should be the drive letter. Format: F:/, C:/..</param>

    public USBEject(string driveLetter)
    {
        string filename = @"\\.\" + driveLetter[0] + ":";
        handle = CreateFile(filename, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, 0x3, 0, IntPtr.Zero);
    }

    public bool Eject()
    {
        if (LockVolume(handle) && DismountVolume(handle))
        {
            PreventRemovalOfVolume(handle, false);
            return AutoEjectVolume(handle);
        }

        return false;
    }

    private bool LockVolume(IntPtr handle)
    {
        uint byteReturned;

        for (int i = 0; i < 10; i++)
        {
            if (DeviceIoControl(handle, FSCTL_LOCK_VOLUME, IntPtr.Zero, 0, IntPtr.Zero, 0, out byteReturned, IntPtr.Zero))
            {
                System.Windows.Forms.MessageBox.Show("Lock success!");
                return true;
            }
            Thread.Sleep(500);
        }
        return false;
    }

    private bool PreventRemovalOfVolume(IntPtr handle, bool prevent)
    {
        byte[] buf = new byte[1];
        uint retVal;

        buf[0] = (prevent) ? (byte)1 : (byte)0;
        return DeviceIoControl(handle, IOCTL_STORAGE_MEDIA_REMOVAL, buf, 1, IntPtr.Zero, 0, out retVal, IntPtr.Zero);
    }

    private bool DismountVolume(IntPtr handle)
    {
        uint byteReturned;
        return DeviceIoControl(handle, FSCTL_DISMOUNT_VOLUME, IntPtr.Zero, 0, IntPtr.Zero, 0, out byteReturned, IntPtr.Zero);
    }

    private bool AutoEjectVolume(IntPtr handle)
    {
        uint byteReturned;
        return DeviceIoControl(handle, IOCTL_STORAGE_EJECT_MEDIA, IntPtr.Zero, 0, IntPtr.Zero, 0, out byteReturned, IntPtr.Zero);
    }

    private bool CloseVolume(IntPtr handle)
    {
        return CloseHandle(handle);
    }
}
Bhargav Rao
  • 50,140
  • 28
  • 121
  • 140
lukew
  • 431
  • 2
  • 5
  • 15
  • Where API calls return a failure code, what error does [`GetLastError`](http://msdn.microsoft.com/en-us/library/ms679360(VS.85).aspx) return? – Richard Oct 09 '11 at 15:51
  • Looks okay. Locking a volume is never not a problem. Ensure no process is using the drive letter, including Windows Explorer. And check if the Windows "Safely Remove Hardware" works. – Hans Passant Oct 09 '11 at 15:53
  • Actually it seems that the CreateFile-function returns an invalid handle. This explains why locking the device fails, but why does CreateFile return an invalid handle? – lukew Oct 09 '11 at 16:19
  • 2
    @LSky: As Richard said, you need to check the result from `GetLastError`. P/invoke calls that for you, and stores the error code, so you actually have to call [`Marshal.GetLastWin32Error`](http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal.getlastwin32error(v=VS.100).aspx). Pretty sure it's going to be access denied, you requested read-write access to a volume containing a mounted filesystem, which needs a lot of privileges. – Ben Voigt Oct 09 '11 at 16:30

4 Answers4

6

Changed just a little bit your code and it goes as follows:

    [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    private static extern IntPtr CreateFile(
     string lpFileName,
     uint dwDesiredAccess,
     uint dwShareMode,
     IntPtr SecurityAttributes,
     uint dwCreationDisposition,
     uint dwFlagsAndAttributes,
     IntPtr hTemplateFile
);

    [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)]
    private static extern bool DeviceIoControl(
        IntPtr hDevice,
        uint dwIoControlCode,
        IntPtr lpInBuffer,
        uint nInBufferSize,
        IntPtr lpOutBuffer,
        uint nOutBufferSize,
        out uint lpBytesReturned,
        IntPtr lpOverlapped
    );

    [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)]
    private static extern bool DeviceIoControl(
        IntPtr hDevice,
        uint dwIoControlCode,
        byte[] lpInBuffer,
        uint nInBufferSize,
        IntPtr lpOutBuffer,
        uint nOutBufferSize,
        out uint lpBytesReturned,
        IntPtr lpOverlapped
    );

    [DllImport("kernel32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool CloseHandle(IntPtr hObject);

    private IntPtr handle = IntPtr.Zero;

    const uint GENERIC_READ = 0x80000000;
    const uint GENERIC_WRITE = 0x40000000;
    const int FILE_SHARE_READ = 0x1;
    const int FILE_SHARE_WRITE = 0x2;
    const int FSCTL_LOCK_VOLUME = 0x00090018;
    const int FSCTL_DISMOUNT_VOLUME = 0x00090020;
    const int IOCTL_STORAGE_EJECT_MEDIA = 0x2D4808;
    const int IOCTL_STORAGE_MEDIA_REMOVAL = 0x002D4804;

    /// <summary>
    /// Constructor for the USBEject class
    /// </summary>
    /// <param name="driveLetter">This should be the drive letter. Format: F:/, C:/..</param>

    public IntPtr USBEject(string driveLetter)
    {
        string filename = @"\\.\" + driveLetter[0] + ":";
        return CreateFile(filename, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, 0x3, 0, IntPtr.Zero);
    }

    public bool Eject(IntPtr handle)
    {
        bool result = false;

        if (LockVolume(handle) && DismountVolume(handle))
        {
            PreventRemovalOfVolume(handle, false);
            result = AutoEjectVolume(handle);
        }
        CloseHandle(handle);
        return result;
    }

    private bool LockVolume(IntPtr handle)
    {
        uint byteReturned;

        for (int i = 0; i < 10; i++)
        {
            if (DeviceIoControl(handle, FSCTL_LOCK_VOLUME, IntPtr.Zero, 0, IntPtr.Zero, 0, out byteReturned, IntPtr.Zero))
            {
                System.Windows.Forms.MessageBox.Show("Lock success!");
                return true;
            }
            Thread.Sleep(500);
        }
        return false;
    }

    private bool PreventRemovalOfVolume(IntPtr handle, bool prevent)
    {
        byte[] buf = new byte[1];
        uint retVal;

        buf[0] = (prevent) ? (byte)1 : (byte)0;
        return DeviceIoControl(handle, IOCTL_STORAGE_MEDIA_REMOVAL, buf, 1, IntPtr.Zero, 0, out retVal, IntPtr.Zero);
    }

    private bool DismountVolume(IntPtr handle)
    {
        uint byteReturned;
        return DeviceIoControl(handle, FSCTL_DISMOUNT_VOLUME, IntPtr.Zero, 0, IntPtr.Zero, 0, out byteReturned, IntPtr.Zero);
    }

    private bool AutoEjectVolume(IntPtr handle)
    {
        uint byteReturned;
        return DeviceIoControl(handle, IOCTL_STORAGE_EJECT_MEDIA, IntPtr.Zero, 0, IntPtr.Zero, 0, out byteReturned, IntPtr.Zero);
    }

    private bool CloseVolume(IntPtr handle)
    {
        return CloseHandle(handle);
    }

So you can use it in two ways:

        handle = USBEject("D:");
        Eject(handle);

or directly:

        Eject(USBEject("D:"));

It works for me on my Windows 10 machine (preview 14291)

OneWorld
  • 17,512
  • 21
  • 86
  • 136
Roger Deep
  • 162
  • 1
  • 6
  • 1
    Works for mounted `ISO` images and `USB` drives but fails to eject my Seagate Expansion external hard drive. – W.M. Aug 10 '17 at 17:22
  • Worked for me on Windows 10 Enterprise 2015 LTSB Build 10240 – OneWorld Mar 14 '18 at 08:43
  • I corrected your code by editing your answer: Added `CloseHandle(handle);` to the `bool Eject(IntPtr handle)` method. Without closing the handle the application can't eject anymore after the lock was rejected. (Kinda locks itself) – OneWorld Mar 20 '18 at 12:23
  • 2
    It works to some degree, because the disk is not accesible but it has not been ejected. If you click on it, it says "insert the drive" but the icon of the disk does not dissapear – KansaiRobot Nov 08 '18 at 09:37
3

Found the answer for my issue by using some of Roger Deep's code for the CreateFile call.

My code to remove a USB drive inside WPF Window:

private void Button_Click_1(object sender, RoutedEventArgs e)
{
    EjectDrive('K');
}
void EjectDrive(char driveLetter)
{
    string path = @"\\.\" + driveLetter + @":";
    IntPtr handle = CreateFile(path, GENERIC_READ | GENERIC_WRITE,
    FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, 0x3, 0, IntPtr.Zero);

    if ((long)handle == -1)
    {
        MessageBox.Show("Unable to open drive " + driveLetter);
        return;
    }

    int dummy = 0;

    DeviceIoControl(handle, IOCTL_STORAGE_EJECT_MEDIA, IntPtr.Zero, 0,
        IntPtr.Zero, 0, ref dummy, IntPtr.Zero);

    CloseHandle(handle);

    MessageBox.Show("OK to remove drive.");
}
[DllImport("kernel32", SetLastError = true)]
private static extern IntPtr CreateFile
    (string filename, uint desiredAccess,
        uint shareMode, IntPtr securityAttributes,
        int creationDisposition, int flagsAndAttributes,
        IntPtr templateFile);
[DllImport("kernel32")]
private static extern int DeviceIoControl
    (IntPtr deviceHandle, uint ioControlCode,
        IntPtr inBuffer, int inBufferSize,
        IntPtr outBuffer, int outBufferSize,
        ref int bytesReturned, IntPtr overlapped);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(IntPtr hObject);
Community
  • 1
  • 1
Nateous
  • 757
  • 9
  • 23
  • 1
    From what I can read, Roger Deep has the better answer. Though yours will work, there is a potential of corrupting the drive. This is why locking and dismounting is imperative. If another app is changing data on the drive and you just eject it, data can get corrupted. – Chizl Nov 11 '20 at 13:51
0

Here's some code that I converted from a powershell script. You need to run with admin privileges and It works to "unmount" the USB drive. However, when you try to unplug the USB drive and plug it in again, it doesn't show up as a drive letter. (To get around that you need to type "WindowsKey-X" and select Disk-Manager to reassign the drive less to the USB device. (If anybody knows how to fix that problem please post to commits.) Here's the Code:

// Right click Project and Add Reference to System.Management.dll
using System.Management;

string mq = "SELECT * FROM Win32_Volume Where Name = 'E:\\'"; 
ManagementObjectSearcher ms = new ManagementObjectSearcher(mq);
foreach (ManagementObject mo in ms.Get())
{
    mo["DriveLetter"] = null;
    mo.Put();       
    ManagementBaseObject inParams = mo.GetMethodParameters("Dismount");
    inParams["Force"] = false;  
    inParams["Permanent"] = false;
    mo.InvokeMethod("Dismount", inParams, null);
}

Note that the powershell script also has the same problem of reattaching the USB device after ejecting. Here's the powershell script for your reference:

$vol = get-wmiobject -Class Win32_Volume | 
    where{$_.Name -eq 'E:\'}         
$vol.DriveLetter = $null  
$vol.Put()  
$vol.Dismount($false, $false)

Here's a class that I just wrote to Manage Mounting and Unmounting of Removable USB Drives using WMI:

using System;
using System.IO;
using System.Text;
using System.Windows;
using System.Management; //<-- right-click on project and add reference
using System.Collections.Generic;
using System.Text.RegularExpressions;

// This Class implements Mount/Unmount for USB Removable Drives
//  in a way similar to "Disk Manager" in the Control Panel.
//
//  Currently, It doesn't implement "Eject" like when you right
//    right-click on the USB icon on lower right of screen.
//    The "Unmount" is similar to "Eject" except it dosn't
//    cleanup the registry so that the USB drive can be automatically
//    recognized again without manually mounting it from "Disk Manager"
//    If somebody knows how to fix this class to gain this function...
//       please post it to their thread.  Thanks.
namespace WPM {

    public struct UsbDriveItem_t {
        public int    Index;
        public string DeviceId;
        public char   DriveLetter;
        public string Label;

        public override string ToString() {
            if (Index < 0)
                return "<none>";
            else 
                return String.Format("{0}: {1}", DriveLetter, Label);
        }               
    };

    delegate void UsbEvent();

    class UsbDriveRemovable {

        public static int Unmount(char DriveLetter) {
            bool success = ValidateAdmin("UsbDriveRemovable.Unmount()");
            if (!success) return -1;

            string Name = "'" + DriveLetter + ":\\\\'";

            string mq   = "SELECT * FROM Win32_Volume Where Name = " + Name;
            ManagementObjectSearcher ms = new ManagementObjectSearcher(mq);
            ManagementObjectCollection mc = ms.Get();
            foreach (ManagementObject mo in mc) {
                var DriveLetterI  = mo["DriveLetter"].ToString();
                mo["DriveLetter"] = null;
                mo.Put();
                ManagementBaseObject inParams = mo.GetMethodParameters("Dismount");
                inParams["Force"] = false;
                inParams["Permanent"] = false;
                ManagementBaseObject outParams = mo.InvokeMethod("Dismount", inParams, null);
                string rc = outParams["ReturnValue"].ToString();
                mo.Dispose();
            }
            mc.Dispose();
            ms.Dispose();
            return 0;
        }

        public static int Mount(string DeviceId, char Letter = '?') {
            bool success = ValidateAdmin("UsbDriveRemovable.Mount()");
            if (!success) return -1;

            if (Letter == '?' || Letter == '#') {
                GetFirstUnsedLetter(out Letter);
            }

            string FixDeviceId = Regex.Replace(DeviceId, @"\\", @"\\");

            string mq = "SELECT * FROM Win32_Volume WHERE DeviceId = '"
                + FixDeviceId
                + "'";

            ManagementObjectSearcher ms = new ManagementObjectSearcher(mq);
            ManagementObjectCollection mc = ms.Get();
            foreach (ManagementObject mo in mc) {
                ManagementBaseObject inParams = mo.GetMethodParameters("AddMountPoint");
                inParams["Directory"] = Letter + ":\\";
                ManagementBaseObject outParams = mo.InvokeMethod("AddMountPoint", inParams, null);
                string rc = outParams["ReturnValue"].ToString();
                mo.Dispose();
            }
            mc.Dispose();
            ms.Dispose();
            return 0;
        }

        /*List<UsbDriveItem_t>*/ 
        public static int ListDrives(ref List<UsbDriveItem_t> DriveList) {
            DriveList.Clear();
            string mq = "SELECT * FROM Win32_Volume Where DriveType = '2'";
            ManagementObjectSearcher ms   = new ManagementObjectSearcher(mq);
            ManagementObjectCollection mc = ms.Get();
            int count = 0;
            foreach (ManagementObject mo in mc) {
                UsbDriveItem_t item = new UsbDriveItem_t();
                item.Index       = count;
                item.Label       = (mo["Label"] == null)       ? "<none>" : mo["Label"].ToString();
                item.DriveLetter = (mo["DriveLetter"] == null) ? '#' : mo["DriveLetter"].ToString()[0];
                item.DeviceId    = (mo["DeviceId"] == null)    ? "<none>" : mo["DeviceId"].ToString();
                DriveList.Add(item);
                mo.Dispose();
            }
            count++;
            mc.Dispose();
            ms.Dispose();

            return 0;
        }

        public static void MountItem(UsbDriveItem_t DriveItem) {            
            char   DriveLetter = DriveItem.DriveLetter;
            string DriveLabel  = DriveItem.Label;
            string DeviceId    = DriveItem.DeviceId;

            // Mount Drive if its not already Mounted
            if (DriveLetter == '#') {
                UsbDriveRemovable.GetFirstUnsedLetter(out DriveLetter);
                UsbDriveRemovable.Mount(DeviceId, DriveLetter);
            }
            return; 
        }

        public static void UnmountItem(UsbDriveItem_t DriveItem) {      
            char   DriveLetter = DriveItem.DriveLetter;         
            UsbDriveRemovable.Unmount(DriveLetter);
            return;
        }

        public static int GetFirstUnsedLetter(out char Letter) {
            bool[] alphabet = new bool[26];

            for (int i=0; i < 26; i++) {
                alphabet[i] = false;
            }

            string mq = "SELECT * FROM Win32_Volume";
            ManagementObjectSearcher ms   = new ManagementObjectSearcher(mq);
            ManagementObjectCollection mc = ms.Get();
            foreach (ManagementObject mo in mc) {
                if (mo["DriveLetter"] != null) {
                    char cc      = mo["DriveLetter"].ToString()[0];
                    int  ci      = char.ToUpper(cc) - 65;
                    alphabet[ci] = true;
                }
                mo.Dispose();
            }
            mc.Dispose();
            ms.Dispose();

            int found = -1;
            for (int i=3; i < 26; i++) {
                if (alphabet[i] == false) {
                    found = i;
                    break;
                }
            }

            if (found >= 0) {
                Letter = (char)(found + 65);
                return 0;
            }
            else {
                Letter = '?';
                return -1;
            }
        }


        public static object
            RegisterInsertEvent(UsbEvent InsertEvent) {
            var insertQuery = new WqlEventQuery("SELECT * FROM Win32_DeviceChangeEvent WHERE EventType = 2");
            var insertWatcher = new ManagementEventWatcher(insertQuery);            
            insertWatcher.EventArrived += delegate(object sender, EventArrivedEventArgs e) {
                // string driveName = e.NewEvent.Properties["DriveName"].Value.ToString();              
                Action action = delegate {
                    InsertEvent();
                };
                Application.Current.Dispatcher.BeginInvoke(action);
            };
            insertWatcher.Start();
            return (object)insertWatcher;
        }

        public static object RegisterRemoveEvent(UsbEvent RemoveEvent) {
            var removeQuery = new WqlEventQuery("SELECT * FROM Win32_DeviceChangeEvent WHERE EventType = 3");
            var removeWatcher = new ManagementEventWatcher(removeQuery);
            removeWatcher.EventArrived += delegate(object sender, EventArrivedEventArgs e) {
                // string driveName = e.NewEvent.Properties["DriveName"].Value.ToString();              
                Action action = delegate {                  
                    RemoveEvent();
                };
                Application.Current.Dispatcher.BeginInvoke(action);
            };
            removeWatcher.Start();
            return (object)removeWatcher;
        }

        // Mount all UsbRemovable Drives that are not currently mounted
        public static int MountAll() {
            List<UsbDriveItem_t> DriveList = new List<UsbDriveItem_t>();
            ListDrives(ref DriveList);

            foreach (UsbDriveItem_t item in DriveList) {
                if (item.DriveLetter == '?') {
                    Mount(item.DeviceId);
                }
            }
            return 0;
        }

        // Unmount all UsbRemovable Drives
        public static int UnmountAll() {
            List<UsbDriveItem_t> DriveList = new List<UsbDriveItem_t>();
            ListDrives(ref DriveList);

            foreach (UsbDriveItem_t item in DriveList) {
                if (item.DriveLetter != '?') {
                    Unmount(item.DriveLetter);
                }
            }
            return 0;
        }

        public static bool IsAdministrator()
        {
            var id   = System.Security.Principal.WindowsIdentity.GetCurrent();
            var prin = new System.Security.Principal.WindowsPrincipal(id);
            return prin.IsInRole(
                System.Security.Principal.WindowsBuiltInRole.Administrator);
        }

        public static bool ValidateAdmin(string CalledFrom = null) {
            if (CalledFrom == null) {
                CalledFrom = "";
            }
            if (!IsAdministrator()) {
                string msg = "Please rerun this application with admin privileges.\r\n\r\n"
                + "Access denied to call " + CalledFrom + "\r\n\r\n";
                MessageBox.Show(msg, "ERROR");
                return false;
            }
            return true;
        }

        public static void StartExplorer(char DriveLetter) 
        {
            var proc1 = new System.Diagnostics.Process();
            proc1.StartInfo.FileName               = @"C:\\Windows\\System32\\explorer.exe";
            proc1.StartInfo.Arguments              = DriveLetter.ToString();
            proc1.StartInfo.CreateNoWindow         = true;
            proc1.StartInfo.UseShellExecute        = false;
            proc1.StartInfo.RedirectStandardOutput = true;
            proc1.StartInfo.RedirectStandardError  = true;
            proc1.Start();
            proc1.WaitForExit();
            string proc1out = proc1.StandardOutput.ReadToEnd();
            string proc1err = proc1.StandardError.ReadToEnd();
            //if (proc1.ExitCode != 0) {
            //  string msg = proc1out + "\r\n\r\n" + proc1err;
            //  MessageBox.Show(msg, "Error: Mountvol /R");
            //}
            proc1.Close();                  
        }       

    } //class
} //namespace



/*  DOESN'T WORK WELL...

        // Kludge to get USB Drive to be recognized again
        void UsbCleanup() {
            var proc1 = new System.Diagnostics.Process();
            proc1.StartInfo.FileName               = @"C:\\Windows\\System32\\mountvol.exe";
            proc1.StartInfo.Arguments              = @"/R";
            proc1.StartInfo.CreateNoWindow         = true;
            proc1.StartInfo.UseShellExecute        = false;
            proc1.StartInfo.RedirectStandardOutput = true;
            proc1.StartInfo.RedirectStandardError  = true;
            proc1.Start();
            proc1.WaitForExit();
            string proc1out = proc1.StandardOutput.ReadToEnd();
            string proc1err = proc1.StandardError.ReadToEnd();
            if (proc1.ExitCode != 0) {
                string msg = proc1out + "\r\n\r\n" + proc1err;
                MessageBox.Show(msg, "Error: Mountvol /R");
            }
            proc1.Close();

            var proc2 = new System.Diagnostics.Process();
            proc2.StartInfo.FileName               = @"C:\\Windows\\System32\\mountvol.exe";
            proc2.StartInfo.Arguments              = @"/E";
            proc2.StartInfo.CreateNoWindow         = true;
            proc2.StartInfo.UseShellExecute        = false;
            proc2.StartInfo.RedirectStandardOutput = true;
            proc2.StartInfo.RedirectStandardError  = true;
            proc2.Start();
            proc2.WaitForExit();
            string proc2out = proc2.StandardOutput.ReadToEnd();
            string proc2err = proc2.StandardError.ReadToEnd();
            if (proc2.ExitCode != 0) {
                string msg = proc1out + "\r\n\r\n" + proc1err;
                MessageBox.Show(msg, "Error: Mountvol /E");
            }
            proc2.Close();
            return;
        }
*/
Bimo
  • 5,987
  • 2
  • 39
  • 61
  • Does "You need to run with admin privileges" also apply for your last code snippet? – OneWorld Mar 20 '18 at 10:48
  • It tested it out. The 'Unmount' function does in a complicated way exactly the same what Win32.DeleteVolumeMountPointW does (https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-deletevolumemountpointw). The same two problems remain: 1) Admin rights are needed 2) The drive letter is cleared before the drive is unmounted so the drive doesn't reattach automatically when it is plugged in the next time. You have to open the 'Volume Manager' in Windows and reassign the drive letter manually (you could also use 'Mount' but this would require the correct cryptic PNPDeviceId). – AndresRohrAtlasInformatik Oct 25 '20 at 13:30
0

You could convert the following PowerShell into C#:

$Eject = New-Object -comObject Shell.Application
$Eject.NameSpace(17).ParseName($usbDrvLetter+“:”).InvokeVerb(“Eject”)
Bimo
  • 5,987
  • 2
  • 39
  • 61