剪贴板变更通知?

17

我知道如何将内容放入剪贴板并从剪贴板检索内容。

但是,在这两个操作之间,可能会有其他操作更改剪贴板的内容。

是否有一种方法可以在任何应用程序修改剪贴板时得到通知?


1
您应该仅在响应用户操作时使用剪贴板。在这种情况下,用户不可能同时单击两个东西。 - Mike Caron
这个代码示例可能会很有用: https://code.msdn.microsoft.com/windowsdesktop/CSWPFClipboardViewer-f601b815 - Wojciech Kulik
https://dev59.com/OXRB5IYBdhLWcg3wcm6d - AzzamAziz
这个回答解决了您的问题吗?C#剪贴板事件 - StayOnTarget
4个回答

21

您需要添加的唯一引用是在您的wpf应用程序中的windows表单。我创建了一个包装器来封装我在互联网上找到的功能。

大多数示例做的事情比我需要的多,所以我决定创建自己的类。我喜欢隔离问题,因此这个类只是监听剪贴板的变化,并告诉您剪贴板上的数据类型。例如,它是文本?图像?还是其他什么?

无论如何,这是我的包装器:

using System;
using System.Windows.Forms;
using System.Threading;
using System.Runtime.InteropServices;

public static class ClipboardMonitor 
{
    public delegate void OnClipboardChangeEventHandler(ClipboardFormat format, object data);
    public static event OnClipboardChangeEventHandler OnClipboardChange;

    public static void Start()
    {
        ClipboardWatcher.Start();
        ClipboardWatcher.OnClipboardChange += (ClipboardFormat format, object data) =>
        {
            if (OnClipboardChange != null)
                OnClipboardChange(format, data);
        };
    }

    public static void Stop()
    {
        OnClipboardChange = null;
        ClipboardWatcher.Stop();
    }
    
    class ClipboardWatcher : Form
    {
        // static instance of this form
        private static ClipboardWatcher mInstance;

        // needed to dispose this form
        static IntPtr nextClipboardViewer;

        public delegate void OnClipboardChangeEventHandler(ClipboardFormat format, object data);
        public static event OnClipboardChangeEventHandler OnClipboardChange;

        // start listening
        public static void Start()
        {
            // we can only have one instance if this class
            if (mInstance != null)
                return;

            Thread t = new Thread(new ParameterizedThreadStart(x =>
            {
                Application.Run(new ClipboardWatcher());
            }));
            t.SetApartmentState(ApartmentState.STA); // give the [STAThread] attribute
            t.Start();
        }

        // stop listening (dispose form)
        public static void Stop()
        {
            mInstance.Invoke(new MethodInvoker(() =>
            {
                ChangeClipboardChain(mInstance.Handle, nextClipboardViewer);
            }));
            mInstance.Invoke(new MethodInvoker(mInstance.Close));

            mInstance.Dispose();

            mInstance = null;
        }

        // on load: (hide this window)
        protected override void SetVisibleCore(bool value)
        {
            CreateHandle();

            mInstance = this;

            nextClipboardViewer = SetClipboardViewer(mInstance.Handle);

            base.SetVisibleCore(false);
        }

        [DllImport("User32.dll", CharSet = CharSet.Auto)]
        public static extern IntPtr SetClipboardViewer(IntPtr hWndNewViewer);

        [DllImport("User32.dll", CharSet = CharSet.Auto)]
        public static extern bool ChangeClipboardChain(IntPtr hWndRemove, IntPtr hWndNewNext);

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern int SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);

        // defined in winuser.h
        const int WM_DRAWCLIPBOARD = 0x308;
        const int WM_CHANGECBCHAIN = 0x030D;

        protected override void WndProc(ref Message m)
        {
            switch (m.Msg)
            {
                case WM_DRAWCLIPBOARD:
                    ClipChanged();
                    SendMessage(nextClipboardViewer, m.Msg, m.WParam, m.LParam);
                    break;

                case WM_CHANGECBCHAIN:
                    if (m.WParam == nextClipboardViewer)
                        nextClipboardViewer = m.LParam;
                    else
                        SendMessage(nextClipboardViewer, m.Msg, m.WParam, m.LParam);
                    break;

                default:
                    base.WndProc(ref m);
                    break;
            }
        }

        static readonly string[] formats = Enum.GetNames(typeof(ClipboardFormat));

        private void ClipChanged()
        {
            IDataObject iData = Clipboard.GetDataObject();

            ClipboardFormat? format = null;

            foreach (var f in formats)
            {
                if (iData.GetDataPresent(f))
                {
                    format = (ClipboardFormat)Enum.Parse(typeof(ClipboardFormat), f);
                    break;
                }
            }

            object data = iData.GetData(format.ToString());

            if (data == null || format == null)
                return;

            if (OnClipboardChange != null)
                OnClipboardChange((ClipboardFormat)format, data);
        }


    }
}

