To do this you just need a handle to the drive (the device handle), and then use WriteFile to copy the image to the drive. Here's a rough example.
At the moment it rounds the byte array containing the image file up to the nearest 512 bytes, as otherwise it returns "invalid parameter".
Imports System.IO
Imports System.Text
Public Class Form1
Private sourceFile As FileInfo
Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Load
RefreshDrives()
End Sub
Private Sub btnBrowse_Click(ByVal sender As Object, ByVal e As EventArgs) _
Handles btnBrowse.Click
Using ofd As New OpenFileDialog
ofd.Title = "Select disk image file"
ofd.Multiselect = False
Dim result As DialogResult = ofd.ShowDialog
If result = Windows.Forms.DialogResult.OK Then
Try
sourceFile = New FileInfo(ofd.FileName)
Catch ex As Exception
MessageBox.Show(ex.Message)
sourceFile = Nothing
End Try
End If
End Using
If sourceFile IsNot Nothing Then
Me.TextBox1.Text = sourceFile.ToString
End If
RefreshBtn_Nuke()
End Sub
Private Sub btnRefresh_Click(ByVal sender As Object, ByVal e As EventArgs) _
Handles btnRefresh.Click
RefreshDrives()
End Sub
Private Sub RefreshDrives()
Me.ComboBox1.Items.Clear()
For Each di As DriveInfo In DriveInfo.GetDrives
If di.DriveType = DriveType.Removable Then
Me.ComboBox1.Items.Add(di)
End If
Next
Me.ComboBox1.SelectedIndex = 0
RefreshBtn_Nuke()
End Sub
Private Sub RefreshBtn_Nuke()
btnNuke.Enabled = (sourceFile IsNot Nothing) AndAlso _
Me.ComboBox1.Items.Count > 0
End Sub
Private Sub btnNuke_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btnNuke.Click
Dim result As DialogResult
Dim drive As String = Me.ComboBox1.Text
Dim sb As New StringBuilder
sb.AppendLine("Do you really want to erase drive " & drive & "?")
sb.AppendLine("Doing so will DESTROY ALL EXISTING DATA ON DRIVE " & drive & "!")
result = MessageBox.Show(sb.ToString, _
"Destroy Drive " & drive & " ?", _
MessageBoxButtons.YesNo, _
MessageBoxIcon.Exclamation, _
MessageBoxDefaultButton.Button2)
If result = Windows.Forms.DialogResult.Yes Then
Dim dest As DriveInfo = TryCast(Me.ComboBox1.SelectedItem, DriveInfo)
If sourceFile IsNot Nothing AndAlso dest IsNot Nothing Then
Dim written As Integer = Win32.CopyToDevice(sourceFile, dest)
MessageBox.Show("Wrote: " & written & " bytes to " & drive, "Done", _
MessageBoxButtons.OK, MessageBoxIcon.Information)
Application.Exit()
Else
RefreshBtn_Nuke()
End If
End If
End Sub
End Class
Option Strict On
Option Explicit On
Imports Microsoft.Win32.SafeHandles
Imports System.ComponentModel
Imports System.IO
Imports System.Runtime.InteropServices
Public Class Win32
Private Const GenericRead As UInteger = &H80000000UI
Private Const GenericWrite As UInteger = &H40000000
Private Const FileShareRead As UInteger = 1
Private Const Filesharewrite As UInteger = 2
Private Const OpenExisting As UInteger = 3
Private Const IoctlVolumeGetVolumeDiskExtents As UInteger = &H560000
Private Const IncorrectFunction As UInteger = 1
Private Const ErrorInsufficientBuffer As UInteger = 122
Private Class NativeMethods
<DllImport("kernel32", CharSet:=CharSet.Unicode, SetLastError:=True)> _
Public Shared Function CreateFile( _
ByVal fileName As String, _
ByVal desiredAccess As UInteger, _
ByVal shareMode As UInteger, _
ByVal securityAttributes As IntPtr, _
ByVal creationDisposition As UInteger, _
ByVal flagsAndAttributes As UInteger, _
ByVal hTemplateFile As IntPtr) As SafeFileHandle
End Function
<DllImport("kernel32", SetLastError:=True)> _
Public Shared Function DeviceIoControl( _
ByVal hVol As SafeFileHandle, _
ByVal controlCode As UInteger, _
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 UInteger, _
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
<DllImport("kernel32", SetLastError:=True)> _
Public Shared Function SetFilePointer( _
ByVal hFile As SafeFileHandle, _
ByVal lDistanceToMove As Integer, _
ByRef lpDistanceToMoveHigh As Integer, _
ByVal dwMoveMethod As Integer) As UInteger
End Function
' you must write the whole inBuffer.
<DllImport("kernel32", SetLastError:=True, CharSet:=CharSet.Ansi)> _
Public Shared Function WriteFile( _
ByVal hVol As SafeFileHandle, _
<MarshalAs(UnmanagedType.LPArray, SizeParamIndex:=3)> _
ByVal inBuffer() As Byte, _
ByVal numberOfBytesToWrite As Integer, _
ByRef numberOfBytesWritten As Integer, _
ByVal overlapped As IntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
End Class
' DISK_EXTENT in the msdn.
<StructLayout(LayoutKind.Sequential)> _
Private Structure DiskExtent
Public DiskNumber As Integer
Public StartingOffset As Long
Public ExtentLength As Long
End Structure
' DISK_EXTENTS
<StructLayout(LayoutKind.Sequential)> _
Private Structure DiskExtents
Public numberOfExtents As Integer
Public first As DiskExtent ' We can't marhsal an array if we don't know its size.
End Structure
Public Shared Function CopyToDevice(ByVal source As FileInfo, _
ByVal destination As DriveInfo) As Integer
Dim physicalDrives As List(Of String) = GetPhysicalDriveStrings(destination)
If physicalDrives.Count > 1 Then
Throw New Exception("The volume spans multiple disks!")
End If
If physicalDrives.Count = 0 Then
Throw New Exception("Could not find the physical drive!")
End If
Dim physicalDrive As String = physicalDrives(0)
Dim sourceFile As String = source.FullName
Dim driveHandle As SafeFileHandle = Nothing
Try
driveHandle = NativeMethods.CreateFile( _
physicalDrive, GenericRead Or GenericWrite, _
FileShareRead Or Filesharewrite, _
IntPtr.Zero, OpenExisting, 0, IntPtr.Zero)
Dim bytes() As Byte = My.Computer.FileSystem.ReadAllBytes(source.FullName)
Dim remainder As Integer = 512 - (bytes.Length Mod 512)
If remainder < 512 Then
ReDim Preserve bytes(bytes.Length + remainder - 1)
End If
Dim written As Integer = 0
NativeMethods.SetFilePointer(driveHandle, 0, 0, 0)
Dim result As Boolean = NativeMethods.WriteFile(driveHandle, _
bytes, bytes.Length, written, IntPtr.Zero)
If result = False Then Throw New Win32Exception
Return written
Finally
If driveHandle IsNot Nothing Then
If driveHandle.IsInvalid = False Then
If driveHandle.IsClosed = False Then
driveHandle.Close()
End If
End If
driveHandle.Dispose()
End If
End Try
End Function
' A Volume could be on many physical drives.
' Returns a list of string containing each physical drive the volume uses.
' For CD Drives with no disc in it will return an empty list.
Public Shared Function GetPhysicalDriveStrings(ByVal driveInfo As DriveInfo) As List(Of String)
Dim sfh As SafeFileHandle = Nothing
Dim physicalDrives As New List(Of String)(1)
Dim path As String = "\\.\" & driveInfo.RootDirectory.ToString.TrimEnd("\"c)
Try
sfh = NativeMethods.CreateFile(path, GenericRead, FileShareRead Or Filesharewrite, _
IntPtr.Zero, OpenExisting, 0, IntPtr.Zero)
Dim bytesReturned As Integer
Dim de1 As DiskExtents = Nothing
Dim numDiskExtents As Integer = 0
Dim result As Boolean = NativeMethods.DeviceIoControl(sfh, _
IoctlVolumeGetVolumeDiskExtents, IntPtr.Zero, 0, de1, Marshal.SizeOf(de1), _
bytesReturned, IntPtr.Zero)
If result = True Then
' there was only one disk extent. So the volume lies on 1 physical drive.
physicalDrives.Add("\\.\PhysicalDrive" & de1.first.DiskNumber.ToString)
Return physicalDrives
End If
If Marshal.GetLastWin32Error = IncorrectFunction Then
' The drive is removable and removed, like a CDRom with nothing in it.
Return physicalDrives
End If
If Marshal.GetLastWin32Error <> ErrorInsufficientBuffer Then
Throw New Win32Exception
End If
' The volume is on multiple disks.
' Untested...
' We need a blob of memory for the DISK_EXTENTS structure, and all the DISK_EXTENTS
Dim blobSize As Integer = Marshal.SizeOf(GetType(DiskExtents)) + _
(de1.numberOfExtents - 1) * _
Marshal.SizeOf(GetType(DiskExtent))
Dim pBlob As IntPtr = Marshal.AllocHGlobal(blobSize)
result = NativeMethods.DeviceIoControl(sfh, IoctlVolumeGetVolumeDiskExtents, _
IntPtr.Zero, 0, pBlob, blobSize, bytesReturned, IntPtr.Zero)
If result = False Then Throw New Win32Exception
' Read them out one at a time.
Dim pNext As New IntPtr(pBlob.ToInt32 + 4) ' is this always ok on 64 bit OSes? ToInt64?
For i As Integer = 0 To de1.numberOfExtents - 1
Dim diskExtentN As DiskExtent = DirectCast(Marshal.PtrToStructure(pNext, _
GetType(DiskExtent)), DiskExtent)
physicalDrives.Add("\\.\PhysicalDrive" & diskExtentN.DiskNumber.ToString)
pNext = New IntPtr(pNext.ToInt32 + Marshal.SizeOf(GetType(DiskExtent)))
Next
Return physicalDrives
Finally
If sfh IsNot Nothing Then
If sfh.IsInvalid = False Then
sfh.Close()
End If
sfh.Dispose()
End If
End Try
End Function
End Class