You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
99 lines
3.4 KiB
99 lines
3.4 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace AIK.Service.Interop
|
|
{
|
|
/// <summary>
|
|
/// P/Invoke 方法实现 Windows 卷弹出功能
|
|
/// </summary>
|
|
internal static class WindowsVolumeEjector
|
|
{
|
|
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
|
|
private static extern IntPtr CreateFile(
|
|
string lpFileName,
|
|
uint dwDesiredAccess,
|
|
uint dwShareMode,
|
|
IntPtr lpSecurityAttributes,
|
|
uint dwCreationDisposition,
|
|
uint dwFlagsAndAttributes,
|
|
IntPtr hTemplateFile);
|
|
|
|
[DllImport("kernel32.dll", SetLastError = true)]
|
|
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")]
|
|
private static extern bool CloseHandle(IntPtr hObject);
|
|
|
|
private const uint GENERIC_READ = 0x80000000;
|
|
private const uint FILE_SHARE_READ = 0x1;
|
|
private const uint FILE_SHARE_WRITE = 0x2;
|
|
private const uint OPEN_EXISTING = 3;
|
|
private const uint FSCTL_DISMOUNT_VOLUME = 0x00090020;
|
|
private const uint IOCTL_STORAGE_EJECT_MEDIA = 0x2D4808;
|
|
|
|
public static async Task<bool> EjectVolumeAsync(string driveLetter)
|
|
{
|
|
return await Task.Run(() =>
|
|
{
|
|
if (string.IsNullOrEmpty(driveLetter))
|
|
return false;
|
|
|
|
// 标准化输入:确保是 "X:" 或 "X:\"
|
|
string cleanDrive = driveLetter.TrimEnd('\\');
|
|
if (!cleanDrive.EndsWith(":"))
|
|
return false;
|
|
|
|
string devicePath = @"\\.\" + cleanDrive + @"\";
|
|
IntPtr handle = IntPtr.Zero;
|
|
|
|
try
|
|
{
|
|
handle = CreateFile(devicePath, GENERIC_READ,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
|
|
|
|
if (handle == IntPtr.Zero || handle == new IntPtr(-1))
|
|
{
|
|
var errorCode = Marshal.GetLastWin32Error();
|
|
// 可记录日志:$"CreateFile failed with error {errorCode}"
|
|
return false;
|
|
}
|
|
|
|
// 尝试卸载卷(可选,提高成功率)
|
|
DeviceIoControl(handle, FSCTL_DISMOUNT_VOLUME,
|
|
IntPtr.Zero, 0, IntPtr.Zero, 0, out _, IntPtr.Zero);
|
|
|
|
// 弹出介质
|
|
bool result = DeviceIoControl(handle, IOCTL_STORAGE_EJECT_MEDIA,
|
|
IntPtr.Zero, 0, IntPtr.Zero, 0, out _, IntPtr.Zero);
|
|
|
|
return result;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// 可记录异常
|
|
return false;
|
|
}
|
|
finally
|
|
{
|
|
if (handle != IntPtr.Zero && handle != new IntPtr(-1))
|
|
{
|
|
CloseHandle(handle);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|