通过C#弹出USB设备

我正在寻找一种通过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; ///  /// Constructor for the USBEject class ///  /// This should be the drive letter. Format: F:/, C:/.. 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; ///  /// Constructor for the USBEject class ///  /// This should be the drive letter. Format: F:/, C:/.. 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 ""; 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*/ public static int ListDrives(ref List 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) ? "" : mo["Label"].ToString(); item.DriveLetter = (mo["DriveLetter"] == null) ? '#' : mo["DriveLetter"].ToString()[0]; item.DeviceId = (mo["DeviceId"] == null) ? "" : 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 DriveList = new List(); 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 DriveList = new List(); 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”)