Getting Drive information with Visual Basic

Introduction

Ever wonder how things work? I do. It's a blessing, and a curse.... It is sad how we developers take everyday things for granted. Things such as getting volume information from hard disks or even just a list of drives on a computer may seem complicated, and yes, some elements may be difficult, but it is an essential, must-have skill to have at your disposal. Today, you will learn the complicated way of getting disk information from your computer.

Our Project

Design

Start a new Visual Basic Windows Forms project. Once the form is loaded, add a ListView object onto your form. You may name the ListView anything you like, but, as always, keep in mind that my object names may differ from yours. Your design should resemble Figure 1.

Drive1
Figure 1: Our Design

Code

Add a class to your project by selecting Project, Add Class. I have named mine clsVolume.

Add the following Namespaces to your class:

Imports Microsoft.Win32.SafeHandles
Imports System.IO
Imports System.Runtime.InteropServices
Imports System.ComponentModel

MSDN explains the preceding Namespaces in the following links:

Add the following variables to your class:

   Private Const intGenRead As Integer = &H80000000
   Private Const intShareRead As Integer = 1
   Private Const intShareWrite As Integer = 2
   Private Const intExisting As Integer = 3
   Private Const IoctlVolumeGetVolumeDiskExtents As _
      Integer = &H560000
   Private Const intIncorrectFunc As Integer = 1
   Private Const intInsufficientBuffer As Integer = 122

Add the following two Structures to clsVolume:

   <StructLayout(LayoutKind.Sequential)> _
      Private Structure DiskExtent
      Public DiskNumber As Integer
      Public StartingOffset As Long
      Public ExtentLength As Long
   End Structure

   <StructLayout(LayoutKind.Sequential)> _
      Private Structure DiskExtents
      Public intExtents As Integer
      Public intExtent As DiskExtent
   End Structure

These two structures will assist in getting the data in the correct format from the system APIs, which we will add next.

Add a subclass and the next few API Declarations to it:

   Private Class NativeMethods

      <DllImport("kernel32", CharSet:=CharSet.Unicode, _
         SetLastError:=True)> _
      Public Shared Function CreateFile( _
         ByVal strFileName As String, _
         ByVal intDesiredAccess As Integer, _
         ByVal intShareMode As Integer, _
         ByVal iptrSecurity As IntPtr, _
         ByVal intCreationDispos As Integer, _
         ByVal intFlags As Integer, _
         ByVal iptrTemplateFile As IntPtr) As SafeFileHandle
      End Function

      <DllImport("kernel32", SetLastError:=True)> _
      Public Shared Function DeviceIoControl( _
         ByVal hVol As SafeFileHandle, _
         ByVal controlCode As Integer, _
         ByVal inBuffer As IntPtr, _
         ByVal inBufferSize As Integer, _
         ByRef outBuffer As DiskExtents, _
         ByVal outBufferSize As Integer, _
         ByRef bytesReturned As Integer, _
         ByVal overlapped As IntPtr) As _
            <MarshalAs(UnmanagedType.Bool)> Boolean
      End Function

      <DllImport("kernel32", SetLastError:=True)> _
      Public Shared Function DeviceIoControl( _
         ByVal hVol As SafeFileHandle, _
         ByVal controlCode As Integer, _
         ByVal inBuffer As IntPtr, _
         ByVal inBufferSize As Integer, _
         ByVal outBuffer As IntPtr, _
         ByVal outBufferSize As Integer, _
         ByRef bytesReturned As Integer, _
         ByVal overlapped As IntPtr) As _
            <MarshalAs(UnmanagedType.Bool)> Boolean
      End Function
   End Class

