Search code examples
c#.netwindowsdiskdiskspace

Get Size of Disk and/or Drive With Unknown File Format, C# .NET Framework?


I want to be able to programmatically get the size of a physical disk, or the logical drives that are part of the physical disk.

Under normal conditions, getting logical drive size is easy. But, for the disk I am using, I intentionally have written raw data to it, and thus it is expected to have no known drive format.

Because of the drive format being unknown to Windows, Windows drive properties and .NET Framework's DriveInfo cannot tell me the size of the logical drive(s) that make up this physical disk. In DriveInfo foo, by format I mean foo.DriveFormat, foo.IsReady is false, and foo.TotalSize triggers an exception. In just right clicking the drive and getting the size manually via Windows' properties option, it shows 0 bytes for the drive size.

Preferably using C#, using .NET Framework 4.0 or earlier, on a Windows 7 system, how do I learn the size of a physical disk, or the logical drives that make up the physical disk, if the logical drives associated with it have no known file structure? Specifically physical disk size is preferable for my purposes.

I know there is a way in general using Windows 7, as I have seen applications give an accurate physical disk size of the same disk.


Solution

  • The solution ended up being fairly simple, and an extension from the existing question Getting Disk Size Properly

    Posting my solution here, as perhaps the full code will help someone in the future:
    (FYI: P/Invoke definitions obtained and minimally modified from http://pinvoke.net)

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern IntPtr CreateFile(
        [MarshalAs(UnmanagedType.LPTStr)] string filename,
        [MarshalAs(UnmanagedType.U4)] FileAccess access,
        [MarshalAs(UnmanagedType.U4)] FileShare share,
        IntPtr securityAttributes, // optional SECURITY_ATTRIBUTES struct or IntPtr.Zero
        [MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
        [MarshalAs(UnmanagedType.U4)] FileAttributes flagsAndAttributes,
        IntPtr templateFile);
    
    [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    static extern bool DeviceIoControl(IntPtr hDevice, uint dwIoControlCode,
        IntPtr lpInBuffer, uint nInBufferSize,
        IntPtr lpOutBuffer, uint nOutBufferSize,
        out uint lpBytesReturned, IntPtr lpOverlapped);
    
    [DllImport("kernel32.dll", SetLastError = true)]
    static extern bool CloseHandle(IntPtr hObject);
    
    struct GET_LENGTH_INFORMATION
    {
        public long Length;
    };
    long GetPhysDiskSize(string physDeviceID)
    {
        uint IOCTL_DISK_GET_LENGTH_INFO = 0x0007405C;
        uint dwBytesReturned;
    
        //Example, physDeviceID == @"\\.\PHYSICALDRIVE1"
        IntPtr hVolume = CreateFile(physDeviceID, FileAccess.ReadWrite,
            FileShare.None, IntPtr.Zero, FileMode.Open, FileAttributes.Normal, IntPtr.Zero);
    
        GET_LENGTH_INFORMATION outputInfo = new GET_LENGTH_INFORMATION();
        outputInfo.Length = 0;
    
        IntPtr outBuff = Marshal.AllocHGlobal(Marshal.SizeOf(outputInfo));
    
        bool devIOPass = DeviceIoControl(hVolume,
                            IOCTL_DISK_GET_LENGTH_INFO,
                            IntPtr.Zero, 0,
                            outBuff, (uint)Marshal.SizeOf(outputInfo),
                            out dwBytesReturned,
                            IntPtr.Zero);
    
        CloseHandle(hVolume);
    
        outputInfo = (GET_LENGTH_INFORMATION)Marshal.PtrToStructure(outBuff, typeof(GET_LENGTH_INFORMATION));
    
        Marshal.FreeHGlobal(hVolume);
        Marshal.FreeHGlobal(outBuff);
    
        return outputInfo.Length;
    }