评估驱动器是不是正在使用中

Posted

技术标签:

【中文标题】评估驱动器是不是正在使用中【英文标题】:Evaluate if drive is in use评估驱动器是否正在使用中 【发布时间】:2018-02-18 11:51:18 【问题描述】:

我想使用 C# 评估驱动器是否正在使用(如果我没记错的话,这意味着驱动器正在发生一些读/写事情)。我不介意使用 bash 脚本或类似脚本的解决方案,因为我可以在 C# 应用程序中使用它们。我已经找到了一个关于 bash 脚本的问题 here,但无法用给出的答案解决我的问题。

我已经考虑过使用DriveInfo 类,但是它似乎对我没有任何用处。我想知道我是否可以使用来自DriveInfoIsReady 属性,因为我猜它在读/写时不会准备好,但这种尝试对我来说似乎相当拙劣。

不过我还是试过了:

private static bool IsDriveInUse ()

    var drive = DriveInfo.GetDrives ().FirstOrDefault(info => info.Name.StartsWith(DRIVE_LETTER.ToString()));
    return drive != null && !drive.IsReady;

但它不起作用(当我从驱动器播放音乐时它返回 false)。

对我来说最佳解决方案是一个函数,它告诉我驱动器是否在特定时间段内使用(让我们坚持使用名称IsDriveInUse)。这意味着如果时间是例如 60 秒,如果在从驱动器读取函数调用内容前 5 秒,IsDriveInUse 应该返回 true,如果在过去的 60 秒内没有读/写操作,则应该返回 false .


编辑为了具体说明使用中的确切含义,我将尝试解释我正在尝试做的事情。我正在编写一个工具,当它空闲或按下键盘快捷键时,它会自动降低硬盘驱动器的转速。我设法以编程方式将其降速(即使 Windows 集成工具或我发现的其他工具都能够做到这一点,但这是另一个问题)。但是,它现在每分钟都会使硬盘驱动器减速,无论它当前是否正在使用。这意味着,如果我从硬盘播放音乐,它仍然会减速,只是在它之后直接加速,这不会减少噪音的产生。

我希望这可以澄清问题。


编辑我现在尝试使用FSCTL_LOCK_VOLUME 控制代码(我找不到IOCTL_DISK_PERFORMANCE 的值),但在我玩游戏时它仍然为IsDriveInUse() 返回false音乐。此外,当我向下旋转它时,它会导致 Windows 直接再次旋转驱动器(可能是因为发布使 Windows 认为某些东西正在使用驱动器)。这是我尝试过的:

