Functions/GenXdev.Windows/Get-ClipboardFiles.ps1
############################################################################### <# .SYNOPSIS Gets files from the Windows clipboard that were set for file operations like copy/paste. .DESCRIPTION This function retrieves file paths from the Windows clipboard that were previously set for file operations. It handles both STA and MTA threading modes automatically, ensuring compatibility across different PowerShell execution contexts. The function validates file existence and returns only existing files/directories as objects similar to Get-ChildItem or Get-Item output. .EXAMPLE Get-ClipboardFiles Gets all files currently in the clipboard and returns them as file system objects. .EXAMPLE $clipboardFiles = Get-ClipboardFiles $clipboardFiles | ForEach-Object { Write-Host $_.FullName } Gets clipboard files and displays their full paths. .EXAMPLE Get-ClipboardFiles | Where-Object { $_.Extension -eq '.txt' } Gets only text files from the clipboard. .EXAMPLE Get-ClipboardFiles | ls Gets all files from the clipboard and lists them in a detailed format. #> function Get-ClipboardFiles { [CmdletBinding()] [Alias("getclipfiles")] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '')] param () # get current thread apartment state for clipboard compatibility $apartmentState = [System.Threading.Thread]::CurrentThread.GetApartmentState() # initialize collection for file paths $clipboardFilePaths = @() # check if running in single-threaded apartment mode if ($apartmentState -eq 'STA') { # load the system.windows.forms assembly for clipboard operations Microsoft.PowerShell.Utility\Add-Type -AssemblyName System.Windows.Forms # output verbose information about direct clipboard operation Microsoft.PowerShell.Utility\Write-Verbose ` "Getting clipboard files directly in STA mode" try { # get file drop list from clipboard in sta mode $fileDropList = [System.Windows.Forms.Clipboard]::GetFileDropList() if ($null -ne $fileDropList) { $clipboardFilePaths = $fileDropList | Microsoft.PowerShell.Core\ForEach-Object { $_ } } } catch { # output verbose information if clipboard doesn't contain files Microsoft.PowerShell.Utility\Write-Verbose ` "No file drop list found in clipboard or clipboard access failed" return } } else { # output verbose information about sta subprocess requirement Microsoft.PowerShell.Utility\Write-Verbose ( "Current thread is MTA mode, launching STA subprocess " + "for clipboard operation") # create a temporary file to receive the json data $tempFile = GenXdev.FileSystem\Expand-Path ([System.IO.Path]::GetTempFileName()) -DeleteExistingFile -CreateDirectory # define the powershell command to execute in sta mode $command = ( "Microsoft.PowerShell.Utility\Add-Type -AssemblyName System.Windows.Forms;"+ "try {"+ "`$fileDropList = [System.Windows.Forms.Clipboard]::GetFileDropList();"+ "if (`$null -ne `$fileDropList) {"+ "`$paths = `$fileDropList | ForEach-Object { `$_ };"+ "`$paths | ConvertTo-Json -Compress | Out-File '$tempFile';"+ "} else { '[]' | Out-File '$tempFile'; }"+ "} catch { '[]' | Out-File '$tempFile'; }" ); try { # output verbose information about subprocess execution Microsoft.PowerShell.Utility\Write-Verbose ` "Executing STA subprocess for clipboard operation" # prepare arguments for powershell subprocess $pwshArgs = @( '-STA', '-NoProfile', '-Command', $command ) # start powershell subprocess in sta mode and wait for completion Microsoft.PowerShell.Management\Start-Process ` -FilePath 'pwsh' ` -ArgumentList $pwshArgs ` -NoNewWindow ` -Wait # read the result from temp file if (Microsoft.PowerShell.Management\Test-Path $tempFile) { $jsonContent = Microsoft.PowerShell.Management\Get-Content $tempFile -Raw if ($jsonContent.Trim()) { $clipboardFilePaths = $jsonContent | Microsoft.PowerShell.Utility\ConvertFrom-Json -ErrorAction SilentlyContinue if ($null -eq $clipboardFilePaths) { $clipboardFilePaths = @() } } Microsoft.PowerShell.Management\Remove-Item $tempFile -Force -ErrorAction SilentlyContinue } } catch { # cleanup temp file in case of error if (Microsoft.PowerShell.Management\Test-Path $tempFile) { Microsoft.PowerShell.Management\Remove-Item $tempFile -Force -ErrorAction SilentlyContinue } # output error if subprocess execution fails Microsoft.PowerShell.Utility\Write-Error ` "Error invoking pwsh: $_" return } } # exit early if no file paths retrieved if (($null -eq $clipboardFilePaths) -or ($clipboardFilePaths.Count -eq 0)) { return } $done = @{} # validate each file path and collect only existing files/directories $validFileObjects = $clipboardFilePaths | Microsoft.PowerShell.Core\ForEach-Object { # expand the file path to absolute path $path = GenXdev.FileSystem\Expand-Path $_ if ($done.ContainsKey($path)) { # skip if this path has already been processed return } # mark this path as processed $done[$path] = $true # check if file exists and return as file system object if ([System.IO.File]::Exists($path)) { # return file object similar to Get-Item Microsoft.PowerShell.Management\Get-Item -Path $path -ErrorAction SilentlyContinue return } if ([System.IO.Directory]::Exists($path)) { # return directory object similar to Get-Item Microsoft.PowerShell.Management\Get-Item -Path $path -ErrorAction SilentlyContinue return } } # output verbose information about results if ($null -ne $validFileObjects) { $count = @($validFileObjects).Count Microsoft.PowerShell.Utility\Write-Verbose ` "Retrieved $count valid file/directory objects from clipboard" } # return the valid file system objects return $validFileObjects } ############################################################################### |