March 2nd, 2023

Enumerating Windows clipboard history in C++/WinRT and C#

Here’s a console program that dumps all the text from the Windows clipboard history:

#include <stdio.h>
#include <winrt/Windows.ApplicationModel.DataTransfer.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>

namespace winrt
{
    using namespace winrt::Windows::Foundation;
    using namespace winrt::Windows::Foundation::Collections;
    using namespace winrt::Windows::ApplicationModel::DataTransfer;
}

winrt::IAsyncAction DumpClipboardHistoryAsync()
{
    auto result = co_await Clipboard::GetHistoryItemsAsync();
    auto items = result.Items();
    for (auto item : items) {
        auto content = item.Content();
        if (content.Contains(StandardDataFormats::Text())) {
            auto text = co_await content.GetTextAsync();
            printf("%ls: %ls\n", item.Id().c_str(), text.c_str());
        } else {
            printf("%ls: (no text content)\n", item.Id().c_str());
        }
    }
}

int main()
{
    winrt::init_apartment(winrt::apartment_type::multi_threaded);

    DumpClipboardHistoryAsync().get();

    return 0;
}

First, we ask for all of the clipboard history items. This call may fail and return no items; if we are interested in that case, we can check the Clipboard­History­Item­Result­Status to see why we got nothing. (Maybe the user hasn’t enabled clipboard history.)

Conveniently, if clipboard history is not available, the Get­History­Items­Async method produces an empty vector, so we can just go straight to the vector of results.¹

For each item in the results, we ask if its content contains text. If so, then we ask for the text and print it. (If not, then we just say “Sorry.”)

The sample doesn’t demonstrate it, but you can also take a history item and pass it to Clipboard::Set­History­Item­As­Content to make it the current item on the clipboard.

And here’s the C# version:

using System;
using System.Threading.Tasks;
using Windows.ApplicationModel.DataTransfer;

class Program
{
    public static async Task Main()
    {
        var result = await Clipboard.GetHistoryItemsAsync();
        var items = result.Items;
        foreach (var item in items) {
            var content = item.Content;
            if (content.Contains(StandardDataFormats.Text)) {
                var text = await content.GetTextAsync();
                System.Console.WriteLine($"{item.Id}: {text}");
            } else {
                System.Console.WriteLine($"{item.Id}: (no text content)");
            }
        }
    }
}

It’s a straightforward translation of the C++/WinRT version.

Next time, we’ll translate this into PowerShell.

Wait, what?

Yup, PowerShell.

¹ This is a general principle in the Windows Runtime that methods which produce vectors return empty vectors rather than null pointers on failure.

Topics
Code

Author

Raymond has been involved in the evolution of Windows for more than 30 years. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie-jeebies. The Web site spawned a book, coincidentally also titled The Old New Thing (Addison Wesley 2007). He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information.

2 comments

Discussion is closed. Login to edit/delete existing comments.

  • Georg Rottensteiner

    Quote: ¹ This is a general principle in the Windows Runtime that methods which produce vectors return empty vectors rather than null pointers on failure.

    I love you for this.

    • Nick

      I’ve tried to extol the virtues of this design approach on my own dev team and it’s surprising how many people seem to struggle with the idea.

      Unless there’s a meaningful semantic difference between “empty collection” and “null collection”, just use the empty collection.