public class DriveManager

    public const int FSCTL_LOCK_VOLUME = 0x00090018;
    public const int FSCTL_UNLOCK_VOLUME = 0x0009001c;

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

    [return: MarshalAs (UnmanagedType.Bool)]
    [DllImport ("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern bool DeviceIoControl (
        [In] SafeFileHandle hDevice,
        [In] int dwIoControlCode, [In] IntPtr lpInBuffer,
        [In] int nInBufferSize, [Out] IntPtr lpOutBuffer,
        [In] int nOutBufferSize, out int lpBytesReturned,
        [In] IntPtr lpOverlapped);

    public static SafeFileHandle CreateFileR (string device)
    
        string str = device.EndsWith (@"\") ? device.Substring (0, device.Length - 1) : device;
        return new SafeFileHandle (
            CreateFile (@"\\.\" + str, WinntConst.GENERIC_READ, WinntConst.FILE_SHARE_READ, IntPtr.Zero,
                        WinntConst.OPEN_EXISTING, WinntConst.FILE_ATTRIBUTE_NORMAL, IntPtr.Zero), true);
    

    internal class WinntConst
    
        // Fields
        internal static uint FILE_ATTRIBUTE_NORMAL = 0x80;

        internal static uint FILE_SHARE_READ = 1;
        internal static uint GENERIC_READ = 0x80000000;
        internal static uint OPEN_EXISTING = 3;
    

    public static bool IsDriveInUse (string deviceName)
    
        var handle = CreateFileR (deviceName);
        var buffer = Marshal.AllocHGlobal (sizeof (int));
        try
        
            return
                DeviceIoControl (handle,
                                 FSCTL_LOCK_VOLUME,
                    IntPtr.Zero, 
                    0,
                    buffer,
                    sizeof(int),
                    out var bytesReturned,
                    IntPtr.Zero
                );
        
        finally
        
            var sessionId = Marshal.ReadInt32 (buffer);
            Marshal.FreeHGlobal (buffer);
            handle.Close ();
        
    

以及实现:

private static bool IsDriveInUse () => DriveManager.IsDriveInUse ($@"DRIVE_LETTER:\");

也许它有助于查看我正在旋转光盘的部分(我为此使用了Smartmontools):

internal static class Program

    private const string PROGRAM_PATH = @"External\smartctl.exe";
    private const string ARGUMENTS_SHUTDOWN = @"-s standby,now 0:";
    private const char DRIVE_LETTER = 'd';


    public static void Main (string [] args)
    
        InitializeHotKey ();
        Console.WriteLine ("Hotkey registered!");

        while (true)
        
            Thread.Sleep (60000);
            if (!IsDriveInUse ())
                ShutDownDrive (true);
        
    

    private static bool IsDriveInUse () => DriveManager.IsDriveInUse ($@"DRIVE_LETTER:\");

    private static void InitializeHotKey ()
    
        HotKeyManager.RegisterHotKey (Keys.D, KeyModifiers.Alt | KeyModifiers.Control);
        HotKeyManager.HotKeyPressed += HotKeyPressed;
    

    private static void HotKeyPressed (object sender, HotKeyEventArgs hotKeyEventArgs) => ShutDownDrive (true);

    private static void ShutDownDrive (bool withDialog = false)
    
        Process process;
        (process = new Process
        
            StartInfo = new ProcessStartInfo
            
                WindowStyle = ProcessWindowStyle.Hidden,
                FileName = PROGRAM_PATH,
                Arguments = string.Format (ARGUMENTS_SHUTDOWN, DRIVE_LETTER)
            
        ).Start ();

        process.WaitForExit ();
        process.Close ();

        if (withDialog)
            Console.WriteLine ("Drive shut down!");
    

【问题讨论】:

具体说明“使用中”和“准备就绪”的含义。磁盘可以通过调度一次执行多项操作。在工作站以及服务器硬盘上,几乎总是在进行读取或写入。你到底想解决什么问题?它是固定在机器中的驱动器、可移动驱动器还是网络驱动器? 还假设您可以获得有关“InUse”的正确信息。在您采取行动之前,其状态可能会发生变化。 @L.B 因为我主要是为我编写这个工具,所以我可以向你保证不会出现这种情况,因为我不经常使用我的硬盘。 认为你需要FSCTL_LOCK_VOLUME - 如果卷上有任何打开的文件,此操作将失败。反之,此操作成功表示没有打开文件。 @RbMm 这对于习惯于 P/Invoke 的人来说可能很明显,但是您能提供一个使用示例吗? 【参考方案1】:

也许您可以使用与您的驱动器相关的 Windows 性能计数器? “磁盘读取/秒”似乎与您的想法非常相关。

在 .Net 中,计数器可通过 System.Diagnostics.PerformanceCounter 看那里 : https://msdn.microsoft.com/en-us/library/system.diagnostics.performancecounter(v=vs.110).aspx

【讨论】:

以上是关于评估驱动器是不是正在使用中的主要内容,如果未能解决你的问题,请参考以下文章

格盘的时候出现“驱动器正在使用中。另一个程序或进程正在使用此驱动器。是不是仍要对其进行格式化”

格盘的时候出现“驱动器正在使用中。另一个程序或进程正在使用此驱动器。是不是仍要对其进行格式化”

强化学习的十大原则

批量检查映射的网络驱动器是不是存在

领域驱动设计(DDD)- 请先搞清楚一些概念

如何制作可引导的iso(不是cd或flash驱动器)来测试自己的引导加载程序?