我已经用C ++代码调用了需要转换为C#的IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS的DeviceIoControl。
我发现了许多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,并且数据结构始终为空。
任何帮助将是巨大的!
最佳答案
您的代码的问题在于,除非知道数组的大小,否则无法将其编组。这意味着在调用DiskExtents
时无法正确整理DeviceIoControl
结构。
以下代码对我有用(出于互操作的目的,我倾向于使用显式大小的类型,并且倾向于使用Win32标头中的名称,因为我已经知道它们的含义并且可以轻松地使用它们):
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
结构。第一次失败调用VOLUME_DISK_EXTENTS.NumberOfDiskExtents
后,所需的号码将在DeviceIoControl
成员中返回。 This C# code显示了类似的实现。花时间编写像这样的讨厌代码的原因是我在很大程度上放弃了用C#开发Windows应用程序的原因。它创造了不可避免的悖论,C ++会变得更干净,更优雅,更易于编写且出错率更低。 (您确定我没有泄漏上述代码中的任何句柄吗?我不是。)
关于c# - 具有IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS的DeviceIoControl-C#,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/37532548/