如何在C#中安全移除挂载为文件夹的USB/SD卡(无需卸载)
在C#中安全移除挂载到文件夹的存储设备
好问题!这种挂载到文件夹的存储设备确实可以在C#里直接执行安全移除,不需要先卸载挂载点——核心是绕开驱动器号,直接操作对应的底层卷设备。下面是具体的实现思路和代码示例:
核心思路
Windows将存储设备挂载到文件夹时,该设备依然对应一个唯一的卷对象。我们只需通过挂载路径找到这个卷的标识,然后调用Windows原生API发送弹出指令,就能实现安全移除操作,全程无需卸载挂载点。
具体实现步骤(附代码)
要实现这个功能,需要通过P/Invoke调用几个Windows核心API,以下是完整的C#代码示例:
首先,定义所需的API和常量:
using System; using System.Runtime.InteropServices; using System.Text; public static class SafeRemovalHelper { // 常量定义 private const int GENERIC_READ = 0x80000000; private const int GENERIC_WRITE = 0x40000000; private const int FILE_SHARE_READ = 0x00000001; private const int FILE_SHARE_WRITE = 0x00000002; private const int OPEN_EXISTING = 3; private const int IOCTL_STORAGE_EJECT_MEDIA = 0x2D4808; // P/Invoke 声明 [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern bool GetVolumeNameForVolumeMountPoint(string lpszVolumeMountPoint, StringBuilder lpszVolumeName, uint cchBufferLength); [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] private static extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr lpSecurityAttributes, 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", SetLastError = true)] private static extern bool CloseHandle(IntPtr hObject); public static bool EjectMountedFolder(string mountPath) { // API要求挂载路径必须以反斜杠结尾,这里做兼容处理 if (!mountPath.EndsWith("\\")) { mountPath += "\\"; } // 1. 获取挂载点对应的卷名 StringBuilder volumeName = new StringBuilder(256); if (!GetVolumeNameForVolumeMountPoint(mountPath, volumeName, (uint)volumeName.Capacity)) { Console.WriteLine($"获取卷名失败,错误码: {Marshal.GetLastWin32Error()}"); return false; } // 2. 打开卷设备 IntPtr hVolume = CreateFile( volumeName.ToString(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero); if (hVolume == IntPtr.Zero || hVolume == (IntPtr)(-1)) { Console.WriteLine($"打开卷设备失败,错误码: {Marshal.GetLastWin32Error()}"); return false; } try { // 3. 发送弹出指令 uint bytesReturned; bool result = DeviceIoControl( hVolume, IOCTL_STORAGE_EJECT_MEDIA, IntPtr.Zero, 0, IntPtr.Zero, 0, out bytesReturned, IntPtr.Zero); if (!result) { Console.WriteLine($"执行弹出操作失败,错误码: {Marshal.GetLastWin32Error()}"); } return result; } finally { // 务必关闭设备句柄,避免资源泄漏 CloseHandle(hVolume); } } }
使用示例
// 调用方法,传入你的挂载文件夹路径 bool success = SafeRemovalHelper.EjectMountedFolder(@"C:\documents\sdcard"); if (success) { Console.WriteLine("存储设备已安全移除"); } else { Console.WriteLine("安全移除失败"); }
关键注意事项
- 权限要求:这个操作需要管理员权限,所以你的程序需要以管理员身份运行,或者在应用程序清单中声明
requestedExecutionLevel为requireAdministrator。 - 设备占用问题:如果弹出失败且错误码为32,说明设备正在被占用,需要先关闭所有打开该设备文件的句柄。
- 系统兼容性:该方法适用于Windows Vista及以上版本,
GetVolumeNameForVolumeMountPoint在这些系统中稳定支持。
内容的提问来源于stack exchange,提问作者theNewOne92
相关产品推荐
相关产品推荐