public enum ClipboardFormat : byte
{
    /// <summary>Specifies the standard ANSI text format. This static field is read-only.
    /// </summary>
    /// <filterpriority>1</filterpriority>
    Text,
    /// <summary>Specifies the standard Windows Unicode text format. This static field
    /// is read-only.</summary>
    /// <filterpriority>1</filterpriority>
    UnicodeText,
    /// <summary>Specifies the Windows device-independent bitmap (DIB) format. This static
    /// field is read-only.</summary>
    /// <filterpriority>1</filterpriority>
    Dib,
    /// <summary>Specifies a Windows bitmap format. This static field is read-only.</summary>
    /// <filterpriority>1</filterpriority>
    Bitmap,
    /// <summary>Specifies the Windows enhanced metafile format. This static field is
    /// read-only.</summary>
    /// <filterpriority>1</filterpriority>
    EnhancedMetafile,
    /// <summary>Specifies the Windows metafile format, which Windows Forms does not
    /// directly use. This static field is read-only.</summary>
    /// <filterpriority>1</filterpriority>
    MetafilePict,
    /// <summary>Specifies the Windows symbolic link format, which Windows Forms does
    /// not directly use. This static field is read-only.</summary>
    /// <filterpriority>1</filterpriority>
    SymbolicLink,
    /// <summary>Specifies the Windows Data Interchange Format (DIF), which Windows Forms
    /// does not directly use. This static field is read-only.</summary>
    /// <filterpriority>1</filterpriority>
    Dif,
    /// <summary>Specifies the Tagged Image File Format (TIFF), which Windows Forms does
    /// not directly use. This static field is read-only.</summary>
    /// <filterpriority>1</filterpriority>
    Tiff,
    /// <summary>Specifies the standard Windows original equipment manufacturer (OEM)
    /// text format. This static field is read-only.</summary>
    /// <filterpriority>1</filterpriority>
    OemText,
    /// <summary>Specifies the Windows palette format. This static field is read-only.
    /// </summary>
    /// <filterpriority>1</filterpriority>
    Palette,
    /// <summary>Specifies the Windows pen data format, which consists of pen strokes
    /// for handwriting software, Windows Forms does not use this format. This static
    /// field is read-only.</summary>
    /// <filterpriority>1</filterpriority>
    PenData,
    /// <summary>Specifies the Resource Interchange File Format (RIFF) audio format,
    /// which Windows Forms does not directly use. This static field is read-only.</summary>
    /// <filterpriority>1</filterpriority>
    Riff,
    /// <summary>Specifies the wave audio format, which Windows Forms does not directly
    /// use. This static field is read-only.</summary>
    /// <filterpriority>1</filterpriority>
    WaveAudio,
    /// <summary>Specifies the Windows file drop format, which Windows Forms does not
    /// directly use. This static field is read-only.</summary>
    /// <filterpriority>1</filterpriority>
    FileDrop,
    /// <summary>Specifies the Windows culture format, which Windows Forms does not directly
    /// use. This static field is read-only.</summary>
    /// <filterpriority>1</filterpriority>
    Locale,
    /// <summary>Specifies text consisting of HTML data. This static field is read-only.
    /// </summary>
    /// <filterpriority>1</filterpriority>
    Html,
    /// <summary>Specifies text consisting of Rich Text Format (RTF) data. This static
    /// field is read-only.</summary>
    /// <filterpriority>1</filterpriority>
    Rtf,
    /// <summary>Specifies a comma-separated value (CSV) format, which is a common interchange
    /// format used by spreadsheets. This format is not used directly by Windows Forms.
    /// This static field is read-only.</summary>
    /// <filterpriority>1</filterpriority>
    CommaSeparatedValue,
    /// <summary>Specifies the Windows Forms string class format, which Windows Forms
    /// uses to store string objects. This static field is read-only.</summary>
    /// <filterpriority>1</filterpriority>
    StringFormat,
    /// <summary>Specifies a format that encapsulates any type of Windows Forms object.
    /// This static field is read-only.</summary>
    /// <filterpriority>1</filterpriority>
    Serializable,
}

我知道这可能有些多,但是在包装后它应该看起来像这样:输入图像描述

然后你可以使用这个类:

    static void Main(string[] args)
    {

        ClipboardMonitor.Start();

        ClipboardMonitor.OnClipboardChange += new ClipboardMonitor.OnClipboardChangeEventHandler(ClipboardMonitor_OnClipboardChange);

        Console.Read();

        ClipboardMonitor.Stop(); // do not forget to stop


        
    }

    static void ClipboardMonitor_OnClipboardChange(ClipboardFormat format, object data)
    {
        Console.WriteLine("Clipboard changed and it has the format: "+format.ToString());
    }

感谢您的实现。但它不是线程安全的,事件将在您创建的线程上引发,而不是在注册事件的线程上引发。这可能会导致UI出现问题。 - metacircle

5
我能找到的只有一个用C#/VB.NET编写的剪贴板监视器。我看到了WPF和WinForms,所以我假设这是一个可行的选项。
涉及从user32 dll中调用一些方法pinvoke

编辑

在编辑时,上面的原始链接已经失效。这里是一个archive.org快照


2
然而,在这两个操作之间,有可能会有另一个操作改变剪贴板的内容。
当一个应用程序调用OpenClipboard()时,没有其他应用程序可以使用剪贴板。一旦锁定剪贴板的应用程序调用CloseClipboard(),则任何应用程序都可以使用和锁定剪贴板。
有没有办法在任何应用程序修改剪贴板时得到通知?
是的。请参阅API SetClipboardViewer()和消息WM_DRAWCLIPBOARD和WM_CHANGECBCHAIN。
更多信息,请查看此处:http://msdn.microsoft.com/en-us/library/ms649016(v=vs.85).aspx#_win32_Adding_a_Window_to_the_Clipboard_Viewer_Chain

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接