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.

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.

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.







Comments
Just me
Posted by MArk Nelson on 12/19/2016 03:34amThanks 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.
Reply