DeviceIoControl does not set output buffer

一曲冷凌霜 提交于 2019-12-24 04:13:07

问题


I'm having some problems with DeviceIOControl. I'm trying to read the disk geometry from a physical drive but the output buffer is never set.

Here is a sample of my code, a simple function that is supposed to get the disk geometry and return whether the disk is removable:

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;
}

And some data structures:

[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;
}

The enums used I got from

  • pinvoke.net: createfile (kernel32)
  • pinvoke.net: deviceiocontrol (kernel32)

The bytesReturned variable gets set (to 24 in this case) but the dg variable is all zeroes.

I have tried several other functions of DeviceIoControl with the same result: bytesReturned is set but the output buffer is all zeroes.

My testing environment

  • OS: Windows 7 Pro 64
  • IDE: Visual Studio 2010
  • Target Framework: .Net 4.0

回答1:


The DISK_GEOMETRY declaration is the problem. It is being marshaled as an object but it was declared as a struct. That means it will be boxed, only the boxed copy will be updated by the pinvoke call. You are reading the original value and thus get all zeros.

The simple fix is to declare it as a class instead of a struct.



来源:https://stackoverflow.com/questions/13192616/deviceiocontrol-does-not-set-output-buffer

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!