[bgreco.net]: PS C:\> Copy-ItemGui
This function can be used to show a Windows Explorer progress dialog to display progress while copying large files and folders. Requires PowerShell 2.0 or higher.
Usage: mostly the same as Copy-Item
. Supports copying files and directories, giving them a new name if desired.
Supports the standard cmdlet parameters -WhatIf
, -Confirm
, and -Verbose
.
Limitations: It is up to Windows Explorer to determine whether a file copy will take long enough to display a progress dialog. For small files, no dialog will be shown. If multiple large files are passed as the source, a separate progress dialog will be shown for each file.
Disclaimer: As with Copy-Item
, careless use can result in loss of data. This function comes with absolutely no warranty.
function Copy-ItemGui { [CmdletBinding(SupportsShouldProcess=$true)] Param ( [Parameter(Position=0, Mandatory=$true)] [ValidateNotNullOrEmpty()] [String[]] $Path, [Parameter(Position=1, Mandatory=$true)] [ValidateNotNullOrEmpty()] [String] $Destination ) $items = @(Get-Item $Path) if($items -eq $null) { throw 'Source file or directory not found' } $dst_is_dir = $false # Copying to an existing directory or file if(Test-Path $Destination) { $dst = Get-Item $Destination if($dst.PSIsContainer) { $dst_dir = $dst.FullName $dst_is_dir = $true } else { # If copying and overwriting an existing file, only one source file is allowed if($items.Count -gt 1) { throw 'Multiple source files are not allowed when a file name is given as the destination.' } $dst_dir = $dst.DirectoryName $dst_name = $dst.Name } # Copying to a new file name in an existing directory } else { if($Destination.EndsWith('/') -or $Destination.EndsWith('\')) { throw "Destination directory '$Destination' does not exist." } # If copying to an existing directory and giving a destination file name, only one source file is allowed. if($items.Count -gt 1) { throw 'Multiple source files are not allowed when a file name is given as the destination.' } # Get directory name. If there is none, file is being copied to the current directory. $dst_dir = [System.IO.Path]::GetDirectoryName($Destination) if($dst_dir -eq '') { $dst_dir = '.' } $dst_name = [System.IO.Path]::GetFileName($Destination) if(-not ((Test-Path $dst_dir) -and (Get-Item $dst_dir).PSIsContainer)) { throw "Destination directory '$dst_dir' does not exist." } } $dst_dir = (Get-Item $dst_dir).FullName $shell = New-Object -ComObject "Shell.Application" $shell_dst = $shell.NameSpace($dst_dir) foreach($item in $items) { if($item.PSIsContainer) { $type = 'Directory' $parent_dir = $item.Parent.FullName } else { $type = 'File' $parent_dir = $item.DirectoryName } if($dst_is_dir) { $dst_name = $item.Name } # Copying an item to its own directory without giving a new name is not allowed if($parent_dir -eq $dst_dir -and $dst_name -eq $Item.Name) { Write-Error "Cannot copy '$($item.FullName)': Source and destination are the same" continue } if($pscmdlet.ShouldProcess("${type}: $item Destination: $dst_dir\$dst_name", "Copy $type")) { # If a file exists in the destination directory with the same name as the source file, # the shell copy will overwrite it. Fake a copy-rename operation by creating a temporary # directory, copy the file there, and then move it. if((Test-Path "$dst_dir\$($Item.Name)") -and $dst_name -ne $Item.Name) { do { $tmp_dir = "$dst_dir\" + [System.IO.Path]::GetRandomFileName() } while (Test-Path $tmp_dir) New-Item -ItemType Directory $tmp_dir | Out-Null $shell_tmp = $shell.NameSpace($tmp_dir) $shell_tmp.CopyHere($item.Fullname, 0x10) Move-Item -Force "$tmp_dir\$($Item.Name)" "$dst_dir\$dst_name" Remove-Item $tmp_dir } else { $shell_dst.CopyHere($item.Fullname, 0x10) if($item.Name -ne $dst_name) { Move-Item -Force "$dst_dir\$($Item.Name)" "$dst_dir\$dst_name" } } } } [System.Runtime.Interopservices.Marshal]::ReleaseComObject($shell) | Out-Null }