This code was downloaded from MOBZystems, Home of Tools. No license, use at will. And at your own risk!
Option Strict On
Option Explicit On
Imports System.Collections.Generic
Imports System.IO
Imports System.Security.Cryptography
'''
''' Console application to display hash values for file contents.
''' Also supports finding and displaying files with identical hashes
'''
'''
''' Return values:
'''
''' 0 - OK
''' 1- Argument error
''' 2 - Run time error
'''
Module MOBZHash
Function Main(arguments() As String) As Integer
' Construct a title
Dim title As String = String.Format(
"{0} v{1} ({2}-bit) by MOBZystems - http://www.mobzystems.com/Tools/MOBZHash",
My.Application.Info.Title,
My.Application.Info.Version.ToString(3),
IntPtr.Size * 8
)
Try
' If no arguments specified, print usage
If arguments.Length = 0 Then
Console.WriteLine(title)
Console.WriteLine()
Console.WriteLine("Usage:")
Console.WriteLine()
Console.WriteLine("MOBZHash [-v] [-r] [-d] hash-type [file-spec [file-spec ...]]")
Console.WriteLine()
Console.WriteLine("hash-type: hash type to use. All .NET hash algorihms are supported")
Console.WriteLine(" Examples: MD5, SHA1 (default), SHA256, SHA384, SHA512")
Console.WriteLine("file-spec: A file name or wildcard. Defaults to all files in current directory")
Console.WriteLine()
Console.WriteLine("Switches:")
Console.WriteLine("-v: verbose")
Console.WriteLine("-r: recursive")
Console.WriteLine("-d: find duplicates. Display a list of all files with an identical hash")
' No error
Return 0
End If
' Parse the command line
' The first non-switch argument is the hash name
Dim hashName As String = Nothing
' List of file specs
Dim fileSpecs As New List(Of String)
' List of switches
Dim switches As New List(Of String)
' Separate switches from arguments, isolate hash name
For Each arg As String In arguments
If arg.StartsWith("-") OrElse arg.StartsWith("/") Then
switches.Add(arg.Substring(1).ToUpperInvariant())
ElseIf hashName Is Nothing Then
hashName = arg.ToUpperInvariant()
Else
fileSpecs.Add(arg)
End If
Next
' Use SHA1 if no hash specified
If hashName Is Nothing Then
hashName = "SHA1"
End If
' See if we can create a hash algorithm for the specified hash name
Dim hasher As HashAlgorithm = Nothing
Try
hasher = DirectCast(CryptoConfig.CreateFromName(hashName), HashAlgorithm)
Catch ex As Exception
' We get an exception if the hash exists, but is not a hash algorithm (e.g. RSA)
Console.Error.WriteLine(
String.Format("Invalid hash type '{0}'. Valid hash types are MD5, SHA1, " +
"SHA256, SHA256, SHA384, SHA512", hashName))
' Argument error
Return 1
End Try
' Did we fail silently? (Hash name is unknown)
If hasher Is Nothing Then
Console.Error.WriteLine(String.Format("Uknown hash type '{0}'", hashName))
' Argument error
Return 1
End If
' Parse the switches:
Dim verbose As Boolean = False
Dim recursive As Boolean = False
Dim duplicates As Boolean = False
For Each switch As String In switches
Select Case switch
Case "V"
verbose = True
Case "R"
recursive = True
Case "D"
duplicates = True
Case Else
Console.Error.WriteLine("Invalid switch '{0}'", switch)
' Argument error
Return 1
End Select
Next
' Process all file specifications
' Provide a default file spec if we didn't supply one
If fileSpecs.Count = 0 Then
fileSpecs.Add("*.*")
End If
' Build a dictionary of hashes if we are looking for duplicates
Dim hashes As Dictionary(Of String, List(Of String)) = Nothing
If duplicates Then
hashes = New Dictionary(Of String, List(Of String))
End If
If verbose Then
Console.WriteLine(title)
Console.WriteLine()
End If
' Loop over the file specs
For Each fileSpec In fileSpecs
Dim pathName As String
Dim fileMask As String
' Did we specify a directory?
If IO.Directory.Exists(fileSpec) Then
' Use the name of the directory
pathName = fileSpec
' and a file mask of *.*
fileMask = "*.*"
Else
' Separate directory from file mask
pathName = Path.GetDirectoryName(fileSpec)
fileMask = Path.GetFileName(fileSpec)
' Did we spcify a file name only?
If String.IsNullOrEmpty(pathName) Then
' Use the current directory
pathName = Environment.CurrentDirectory
End If
End If
' Show some text in verbose mode
If verbose Then
Console.WriteLine(
String.Format(
"{0} {1} hash for {2}{3}...",
If(duplicates, "Finding duplicates by", "Computing"),
hashName,
fileSpec,
If(recursive, " and subdirectories", "")
)
)
End If
' Find all files corresponding to the file mask in the specified path,
' possibly recursive
Dim dir As New DirectoryInfo(pathName)
Dim files() As FileInfo = Nothing
Try
files = dir.GetFiles(
fileMask,
If(recursive, SearchOption.AllDirectories, SearchOption.TopDirectoryOnly)
)
Catch ex As Exception
Console.Error.WriteLine(
String.Format(
"Could not access '{0}': {1}",
pathName,
ex.Message
)
)
End Try
If files IsNot Nothing Then
' Process all files
For Each file As FileInfo In files
' Skip hidden files
If (file.Attributes And FileAttributes.Hidden) = 0 Then
' Set up the file name to display
Dim relativeFileName As String = file.FullName.Substring(dir.FullName.Length + 1)
' Use the relative name for display purposes if we have a single file spec.
' Otherwise, use the full name of the file
Dim displayName As String = relativeFileName
If fileSpecs.Count > 1 Then
displayName = file.FullName
End If
Try
Using stream As Stream = System.IO.File.OpenRead(file.FullName)
' Compute the hash on the stream of the file
Dim hash As Byte() = hasher.ComputeHash(stream)
' Convert the hash to a string
Dim hashString As String = BitConverter.ToString(hash)
' Are we collecting duplicates?
If duplicates Then
' Store the hash and the file name in the hashes
Dim fileList As List(Of String) = Nothing
' Do we know this hash?
If hashes.TryGetValue(hashString, fileList) Then
' Add this file if it isn't in the list already (case insensitive!)
If Not fileList.Contains(
displayName,
StringComparer.InvariantCultureIgnoreCase
) Then
fileList.Add(displayName)
End If
Else
' New hash - create a new file list
fileList = New List(Of String)
' Add this file
fileList.Add(displayName)
' Store in hashes
hashes.Add(hashString, fileList)
End If
Else
' Not finding duplicates - simply list hash and file name
Console.WriteLine(String.Format("{0}: {1}", hashString, displayName))
End If
End Using
Catch ex As Exception
Console.Error.WriteLine(
String.Format(
"Error computing {0} hash for file '{1}': {2}",
hashName,
displayName,
ex.Message
)
)
End Try
End If ' Not hidden
Next file
End If ' If no error getting files
Next fileSpec
' We're done processing all file specs.
' For duplicates, display all lists containing more than one item:
If duplicates Then
' Loop over all hashes
For Each hashString As String In hashes.Keys
' Get the list of files with this hash
Dim fileList As List(Of String) = hashes(hashString)
' More than one? Display them
If fileList.Count > 1 Then
Console.WriteLine("{0} {1}: {2} files", hashName, hashString, fileList.Count)
For Each filename In fileList
Console.WriteLine(String.Format("- {0}", filename))
Next
End If
Next
End If
' OK
Return 0
Catch ex As Exception
Console.Error.WriteLine(
String.Format(
"Hash: Generic error: {0}",
ex.Message
)
)
' Run-time eror
Return 2
End Try
End Function
End Module