c# - 带有 IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS 的 DeviceIoControl - C#

标签 c# winapi

我的 C++ 代码使用 IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS 调用 DeviceIoControl,我需要将其转换为 C#。
我发现了许多 DeviceIoControl p\invoke 示例,但没有针对此特定标志的示例。 我试过使用以下 extern:

[DllImport("kernel32", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DeviceIoControl(SafeFileHandle hVol, int controlCode, IntPtr inBuffer, int inBufferSize, ref DiskExtents outBuffer, int outBufferSize, ref int bytesReturned, IntPtr overlapped);

结构:

[StructLayout(LayoutKind.Sequential)]
public struct DiskExtent
{
    public uint DiskNumber;
    public long StartingOffset;
    public long ExtentLength;
}

[StructLayout(LayoutKind.Sequential)]
public struct DiskExtents
{
    public int numberOfExtents;
    public DiskExtent[] first;
}

以及以下调用代码:

int bytesReturned = 0;
DiskExtents diskExtents = new DiskExtents();
bool res = DeviceIoControl(hVol, 5636096, IntPtr.Zero, 0, ref diskExtents, Marshal.SizeOf(diskExtents), ref bytesReturned, IntPtr.Zero);

但调用返回 false,数据结构始终为空。 任何帮助都会很棒!

最佳答案

您的代码的问题在于,除非数组的大小已知,否则无法编码数组。这意味着在进行 DeviceIoControl 调用时,您的 DiskExtents 结构无法正确编码。

以下代码适用于我(出于互操作目的,我更喜欢明确大小的类型,并且更喜欢坚持使用 Win32 header 中的名称,因为我已经知道它们的含义并且可以轻松地通过 Google 搜索它们):

internal static class NativeMethods
{
   internal const UInt32 IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS = 0x00560000;

   [StructLayout(LayoutKind.Sequential)]
   internal class DISK_EXTENT
   {
      public UInt32 DiskNumber;
      public Int64  StartingOffset;
      public Int64  ExtentLength;
   }

   [StructLayout(LayoutKind.Sequential)]
   internal class VOLUME_DISK_EXTENTS
   {
      public UInt32      NumberOfDiskExtents;
      public DISK_EXTENT Extents;
   }

   [DllImport("kernel32", SetLastError = true)]
   [return: MarshalAs(UnmanagedType.Bool)]                             
   internal static extern bool DeviceIoControl(SafeFileHandle hDevice,
                                               UInt32         ioControlCode,
                                               IntPtr         inBuffer,
                                               UInt32         inBufferSize,
                                               IntPtr         outBuffer,
                                               UInt32         outBufferSize,
                                               out UInt32     bytesReturned,
                                               IntPtr         overlapped);
}
class Program
{
    static void Main(string[] args)
    {
       // Open the volume handle using CreateFile()
       SafeFileHandle sfh = ...

       // Prepare to obtain disk extents.
       // NOTE: This code assumes you only have one disk!
       NativeMethods.VOLUME_DISK_EXTENTS vde           = new NativeMethods.VOLUME_DISK_EXTENTS();
       UInt32                            outBufferSize = (UInt32)Marshal.SizeOf(vde);
       IntPtr                            outBuffer     = Marshal.AllocHGlobal((int)outBufferSize);
       UInt32                            bytesReturned = 0;
       if (NativeMethods.DeviceIoControl(sfh,
                                         NativeMethods.IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
                                         IntPtr.Zero,
                                         0,
                                         outBuffer,
                                         outBufferSize,
                                         out bytesReturned,
                                         IntPtr.Zero))
       {
          // The call succeeded, so marshal the data back to a
          // form usable from managed code.
          Marshal.PtrToStructure(outBuffer, vde);

          // Do something with vde.Extents here...e.g.
          Console.WriteLine("DiskNumber: {0}\nStartingOffset: {1}\nExtentLength: {2}",
                            vde.Extents.DiskNumber,
                            vde.Extents.StartingOffset,
                            vde.Extents.ExtentLength);
       }
       Marshal.FreeHGlobal(outBuffer);
    }
}

当然,这假设您只需要获取有关单个磁盘的信息。如果您需要 DeviceIoControl 函数将有关多个 磁盘的信息填充到VOLUME_DISK_EXTENTS 结构中,您将不得不更加努力。

问题是直到运行时你才能知道究竟有多少磁盘。 DeviceIoControl 将返回 ERROR_MORE_DATA 以通知您表中还有更多信息,您需要使用更大的缓冲区再次调用。您将按照与上面相同的方式执行此操作,但会出现一些额外的复杂情况。您将需要使用类似 Marshal.ReAllocHGlobal 的东西来扩展缓冲区的大小以容纳额外的 DISK_EXTENT 结构。在第一次调用 DeviceIoControl 失败后,所需的数字将在 VOLUME_DISK_EXTENTS.NumberOfDiskExtents 成员中返回。 This C# code显示了类似的实现。

花时间编写像这样令人讨厌的代码是我基本上放弃使用 C# 开发 Windows 应用程序的原因。它造成了一个不可避免的悖论,即 C++ 本来应该更简洁、更优雅、更易于编写并且不易出错。 (你确定我没有在上面的代码中泄露任何句柄吗?我不是。)

关于c# - 带有 IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS 的 DeviceIoControl - C#,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37532548/

相关文章:

c# - 如何在 C# 中清除 Cursor.Clip 并允许光标再次自由移动?

c - 从 c 中的函数返回 SetConsoleTextAttribute 颜色值

winapi - 哪里是开始为 Windows 编程 GUI 的好地方?

c++ - Windows 精确计时:CreateTimerQueueTimer 或 system_clock::now() 出错?

javascript - 如何禁用 asp 按钮上的回发并仍然运行 OnClick 方法?

c# - 如何使用带有 ASP.Net 的 smtpClient 安全地发送我的凭据

c# - 获取不带扩展名的文件名

c# - System.Drawing 对象非托管资源处理

c++ - 子类化 Win32 时静态类行为异常

c++ - 执行前解析 STARTUPINFO hStdInput