Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert Keith Hill's PowerShell Get-Clipboard and Set-Clipboard to a PSM1 script

I'd like to convert Keith Hill's C# implementation of Get-Clipboard and Set-Clipboard into pure PowerShell as a .PSM1 file.

Is there a way to spin up an STA thread in PowerShell as he does in his Cmdlet when working with the clipboard?

The Blog Post
The Code

like image 335
Eric Schoonover Avatar asked Oct 14 '09 15:10

Eric Schoonover


2 Answers

TextBox doesn't require -STA switch.

function Get-ClipBoard {
    Add-Type -AssemblyName System.Windows.Forms
    $tb = New-Object System.Windows.Forms.TextBox
    $tb.Multiline = $true
    $tb.Paste()
    $tb.Text
}


function Set-ClipBoard() {
    Param(
      [Parameter(ValueFromPipeline=$true)]
      [string] $text
    )
    Add-Type -AssemblyName System.Windows.Forms
    $tb = New-Object System.Windows.Forms.TextBox
    $tb.Multiline = $true
    $tb.Text = $text
    $tb.SelectAll()
    $tb.Copy()
}
like image 69
hoge Avatar answered Oct 21 '22 06:10

hoge


See the bottom section for a cross-edition, cross-platform module that offers clipboard text support in PowerShell Core and in Windows PowerShell v2 - v4.

An attempt to summarize the state of affairs and options as of Windows PowerShell v5.1 / PowerShell Core v6.1.0:

  • Windows PowerShell v5.0+: Use the built-in Get-Clipboard and Set-Clipboard cmdlets.

  • Windows PowerShell v4.0- (v1 - v4.0): has no built-in cmdlets for interacting with the clipboard, but there are workarounds:

    • Use PowerShell Community Extensions (PSCX; http://pscx.codeplex.com/), which come with several clipboard-related cmdlets that go beyond just handling text.
    • Pipe to the standard command-line utility clip.exe (W2K3+ server-side, Vista+ client-side)[1]:

      • Note: Aside from the encoding issues discussed below, ... | clip.exe invariably appends a trailing newline to the input; the only way to avoid that is to use a temporary file whose content is provided via cmd's < input redirection - see the Set-ClipboardText function below.

      • If only ASCII-character (7-bit) support is needed: works by default.

      • If only OEM-encoding (8-bit) support (e.g., IBM437 in the US) is needed, run the following first:

        • $OutputEncoding = [System.Text.Encoding]::GetEncoding([System.Globalization.CultureInfo]::CurrentCulture.TextInfo.OEMCodePage)
      • If full Unicode support is needed, a UTF-16 LE encoding without BOM must be used; run the following first:

        • $OutputEncoding = New-Object System.Text.UnicodeEncoding $false, $false # UTF-16 encoding *without BOM*
        • Example to test with (the PS console will display the Asian chars. as "??", but still handle them correctly - verify clipboard content in Notepad, for instance):

          • "I enjoyed Thomas Hübl's talk about 中文" | clip # should appear as is on the clipboard
      • Note: Assigning to $OutputEncoding as above works fine in the global scope, but not otherwise, such as in a function, due to a bug as of Windows PowerShell v5.1 / PowerShell Core v6.0.0-rc.2 - see https://github.com/PowerShell/PowerShell/issues/5763

        • In a non-global context, use (New-Object ...).psobject.BaseObject to work around the bug, or - in PSv5+ - use [...]:new() instead.
      • Note: clip.exe apparently understands 2 formats:

        • the system's current OEM codepage (e.g., IBM 437)
        • UTF-16 LE ("Unicode")
        • Unfortunately, clip.exe always treats a BOM as data, hence the need to use a BOM-less encoding.
        • Note that the above encodings matter only with respect to correctly detecting input; once on the clipboard, the input string is available in all of the following encodings: UTF-16 LE, "ANSI", and OEM.
    • Use a PowerShell-based solution with direct use of .NET classes:

      • Note that clipboard access can only occur from a thread in STA (single-threaded apartment) mode - as opposed to MTA (multi-threaded apartment):

        • v3: STA is the default (MTA mode can be entered by invoking powershell.exe with the -mta switch).
        • v2 and v1: MTA is the default; STA mode can be entered by invoking powershell.exe with the -sta switch.
        • Ergo: Robust functions should be able to access the clipboard from sessions in either mode.
  • PowerShell Core (multi-platform), as of v6.1.0, has no built-in cmdlets for interacting with the clipboard, not even when run on Windows.

    • The workaround is to use platform-specific utilities or APIs - see below.

My ClipboardText module provides "polyfill" functions Get-ClipboardText and Set-ClipboardText for getting and setting text from the clipboard; they work on Windows PowerShell v2+ as well as on PowerShell Core (with limitations, see below).

In the simplest case (PSv5+ or v3/v4 with the package-management modules installed), you can install it from the PowerShell Gallery from an elevated / sudo session as follows:

Install-Module ClipboardText

For more information, including prerequisites and manual-installation instructions, see the repo.

  • Note: Strictly speaking, the functions aren't polyfills, given that their names differ from the built-in cmdlets. However, the name suffix Text was chosen so as to make it explicit that these functions handle text only.

  • The code gratefully builds on information from various sites, notably @hoge's answer (https://stackoverflow.com/a/1573295/45375) and http://techibee.com/powershell/powershell-script-to-copy-powershell-command-output-to-clipboard/1316

  • Running on Windows PowerShell v5+ in STA mode:

    • The built-in cmdlets (Get-Clipboard / Set-Clipboard) are called behind the scenes.
      Note that STA mode (a COM threading model) is the default since v3, but you can opt into MTA (multi-threaded mode) with command-line option -MTA.
  • In all other cases (Windows PowerShell v4- and/or in MTA mode, PowerShell Core on all supported platforms):

    • Windows:
      • A P/Invoke-based solution that calls the Windows API is used, via ad-hoc C# code compiled with Add-Type.
    • Unix-like platforms: Native utilities are called behind the scenes:
      • macOS: pbcopy and pbpaste
      • Linux: xclip, if available and installed;
        for instance, on Ubuntu, use sudo apt-get xclip to install.
  • Set-ClipboardText can accept any type of object(s) as input (which is/are then converted to text the same way they would render in the console), either directly, or from the pipeline.

  • Invoke with -Verbose to see what technique is used behind the scenes to access the clipboard.


[1] An earlier version of this answer incorrectly claimed that clip.exe:
- always appends a line break when copying to the clipboard (it does NOT)
- correctly handles UTF-16 LE BOMs in files redirected to stdin via < vs. when input is piped via | (clip.exe always copies the BOM to the clipboard, too).

like image 33
mklement0 Avatar answered Oct 21 '22 08:10

mklement0