2

我遇到了一些问题DeviceIOControl。我正在尝试从物理驱动器读取磁盘几何形状,但从未设置输出缓冲区。

这是我的代码示例,它是一个简单的函数,应该获取磁盘几何形状并返回磁盘是否可移动:

public static bool IsDeviceRemovable(int DriveNo) {
    string Filename=@"\\.\Physicaldrive"+DriveNo;

    SafeFileHandle drive=CreateFile(
        Filename,
        FileAccess_e.None,
        FileShare_e.Write|FileShare_e.Read,
        IntPtr.Zero,
        CreationDisposition_e.OpenExisting,
        FileAttributes_e.None,
        IntPtr.Zero
    );

    if(drive.IsInvalid) {
        throw new IOException("Unable to access drive. Win32 Error Code: "+Marshal.GetLastWin32Error());
    }

    uint bytesReturned=0;
    DISK_GEOMETRY dg=new DISK_GEOMETRY();

    if(!DeviceIoControl(
        drive,
        IOControlCode_e.DiskGetDriveGeometry,
        IntPtr.Zero,
        0,
        dg,
        (uint)Marshal.SizeOf(dg),
        ref bytesReturned,
        IntPtr.Zero
        )) {
        throw new Exception("Unable to get properties from device. Win32 Error Code: "+Marshal.GetLastWin32Error());
    }

    drive.Close();
    return dg.MediaType==MEDIA_TYPE.RemovableMedia;
}

还有一些数据结构:

[DllImport("Kernel32.dll", SetLastError=false, CharSet=CharSet.Auto)]
private static extern bool DeviceIoControl(
    SafeFileHandle hDevice,
    IOControlCode_e IoControlCode,
    [MarshalAs(UnmanagedType.AsAny)]
    [In] object InBuffer,
    uint nInBufferSize,
    [MarshalAs(UnmanagedType.AsAny)]
    [Out] object OutBuffer,
    uint nOutBufferSize,
    ref uint pBytesReturned,
    IntPtr Overlapped
    );

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

private enum MEDIA_TYPE {
    Unknown=0x00,
    F5_1Pt2_512=0x01,
    F3_1Pt44_512=0x02,
    F3_2Pt88_512=0x03,
    F3_20Pt8_512=0x04,
    F3_720_512=0x05,
    F5_360_512=0x06,
    F5_320_512=0x07,
    F5_320_1024=0x08,
    F5_180_512=0x09,
    F5_160_512=0x0a,
    RemovableMedia=0x0b,
    FixedMedia=0x0c,
    F3_120M_512=0x0d,
    F3_640_512=0x0e,
    F5_640_512=0x0f,
    F5_720_512=0x10,
    F3_1Pt2_512=0x11,
    F3_1Pt23_1024=0x12,
    F5_1Pt23_1024=0x13,
    F3_128Mb_512=0x14,
    F3_230Mb_512=0x15,
    F8_256_128=0x16,
    F3_200Mb_512=0x17,
    F3_240M_512=0x18,
    F3_32M_512=0x19
}

[StructLayout(LayoutKind.Sequential)]
private struct DISK_GEOMETRY {
    public Int64 Cylinders;
    public MEDIA_TYPE MediaType;
    public Int32 TracksPerCylinder;
    public Int32 SectorsPerTrack;
    public Int32 BytesPerSector;
}

我使用的枚举来自

bytesReturned变量被设置(在本例中为 24),但变量dg全为零。

我尝试了其他几个DeviceIoControl具有相同结果的函数:bytesReturned已设置但输出缓冲区全为零。

我的测试环境

  • 操作系统:Windows 7 Pro 64
  • IDE:Visual Studio 2010
  • 目标框架:.Net 4.0
4

1 回答 1

3

DISK_GEOMETRY 声明是问题所在。它被编组为一个对象,但它被声明为一个结构。这意味着它将被装箱,只有装箱的副本会被 pinvoke 调用更新。您正在读取原始值,因此得到全零。

简单的解决方法是将其声明为而不是结构。

于 2012-11-02T10:19:31.430 回答