Getting Drive information with Visual Basic

Wednesday Dec 7th 2016 by Hannes DuPreez
Share:

Learn the complicated way of getting disk information from your computer.

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.

Share:
Home
Mobile Site | Full Site
Copyright 2017 © QuinStreet Inc. All Rights Reserved