【问题标题】:How to detect whether physical device is online or offline in Windows using pInvoke如何使用 pInvoke 在 Windows 中检测物理设备是在线还是离线
【发布时间】:2020-07-26 12:29:04
【问题描述】:

我编写了可以从物理磁盘写入和读取数据的代码。 我使用 pInvoke CreateFile 打开物理磁盘,并使用 FileStream 执行读取和写入数据。

当物理磁盘在线时,一切正常。

如果物理磁盘脱机并且我尝试写入磁盘,则会收到错误消息 System.IO.IOException: '媒体被写保护。'

如何在不尝试写入磁盘的情况下检测磁盘是否脱机。

这是创建 FileStream 并执行写入磁盘的代码

    private const uint GENERIC_READ = 0x80000000;
    private const uint GENERIC_WRITE = 0x40000000;
    private const uint OPEN_EXISTING = 3;
    private const uint FILE_FLAG_NO_BUFFERING = 0x20000000;
    private const uint FILE_FLAG_WRITE_THROUGH = 0x80000000;

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



    public void OpenFile()
    {
        m_handleValue = CreateFile(DevicePath, GENERIC_WRITE | GENERIC_READ,
                            0x3, IntPtr.Zero, OPEN_EXISTING,
                            FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH
                            , IntPtr.Zero);
        
        m_fileStream = new FileStream(m_handleValue, FileAccess.ReadWrite, 512);
    }



    public void WriteData(int offset, byte[] data)
    {
        m_fileStream.Seek(offset, SeekOrigin.Begin);
        m_fileStream.Write(data, 0, data.Length);
    }

【问题讨论】:

  • 事实上,不可能 100% 确定地知道,因为您依赖该设备的设备驱动程序来正确准确地将其状态报告给 Windows。 Windows 向您提供错误消息“媒体已写保护”。 因为它认为设备在线且已连接但受写保护,如果 Windows 认为设备离线或不可用,它会给您不同的错误消息和错误代码。
  • 你为什么要重新实现CreateFile?您的声明不会添加任何 FileStream 的构造函数中不存在的任何功能。
  • 无论如何,您都希望使用 WMI 来检查设备状态,而不是 Win32 的 CreateFile。也就是说,可能实际上允许抛出异常 - 但前提是离线/不可用设备确实是一个异常情况。
  • 我没有重新实现 CreateFile,我将它与标志一起使用,因此我可以跳过操作系统(使用 FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH)
  • 这些选项也可以通过其他 FileStream 构造函数重载获得。

标签: c# pinvoke filehandle


【解决方案1】:

使用 DeviceIoControl pInvoke 我们可以查看属性 DISK_ATTRIBUTE_OFFLINE

public static class RawDeviceInfoProvider
    {
        private const int IOCTL_DISK_GET_DISK_ATTRIBUTES = 0x000700F0;
        private const uint DISK_ATTRIBUTE_OFFLINE = 0x1;

        [StructLayout(LayoutKind.Sequential)]
        public struct GetDiskAttributes
        {
            public uint Version;
            public uint Reserved;
            public ulong Attributes;
        }

        [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)]
        public static extern bool DeviceIoControl(SafeHandle hDevice, uint dwIoControlCode, IntPtr lpInBuffer,
            uint nInBufferSize, IntPtr lpOutBuffer, uint nOutBufferSize, out uint lpBytesReturned, IntPtr lpOverlapped);


        public static bool GetOnlineStatus(SafeFileHandle hDevice)
        {
            uint dummy;
            GetDiskAttributes attributes = new GetDiskAttributes();
            IntPtr lpOutBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(attributes));
            bool success = DeviceIoControl(hDevice, IOCTL_DISK_GET_DISK_ATTRIBUTES, IntPtr.Zero, 0,
                lpOutBuffer, (uint)Marshal.SizeOf(typeof(GetDiskAttributes)), out dummy, IntPtr.Zero);
            attributes = (GetDiskAttributes)Marshal.PtrToStructure(lpOutBuffer, typeof(GetDiskAttributes));
            Marshal.FreeHGlobal(lpOutBuffer);
            if (!success)
            {
                int errorCode = Marshal.GetLastWin32Error();
                throw new IOException("Unable to retrieve disk attributes, Error: " + errorCode);
            }
            bool isOffline = (attributes.Attributes & DISK_ATTRIBUTE_OFFLINE) > 0;
            return !isOffline;
        }
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-20
    相关资源
    最近更新 更多