The code below, or very similar variations, seem to be a very widespread recommendation to adding files to a zip archive in PowerShell:
$fileName = 'C:\Temp\test.zip' $source = 'C:\Temp\reallylargefile.txt' Set-Content $fileName ('PK' + [char]5 + [char]6 + ("$([char]0)" * 18)); $oShell = New-Object -ComObject Shell.Application; $zipPackage = $oShell.NameSpace($fileName); $zipPackage.CopyHere($source);
There’s nothing particularly wrong with this code — it’s easy, it’s short, and it’s effective. However, a number of scripters out there have noticed that there’s a small problem: The CopyHere method returns immediately, rather than waiting for the zip process to complete. If your script wants to do something with that zip file, it’s got to find a way to know when the zip is free to be moved, renamed, copied, and so on.
Fortunately, there’s a way to do that. Here’s the solution I use:
function Test-FileLock { param ( [parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] [Alias("FullName")] [string]$Path, [switch]$PassThru ) begin {} Process { if ((Test-Path -Path $Path) -eq $false) { throw [System.IO.FileNotFoundException] return } $oFile = Get-Item $Path if ($oFile.PSIsContainer) { return } try { $oStream = $oFile.Open([System.IO.FileMode]::Open, [System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::None) if ($oStream) { $oStream.Close() } if ($PassThru.IsPresent -eq $false) { $false } else { Get-Item $Path } } catch { # file is locked by a process. if ($PassThru.IsPresent -eq $false) { $true } } } end {} }
The function above can be handy for any instance in which you need to know whether a file is being held open by another process. It just so happens that one of those instances is when waiting for Explorer to finish dumping files into a zip archive! Armed with the example above, you can easily wait for the zip file to become available:
$zipPackage.CopyHere($source); Start-Sleep -Seconds 2 while (Test-FileLock $fileName) { Start-Sleep -Seconds 2 }
Note that while the default for the Test-FileLock function is to return $true or $false, I added a -PassThru parameter that changes the output to either a FileInfo object or $null. This alternate behavior allows you pass unlocked files down the pipeline to be used by other cmdlets.
Recent Comments