WinForms clipboard monitoring

A quick example showing how to monitor the clipboard in a WinForms project.


WinForms makes it very easy to access the system clipboard using the Clipboard class. However, it doesn't provide an out-of-the-box way to monitor the clipboard for changes, or raise events when the clipboard is updated.

In order to get this functionality, we have to delve deeper into the Windows API. The AddClipboardFormatListener and RemoveClipboardFormatListener functions can be used to add windows to the system's clipboard listener list, so that they will be notified whenever the clipboard changes. However, we want to have a global clipboard listener, rather than a form/window specific one. In order to do this, we can leverage what is known as a 'message-only window. Message-only windows are not visible, have no z-order, cannot be enumerated, and do not receive broadcast messages. They simply dispatches messages. This is ideal for our purpose.

The implementation I settled on involved setting up a static ClipboardMonitor class that wraps a hidden message-only window. The message-only window adds itself to the clipboard listener list using AddClipboardFormatListener, and then passes all WM_CLIPBOARDUPDATE events on to the ClipboardUpdate event on the static ClipboardMonitor class.

The implementation is as follows:

public static class ClipboardMonitor
{
    public static event EventHandler ClipboardUpdate;

    private static ClipboardMonitorWindow _window = new ClipboardMonitorWindow();
    public static void Start() => _window.Start();
    public static void Stop() => _window.Stop();

    private class ClipboardMonitorWindow : Form
    {
        public ClipboardMonitorWindow()
        {
            // Set this window as a message-only window.
            NativeMethods.SetParent(Handle, new IntPtr(-3)); // HWND_MESSAGE

            Start();
        }

        public void Start()
        {
            NativeMethods.AddClipboardFormatListener(Handle);
        }

        public void Stop()
        {
            NativeMethods.RemoveClipboardFormatListener(Handle);
        }

        protected override void WndProc(ref Message m)
        {
            if (m.Msg == 0x031D) // WM_CLIPBOARDUPDATE
            {
                ClipboardUpdate?.Invoke(null, EventArgs.Empty);
            }

            base.WndProc(ref m);
        }

        private static class NativeMethods
        {
            [DllImport("user32.dll", SetLastError = true)]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool AddClipboardFormatListener(IntPtr hwnd);

            [DllImport("user32.dll", SetLastError = true)]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool RemoveClipboardFormatListener(IntPtr hwnd);

            [DllImport("user32.dll", SetLastError = true)]
            public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
        }
    }
}

An example of the class in action:

ClipboardMonitor.ClipboardUpdate += (object sender, EventArgs e) =>
{
    var clipboardText = Clipboard.GetText();
    MessageBox.Show(clipboardText);
};

Hopefully this post helps you set up a clipboard monitoring system for your application!


Posted by Matthew King on 2 April 2018
Permission is granted to use all code snippets under CC BY-SA 3.0 (just like StackOverflow), or the MIT license - your choice!