Backporting the Get-FileHash function

Not later than this morning, my colleague needed to check the checksum of a file but didn’t got any PowerShell script to easily get it but got the 11-years old fciv.exe instead.

To get rid of the fciv.exe utility, there’s actually a Get-FileHash function available as of PowerShell version 4.0 built-in the Microsoft.PowerShell.Utility module.

Good news! As it’s a function, it’s directly accessible from the Microsoft.PowerShell.Utility.psm1 file and the effort to make it work on PowerShell version 2.0 on Windows 7 isn’t as monumental as we can think.

The first step is to open the module on a Windows 10 Technical Preview in the ISE to copy the first 2 functions Get-FileHash and GetStreamHash.

and paste them into the ISE of a PowerShell version 2.0

NB: We can see that the syntax highlighting is broken.

The second step consists in making minor changes to make these functions compatible with PowerShell version 2.0

  • On lines 5,9 and 14: replace the Mandatory statement by Mandatory = $true.
    It will restore the syntax highlighting and avoid the following error message
  • # from
    [Parameter(Mandatory, ParameterSetName="Path", Position = 0)]
    # to
    [Parameter(Mandatory=$true, ParameterSetName="Path", Position = 0)]
    
  • On line 3, remove the HelpURI from the CmdletBinding statement and avoid the following error message
  • # from
    [CmdletBinding(DefaultParameterSetName = "Path", HelpURI = "http://go.microsoft.com/fwlink/?LinkId=517145")]
    # to
    [CmdletBinding(DefaultParameterSetName = "Path")]
    
  • On lines 40 and 44, fix the Foreach-Object statement and avoid the following error message
  • # from
    $pathsToProcess += Resolve-Path $Path | 
    Foreach-Object ProviderPath
    # to
    $pathsToProcess += Resolve-Path $Path | 
    Foreach-Object { $_.ProviderPath}
    
  • On line 62, remove the type PowerShell isn’t aware of when the file doesn’t exist
  • # from
    $errorMessage = [Microsoft.PowerShell.Commands.UtilityResources]::FileReadError -f $FilePath, $_
    # to
    $errorMessage = 'FileReadError {0}:{1}' -f $FilePath, $_
    

That’s all folks 😎

You can see the result (stored as gist file)

function Get-FileHash
{
[CmdletBinding(DefaultParameterSetName = "Path")]
param(
[Parameter(Mandatory=$true, ParameterSetName="Path", Position = 0)]
[System.String[]]
$Path,
[Parameter(Mandatory=$true, ParameterSetName="LiteralPath", ValueFromPipelineByPropertyName = $true)]
[Alias("PSPath")]
[System.String[]]
$LiteralPath,
[Parameter(Mandatory=$true, ParameterSetName="Stream")]
[System.IO.Stream]
$InputStream,
[ValidateSet("SHA1", "SHA256", "SHA384", "SHA512", "MACTripleDES", "MD5", "RIPEMD160")]
[System.String]
$Algorithm="SHA256"
)
begin
{
# Construct the strongly-typed crypto object
$hasher = [System.Security.Cryptography.HashAlgorithm]::Create($Algorithm)
}
process
{
if($PSCmdlet.ParameterSetName -eq "Stream")
{
GetStreamHash -InputStream $InputStream -RelatedPath $null -Hasher $hasher
}
else
{
$pathsToProcess = @()
if($PSCmdlet.ParameterSetName -eq "LiteralPath")
{
$pathsToProcess += Resolve-Path -LiteralPath $LiteralPath | Foreach-Object { $_.ProviderPath }
}
if($PSCmdlet.ParameterSetName -eq "Path")
{
$pathsToProcess += Resolve-Path $Path | Foreach-Object { $_.ProviderPath }
}
foreach($filePath in $pathsToProcess)
{
if(Test-Path -LiteralPath $filePath -PathType Container)
{
continue
}
try
{
# Read the file specified in $FilePath as a Byte array
[system.io.stream]$stream = [system.io.file]::OpenRead($filePath)
GetStreamHash -InputStream $stream -RelatedPath $filePath -Hasher $hasher
}
catch [Exception]
{
$errorMessage = 'FileReadError {0}:{1}' -f $FilePath, $_
Write-Error -Message $errorMessage -Category ReadError -ErrorId "FileReadError" -TargetObject $FilePath
return
}
finally
{
if($stream)
{
$stream.Close()
}
}
}
}
}
}
function GetStreamHash
{
param(
[System.IO.Stream]
$InputStream,
[System.String]
$RelatedPath,
[System.Security.Cryptography.HashAlgorithm]
$Hasher)
# Compute file-hash using the crypto object
[Byte[]] $computedHash = $Hasher.ComputeHash($InputStream)
[string] $hash = [BitConverter]::ToString($computedHash) -replace '-',''
if ($RelatedPath -eq $null)
{
$retVal = [PSCustomObject] @{
Algorithm = $Algorithm.ToUpperInvariant()
Hash = $hash
}
$retVal.psobject.TypeNames.Insert(0, "Microsoft.Powershell.Utility.FileHash")
$retVal
}
else
{
$retVal = [PSCustomObject] @{
Algorithm = $Algorithm.ToUpperInvariant()
Hash = $hash
Path = $RelatedPath
}
$retVal.psobject.TypeNames.Insert(0, "Microsoft.Powershell.Utility.FileHash")
$retVal
}
}
view raw Get-FileHash.ps1 hosted with ❤ by GitHub

3 thoughts on “Backporting the Get-FileHash function

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.