首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >DeviceIoControl与IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS - C#

DeviceIoControl与IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS - C#
EN

Stack Overflow用户
提问于 2016-05-30 19:31:23
回答 1查看 2K关注 0票数 3

我有C++代码,它用IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS调用DeviceIoControl,需要转换成C#。

我已经找到了许多DeviceIoControl p\invoke示例,但是没有针对这个特定标志的示例。我试过使用以下方法:

代码语言:javascript
复制
[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);

结构:

代码语言:javascript
复制
[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;
}

以及以下呼叫代码:

代码语言:javascript
复制
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,数据结构始终是空的。任何帮助都会很好!

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2016-05-31 11:18:19

代码的问题是,除非知道数组的大小,否则不能对其进行封送。这意味着在进行DiskExtents调用时不能正确地封送DeviceIoControl结构。

以下代码适用于我(为了互操作目的,我倾向于使用显式大小的类型,并且更喜欢使用来自Win32头的名称,因为我已经知道它们的含义,并且可以轻松地搜索它们):

代码语言:javascript
复制
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);
}
代码语言:javascript
复制
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结构。在第一次调用VOLUME_DISK_EXTENTS.NumberOfDiskExtents失败后,将在DeviceIoControl成员中返回所需的号码。这个C#代码展示了类似的实现。

我花了这么长时间编写这种讨厌的代码,所以我基本上放弃了在C#中开发Windows应用程序。它创造了一个不可避免的悖论: C++本来会更干净、更优雅、更易于编写,并且更容易出错。(你确定我没有泄露上述代码中的任何句柄吗?)我没有。)

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/37532548

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档