Tool Time - Clipboard History
A reminder that all previous articles can be found in the online archive. Premium subscribers have full access.
As I've worked with PowerShell over the years, I've realized that the reason for writing PowerShell code generally falls into two camps. One outcome is to achieve a desired result. For example, you may write PowerShell code to provision a new user account in Active Directory. I tend to think this is the purpose of many PowerShell scripts, although functions and cmdlets can fall into this category as well. Something as simple as Start-Service
is a cmdlet that achieves a desired result.
The other category is PowerShell code as a tool. Perhaps another way to consider this is as a utility like nslookup
or ipconfig
. I think an argument could be made that Get-
commands fall into this group. Another good example of a PowerShell tool is your prompt
function. Since I spend a lot of time working from a PowerShell prompt, I have written a number of PowerShell tools to help me work more efficiently. Recently, I've added a few new tools to my toolbox that I'd like to share with you. If nothing else, you might pick up a scripting tip or trick that you can use in your projects.
Clipboard History
Windows systems have a clipboard history feature that I keep forget to take advantage of. Press Windows+V
to see a list of items you've copied to the clipboard. Although, you might need to turn it on.

Once you've added some items, you can see them in the clipboard history.

You can click on an item and Windows will insert it the cursor location of the active application. I wanted to find a way to access this clipboard history from PowerShell. The Get-Clipboard
cmdlet only retrieves the last copied content. I wanted to see the entire history, which is limited to 25 items.
Windows Runtime and WinMetadata
To be honest, I had no idea of where to even begin. I assumed I would need to use .NET classes and methods, but I'm not a .NET developer. I decided to take advantage of AI and asked CoPilot to write some PowerShell code to achieve my goal. The code was complicated and didn't work in PowerShell 7, which is my primary environment. I kept digging and searching. I read a lot of stuff from StackOverflow. Eventually, I started to get the picture.
The clipboard history feature required use of the WindowsRuntime
assembly. Typically, this command should get the job done.
Add-Type -AssemblyName System.Runtime.WindowsRuntime
In PowerShell 7, this looks like it works. But it actually doesn't. During code development, run the command like this:
Add-Type -AssemblyName System.Runtime.WindowsRuntime -PassThru
Now, I see that this assembly isn't supported.

A little more research confirmed that this class is not supported in PowerShell 7. It requires the full .NET Framework. Remember, PowerShell 7 is running on .NET Core. Here's a helper function, i.e. another tool, that will tell you what .NET version you are using in a given PowerShell session.
Function Get-RuntimeInformation {
[cmdletbinding()]
[OutputType('PSRunTimeInformation')]
[alias('rti')]
Param()
[PSCustomObject]@{
PSTypeName = 'PSRuntimeInformation'
PSVersion = $PSVersionTable.PSVersion
Framework = [System.Runtime.InteropServices.RuntimeInformation]::FrameworkDescription
FrameworkVersion = [System.Runtime.InteropServices.RuntimeInformation]::FrameworkDescription.Replace('.NET', '').Trim()
OSVersion = [System.Runtime.InteropServices.RuntimeInformation]::OSDescription
Architecture = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture
Computername = [Environment]::MachineName
}
}
Here's an example from PowerShell 7
PS C:\> Get-RuntimeInformation
PSVersion : 7.5.0-preview.4
Framework : .NET 9.0.0-preview.6.24327.7
FrameworkVersion : 9.0.0-preview.6.24327.7
OSVersion : Microsoft Windows 10.0.26100
Architecture : X64
Computername : JEFFDESK
And from Windows PowerShell
PS C:\> get-RuntimeInformation
PSVersion : 5.1.26100.2152
Framework : .NET Framework 4.8.9277.0
FrameworkVersion : Framework 4.8.9277.0
OSVersion : Microsoft Windows 10.0.26100
Architecture : X64
Computername : JEFFDESK
Now, I know that I need to use Windows PowerShell 5.1.

The other piece of information I learned I would need is the use of WinMetadata assemblies. I believe these are used in UWP (Universal Windows Platform) applications. These are files with a .winmd
extension. You can find them in the C:\Windows\System32\WinMetadata
folder. Here's an example of what you might see.
PS C:\> dir C:\windows\system32\WinMetadata\*.winmd
Directory: C:\windows\system32\WinMetadata
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 9/30/2024 11:08 PM 58888 Windows.AI.winmd
-a---- 10/15/2024 8:43 AM 893040 Windows.ApplicationModel.winmd
...
My sample code indicated I needed to use the Windows.ApplicationModel assembly. However, you can't just load these assemblies with Add-Type
, even in Windows PowerShell.

My research dug up this bit of code.
$null = [Windows.ApplicationModel.DataTransfer.Clipboard, Windows.ApplicationModel.DataTransfer, ContentType = WindowsRuntime]
If I try this without sending it to $null
, I can see that it does something.

I have never seen syntax like this, and even though it does something, I didn't know what that was. Years ago, I would have had to bug one of my .NET developer friends to explain it to me. But now, I have CoPilot. I asked it to explain the syntax to me.
Explain what this line of code is doing: [Windows.ApplicationModel.DataTransfer.Clipboard, Windows.ApplicationModel.DataTransfer, ContentType = WindowsRuntime]
This line of code is actually a metadata attribute applied to a class or method in a Windows Runtime (WinRT) environment, like a UWP (Universal Windows Platform) app. Here’s the breakdown:
Windows.ApplicationModel.DataTransfer.Clipboard
: This refers to the clipboard API provided by the Windows Runtime. It's used to interact with the system clipboard, enabling apps to cut, copy, and paste data.
Windows.ApplicationModel.DataTransfer
: This is the namespace that contains classes for working with data transfer operations, such as clipboard and drag-and-drop.
ContentType = WindowsRuntime
: This indicates that the content type is specific to the Windows Runtime, which is a platform for Windows apps that provides a common API layer.
Effectively, this attribute specifies that the clipboard functionality from the Windows.ApplicationModel.DataTransfer
namespace should be used, and that the content type is suitable for the Windows Runtime.
I'll be honest, there's no way I would have figured this out on my own. But I know enough about .NET to understand this explanation and began to visualize what the code snippets were doing. I also have enough experience to turn them into PowerShell code.
Asynchronous Operations
Now it gets really interesting.I knew from my research I need to use [Windows.ApplicationModel.DataTransfer.Clipboard]
to access the clipboard history.

This screen shot confirms what I have in my suggested code snippets.
PS C:\> [Windows.ApplicationModel.DataTransfer.Clipboard]::GetHistoryItemsAsync
OverloadDefinitions
-------------------
static Windows.Foundation.IAsyncOperation[Windows.ApplicationModel.DataTransfer.ClipboardHistoryItemsResult]
GetHistoryItemsAsync()
The output is going to be an asynchronous operation.