簡體   English   中英

通過C#彈出USB設備

[英]Eject USB device via C#

我正在尋找一種通過C#-code彈出USB設備的簡短方法,所以我自己編寫了一個小類,但它根本不起作用。 因為沒有彈出窗口說“鎖定成功!” 我認為問題依賴於“LockVolume”函數,但我不知道在哪里。

有人看到我犯的錯誤嗎?

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);
    }
}

改變了一點你的代碼,它如下:

    [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);
    }

所以你可以用兩種方式使用它:

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

或直接:

        Eject(USBEject("D:"));

它適用於我的Windows 10機器(預覽14291)

通過使用一些Roger Deep的CreateFile調用代碼找到了我的問題的答案。

我在WPF窗口中刪除USB驅動器的代碼:

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);

這是我從powershell腳本轉換的一些代碼。 您需要以管理員權限運行,它可以“卸載”USB驅動器。 但是,當您嘗試拔下USB驅動器並重新插入時,它不會顯示為驅動器號。 (為了解決這個問題,你需要鍵入“WindowsKey-X”並選擇Disk-Manager來將驅動器重新分配給USB設備。(如果有人知道如何解決這個問題,請發布到提交。)這是代碼:

// 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);
}

請注意,powershell腳本也具有在彈出后重新連接USB設備的相同問題。 這是powershell腳本供您參考:

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

這是我剛剛編寫的一個類,用於管理使用WMI安裝和卸載可移動USB驅動器:

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;
        }
*/

您可以將以下PowerShell轉換為C#:

$Eject = New-Object -comObject Shell.Application
$Eject.NameSpace(17).ParseName($usbDrvLetter+“:”).InvokeVerb(“Eject”)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM