PowerShell Function to Unzip Files Using the .NET Framework 4.5 with Fallback to COM

A few days ago, I saw a tweet about someone needing to extract a zip file with PowerShell and wanting to accomplish the task without loading any third party extensions so I thought I would write a function to accomplish the task.

I start out by declaring the function followed by its name, it includes comment based help which is collapsed in the following image, then the [CmdletBinding()] attribute is specified to define this function as an advanced function. Then the param block is opened which is where the parameters are defined:

unzip-file2.png

The first parameter is File which is for the complete path including the name of the zip file. This parameter is mandatory and is also accepted via the pipeline. ValidateScript is used to make sure the value provided is a valid leaf object and that it also at least ends in .zip otherwise an exception is thrown:

unzip-file3.png

The second parameter is for the Destination folder path of where to extract the items contained in the zip file. [ValidateNotNullOrEmpty()] prevents the value specified for this parameter from being just that (the input cannot be Null or Empty)`. The path is validated to be a container otherwise an exception is thrown. If this parameter is not specified, the current path is used as the default value:

unzip-file4.png

The last parameter is a switch parameter which means it's on or off (true or false). This parameter will be used to force the use of COM for the extraction process. Lastly, the param block is closed with a closing parenthesis:

unzip-file5.png

There's a lot going on in the If block. First, $ForceCOM is checked to make sure it wasn't specified, then whether or not PowerShell version 3 or greater is being used is checked, and either the full or client profile version of the .NET Framework 4.5 must be present so that's a total of three things that have to evaluate to true in order for the code inside the If block to be executed:

unzip-file6a.png

If the user of this function specified the $ForceCOM switch parameter, or if a version of PowerShell less than version 3 is being used (PowerShell version 2 would generate an error with the code that's in the If block) or if the .NET Framework 4.5 isn't installed on the machine this function is being run on, the else block will be executed which uses COM to perform the zip file extraction process:

unzip-file7.png

  1function Unzip-File {
  2
  3<#
  4.SYNOPSIS
  5   Unzip-File is a function which extracts the contents of a zip file.
  6
  7.DESCRIPTION
  8   Unzip-File is a function which extracts the contents of a zip file specified via the -File parameter to the
  9location specified via the -Destination parameter. This function first checks to see if the .NET Framework 4.5
 10is installed and uses it for the unzipping process, otherwise COM is used.
 11
 12.PARAMETER File
 13    The complete path and name of the zip file in this format: C:\zipfiles\myzipfile.zip
 14
 15.PARAMETER Destination
 16    The destination folder to extract the contents of the zip file to. If a path is no specified, the current path
 17is used.
 18
 19.PARAMETER ForceCOM
 20    Switch parameter to force the use of COM for the extraction even if the .NET Framework 4.5 is present.
 21
 22.EXAMPLE
 23   Unzip-File -File C:\zipfiles\AdventureWorks2012_Database.zip -Destination C:\databases\
 24
 25.EXAMPLE
 26   Unzip-File -File C:\zipfiles\AdventureWorks2012_Database.zip -Destination C:\databases\ -ForceCOM
 27
 28.EXAMPLE
 29   'C:\zipfiles\AdventureWorks2012_Database.zip' | Unzip-File
 30
 31.EXAMPLE
 32    Get-ChildItem -Path C:\zipfiles | ForEach-Object {$_.fullname | Unzip-File -Destination C:\databases}
 33
 34.INPUTS
 35   String
 36
 37.OUTPUTS
 38   None
 39
 40.NOTES
 41   Author:  Mike F Robbins
 42   Website: http://mikefrobbins.com
 43   Twitter: @mikefrobbins
 44
 45#>
 46
 47    [CmdletBinding()]
 48    param (
 49        [Parameter(Mandatory=$true,
 50                   ValueFromPipeline=$true)]
 51        [ValidateScript({
 52            If ((Test-Path -Path $_ -PathType Leaf) -and ($_ -like "*.zip")) {
 53                $true
 54            }
 55            else {
 56                Throw "$_ is not a valid zip file. Enter in 'c:\folder\file.zip' format"
 57            }
 58        })]
 59        [string]$File,
 60
 61        [ValidateNotNullOrEmpty()]
 62        [ValidateScript({
 63            If (Test-Path -Path $_ -PathType Container) {
 64                $true
 65            }
 66            else {
 67                Throw "$_ is not a valid destination folder. Enter in 'c:\destination' format"
 68            }
 69        })]
 70        [string]$Destination = (Get-Location).Path,
 71
 72        [switch]$ForceCOM
 73    )
 74
 75    If (-not $ForceCOM -and ($PSVersionTable.PSVersion.Major -ge 3) -and
 76       ((Get-ItemProperty -Path "HKLM:\Software\Microsoft\NET Framework Setup\NDP\v4\Full" -ErrorAction SilentlyContinue).Version -like "4.5*" -or
 77       (Get-ItemProperty -Path "HKLM:\Software\Microsoft\NET Framework Setup\NDP\v4\Client" -ErrorAction SilentlyContinue).Version -like "4.5*")) {
 78
 79        Write-Verbose -Message "Attempting to Unzip $File to location $Destination using .NET 4.5"
 80
 81        try {
 82            [System.Reflection.Assembly]::LoadWithPartialName("System.IO.Compression.FileSystem") | Out-Null
 83            [System.IO.Compression.ZipFile]::ExtractToDirectory("$File", "$Destination")
 84        }
 85        catch {
 86            Write-Warning -Message "Unexpected Error. Error details: $_.Exception.Message"
 87        }
 88
 89    }
 90    else {
 91
 92        Write-Verbose -Message "Attempting to Unzip $File to location $Destination using COM"
 93
 94        try {
 95            $shell = New-Object -ComObject Shell.Application
 96            $shell.Namespace($destination).copyhere(($shell.NameSpace($file)).items())
 97        }
 98        catch {
 99            Write-Warning -Message "Unexpected Error. Error details: $_.Exception.Message"
100        }
101
102    }
103
104}

µ