Add the GetDriveStrings function to clsVolume:

   Public Function GetDriveStrings(ByVal diDriveInfo As DriveInfo) _
      As List(Of String)

      Dim sfhFile As SafeFileHandle = Nothing
      Dim lstPhysicalDrives As New List(Of String)(1)
      Dim strPath As String = "\\.\" & _
         diDriveInfo.RootDirectory.ToString.TrimEnd("\"c)

      Try

         sfhFile = NativeMethods.CreateFile(strPath, intGenRead, _
            intShareRead Or intShareWrite, IntPtr.Zero, _
            intExisting, 0, IntPtr.Zero)
         Dim intBytes As Integer
         Dim intDiskExtents As Integer = 0

         Dim deExtents As DiskExtents = Nothing

         Dim blnResult As Boolean = NativeMethods.DeviceIoControl(sfhFile, _
            IoctlVolumeGetVolumeDiskExtents, IntPtr.Zero, _
            0, deExtents, Marshal.SizeOf(deExtents), intBytes, IntPtr.Zero)
         If blnResult = True Then

            lstPhysicalDrives.Add("\\.\PhysicalDrive" & _
               deExtents.intExtent.DiskNumber.ToString)

            Return lstPhysicalDrives

         End If

         If Marshal.GetLastWin32Error = intIncorrectFunc Then

            Return lstPhysicalDrives

         End If

         Dim intSize As Integer = Marshal.SizeOf(GetType(DiskExtents)) + _
            (deExtents.intExtents - 1) * _
            Marshal.SizeOf(GetType(DiskExtent))

         Dim iptrBlob As IntPtr = Marshal.AllocHGlobal(intSize)

         blnResult = NativeMethods.DeviceIoControl(sfhFile, _
            IoctlVolumeGetVolumeDiskExtents, IntPtr.Zero, 0, _
            iptrBlob, intSize, intBytes, IntPtr.Zero)

         Dim iptrNext As New IntPtr(iptrBlob.ToInt32 + 4)

         For i As Integer = 0 To deExtents.intExtents - 1

            Dim dediskExtent As DiskExtent = _
               DirectCast(Marshal.PtrToStructure(iptrNext, _
               GetType(DiskExtent)), DiskExtent)

            lstPhysicalDrives.Add("\\.\PhysicalDrive" & _
               dediskExtent.DiskNumber.ToString)
            iptrNext = New IntPtr(iptrNext.ToInt32 + _
               Marshal.SizeOf(GetType(DiskExtent)))

         Next

         Return lstPhysicalDrives

      Finally

         If sfhFile IsNot Nothing Then

            If sfhFile.IsInvalid = False Then

               sfhFile.Close()

            End If

            sfhFile.Dispose()

         End If

      End Try

   End Function

This function simply makes use of the APIs to extract the volume information from all the disks on the system and add this information to a list which then gets returned to the calling function—which we will add in the form.

Add the following Namespaces to your form's code:

Imports HTG_HDStuff.clsVolume
Imports System.IO
Imports System.Text

Our class gets imported and becomes part of our form. Here is more information regarding the System.Text Namespace.

Create a new clsVolume object:

   Private cVolume As New clsVolume

Add the last piece of code to your Form's Load event:

   Private Sub Form1_Load(ByVal sender As System.Object, _
      ByVal e As System.EventArgs) Handles MyBase.Load

      For Each diDrives As DriveInfo In DriveInfo.GetDrives

         Dim lstDrives As List(Of String) = _
            cVolume.GetDriveStrings(diDrives)

         Dim sbDrives As New StringBuilder

         If lstDrives.Count > 0 Then

            For Each s As String In lstDrives

               sbDrives.Append(s)
               sbDrives.Append(", ")

            Next

               sbDrives.Remove(sbDrives.Length - 2, 2)

         Else

            sbDrives.Append("N / A")

         End If

         Dim lviItem As New _
            ListViewItem(diDrives.RootDirectory.ToString)

         lviItem.SubItems.Add(sbDrives.ToString)

         lvListView.Items.Add(lviItem)

      Next

   End Sub

This code makes use of the clsVolume class' GetDriveStrings function to return the obtained volume information to the ListView. Your running app should resemble Figure 2.

Drive2
Figure 2: Running your application

Conclusion

There are numerous ways to obtain disk information. This was just one of them, albeit somewhat more complicated than others.



About the Author

Hannes DuPreez

Hannes du Preez is a Microsoft MVP for Visual Basic for the ninth consecutive year. He loves technology and loves Visual Basic. He loves writing articles and proving that Visual Basic is more powerful than what most believe. His ultimate dream is to write a Visual Basic book, hopefully one day that dream will come true. You are most welcome to reach him at: ojdupreez1978@gmail.com

Related Articles

Comments

  • Just me

    Posted by MArk Nelson on 12/19/2016 03:34am

    Thanks for this post. I have been interested in how this is done using the current VB. I would like to learn a couple of other things and perhaps you can help. How does one program the positions of the icons on the desktop? Also, how can one know what windows are open and what their state (minimized, etc.) is? Thanks for any help.

    • Forums

      Posted by Hannes on 12/21/2016 09:39pm

      Hello Mark. Thanks for your good comments. The apt place to ask some questions would be on the CodeGuru forums: http://forums.codeguru.com/ Regards, Hannes

      Reply
    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date