剪贴板事件C#

时间:2009-03-07 09:18:04

标签: c# events clipboard

是否有可以通过C#访问的剪贴板更改或更新的事件?

10 个答案:

答案 0 :(得分:73)

为了完整性,这是我在生产代码中使用的控件。只需从设计器拖动并双击即可创建事件处理程序。

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Drawing;

namespace ClipboardAssist {

// Must inherit Control, not Component, in order to have Handle
[DefaultEvent("ClipboardChanged")]
public partial class ClipboardMonitor : Control 
{
    IntPtr nextClipboardViewer;

    public ClipboardMonitor()
    {
        this.BackColor = Color.Red;
        this.Visible = false;

        nextClipboardViewer = (IntPtr)SetClipboardViewer((int)this.Handle);
    }

    /// <summary>
    /// Clipboard contents changed.
    /// </summary>
    public event EventHandler<ClipboardChangedEventArgs> ClipboardChanged;

    protected override void Dispose(bool disposing)
    {
        ChangeClipboardChain(this.Handle, nextClipboardViewer);
    }

    [DllImport("User32.dll")]
    protected static extern int SetClipboardViewer(int 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);

    protected override void WndProc(ref System.Windows.Forms.Message m)
    {
        // defined in winuser.h
        const int WM_DRAWCLIPBOARD = 0x308;
        const int WM_CHANGECBCHAIN = 0x030D;

        switch (m.Msg)
        {
            case WM_DRAWCLIPBOARD:
                OnClipboardChanged();
                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;
        }
    }

    void OnClipboardChanged()
    {
        try
        {
            IDataObject iData = Clipboard.GetDataObject();
            if (ClipboardChanged != null)
            {
                ClipboardChanged(this, new ClipboardChangedEventArgs(iData));
            }

        }
        catch (Exception e)
        {
            // Swallow or pop-up, not sure
            // Trace.Write(e.ToString());
            MessageBox.Show(e.ToString());
        }
    }
}

public class ClipboardChangedEventArgs : EventArgs
{
    public readonly IDataObject DataObject;

    public ClipboardChangedEventArgs(IDataObject dataObject)
    {
        DataObject = dataObject;
    }
}
}

答案 1 :(得分:70)

我认为你必须使用一些p / invoke:

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

请参阅this article on how to set up a clipboard monitor in c#

基本上,您使用

将您的应用注册为剪贴板查看器
_ClipboardViewerNext = SetClipboardViewer(this.Handle);

然后您将收到WM_DRAWCLIPBOARD消息,您可以通过覆盖WndProc来处理该消息:

protected override void WndProc(ref Message m)
{
    switch ((Win32.Msgs)m.Msg)
    {
        case Win32.Msgs.WM_DRAWCLIPBOARD:
        // Handle clipboard changed
        break;
        // ... 
   }
}

(还有更多工作要做;在剪贴板链上传递内容并取消注册视图,但您可以从the article获取

答案 2 :(得分:22)

我在WPF中遇到了这个挑战,最终使用了下面描述的方法。对于Windows表单,在这个答案的其他地方有很好的例子,例如ClipboardHelper控件。

对于WPF,我们不能覆盖WndProc,因此我们必须使用窗口中的Source显式地将其与HwndSource AddHook调用挂钩。剪贴板侦听器仍使用AddClipboardFormatListener本机互操作调用。

原生方法:

internal static class NativeMethods
{
    // See http://msdn.microsoft.com/en-us/library/ms649021%28v=vs.85%29.aspx
    public const int WM_CLIPBOARDUPDATE = 0x031D;
    public static IntPtr HWND_MESSAGE = new IntPtr(-3);

    // See http://msdn.microsoft.com/en-us/library/ms632599%28VS.85%29.aspx#message_only
    [DllImport("user32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool AddClipboardFormatListener(IntPtr hwnd);
}

剪贴板管理器类:

using System.Windows;
using System.Windows.Interop;

public class ClipboardManager
{
    public event EventHandler ClipboardChanged;

    public ClipboardManager(Window windowSource)
    {
        HwndSource source = PresentationSource.FromVisual(windowSource) as HwndSource;
        if(source == null)
        {
            throw new ArgumentException(
                "Window source MUST be initialized first, such as in the Window's OnSourceInitialized handler."
                , nameof(windowSource));
        }

        source.AddHook(WndProc);

        // get window handle for interop
        IntPtr windowHandle = new WindowInteropHelper(windowSource).Handle;

        // register for clipboard events
        NativeMethods.AddClipboardFormatListener(windowHandle);
    }

    private void OnClipboardChanged()
    {
        ClipboardChanged?.Invoke(this, EventArgs.Empty);
    }

    private static readonly IntPtr WndProcSuccess = IntPtr.Zero;

    private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
    {
        if (msg == NativeMethods.WM_CLIPBOARDUPDATE)
        {
            OnClipboardChanged();
            handled = true;
        }

        return WndProcSuccess;
    }
}

通过在OnSourceInitialized或更高版本中添加事件(如Window.Loaded事件或在操作期间),可以在WPF窗口中使用它。 (当我们有足够的信息来使用本机钩子时):

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    protected override void OnSourceInitialized(EventArgs e)
    {
        base.OnSourceInitialized(e);

        // Initialize the clipboard now that we have a window soruce to use
        var windowClipboardManager = new ClipboardManager(this);
        windowClipboardManager.ClipboardChanged += ClipboardChanged;
    }

    private void ClipboardChanged(object sender, EventArgs e)
    {
        // Handle your clipboard update here, debug logging example:
        if (Clipboard.ContainsText())
        {
            Debug.WriteLine(Clipboard.GetText());
        }
    }
}

我在“流亡之路”项目分析器项目中使用此方法,因为当您按Ctrl-C时,游戏会通过剪贴板显示项目信息。

https://github.com/ColinDabritz/PoeItemAnalyzer

我希望这可以帮助有WPF剪贴板更改处理的人!

答案 3 :(得分:10)

好的,所以这是一篇旧帖子,但我们发现一个解决方案与目前的答案相比看起来非常简单。我们正在使用WPF,如果剪贴板包含文本,我们希望自己的自定义命令(在ContextMenu中)启用和禁用。已有ApplicationCommands.Cut,Copy和Paste,这些命令可以正确响应剪贴板更改。所以我们刚刚添加了以下EventHandler。

ApplicationCommands.Paste.CanExecuteChanged += new EventHandler(Paste_CanExecuteChanged);

private void Paste_CanExecuteChanged(object sender, EventArgs e) {
  ourVariable= Clipboard.ContainsText();
}

我们实际上是通过这种方式在我们自己的Command上控制CanExecute。为我们需要的东西工作,也许它会帮助其他人。

答案 4 :(得分:9)

有多种方法可以做到这一点,但这是我的最爱,也适合我。我创建了一个类库,以便其他人可以添加项目并包含DLL然后只需调用它并在其应用程序中的任何地方使用它。

这个答案是在this one的帮助下完成的。

  1. 创建类库项目并将其命名为ClipboardHelper。
  2. 用ClipboardMonitor替换Class1名称。
  3. 将以下代码添加到其中。
  4. 添加System.Windows.Forms参考。
  5. 代码下的更多步骤。

    using System;
    using System.Windows.Forms;
    using System.Threading;
    using System.Runtime.InteropServices;
    
    namespace ClipboardHelper
    {
        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;
    
                    var 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)]
                private static extern IntPtr SetClipboardViewer(IntPtr hWndNewViewer);
    
                [DllImport("User32.dll", CharSet = CharSet.Auto)]
                private static extern bool ChangeClipboardChain(IntPtr hWndRemove, IntPtr hWndNewNext);
    
                [DllImport("user32.dll", CharSet = CharSet.Auto)]
                private 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,
        }
    }
    
    1. 在您的其他项目中右键单击解决方案并添加 - &gt;退出项目 - &gt; ClipboardHelper.csproj
    2. 在您的项目上转到右键点击参考 - &gt;添加参考 - &gt;解决方案 - &gt;选择ClipboardHelper。
    3. 在使用ClipboardHelper的项目类型的类文件中。
    4. 您现在可以键入ClipboardMonitor.Start或.Stop或.OnClipboardChanged

      using ClipboardHelper;
      
      namespace Something.Something.DarkSide
      {
          public class MainWindow
          {
      
              public MainWindow()
              {
                  InitializeComponent();
      
                  Loaded += MainWindow_Loaded;
              }
      
              void MainWindow_Loaded(object sender, RoutedEventArgs e)
              {
                  ClipboardMonitor.OnClipboardChange += ClipboardMonitor_OnClipboardChange;
                  ClipboardMonitor.Start();
              }               
      
              private void ClipboardMonitor_OnClipboardChange(ClipboardFormat format, object data)
              {
                  // Do Something...
              }
      }
      

答案 5 :(得分:6)

我相信早期解决方案之一不会检查dispose方法的null:

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Drawing;

namespace ClipboardAssist {

// Must inherit Control, not Component, in order to have Handle
[DefaultEvent("ClipboardChanged")]
public partial class ClipboardMonitor : Control 
{
    IntPtr nextClipboardViewer;

    public ClipboardMonitor()
    {
        this.BackColor = Color.Red;
        this.Visible = false;

        nextClipboardViewer = (IntPtr)SetClipboardViewer((int)this.Handle);
    }

    /// <summary>
    /// Clipboard contents changed.
    /// </summary>
    public event EventHandler<ClipboardChangedEventArgs> ClipboardChanged;

    protected override void Dispose(bool disposing)
    {
        if(nextClipboardViewer != null)
            ChangeClipboardChain(this.Handle, nextClipboardViewer);
    }

    [DllImport("User32.dll")]
    protected static extern int SetClipboardViewer(int 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);

    protected override void WndProc(ref System.Windows.Forms.Message m)
    {
        // defined in winuser.h
        const int WM_DRAWCLIPBOARD = 0x308;
        const int WM_CHANGECBCHAIN = 0x030D;

        switch (m.Msg)
        {
            case WM_DRAWCLIPBOARD:
                OnClipboardChanged();
                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;
        }
    }

    void OnClipboardChanged()
    {
        try
        {
            IDataObject iData = Clipboard.GetDataObject();
            if (ClipboardChanged != null)
            {
                ClipboardChanged(this, new ClipboardChangedEventArgs(iData));
            }

        }
        catch (Exception e)
        {
            // Swallow or pop-up, not sure
            // Trace.Write(e.ToString());
            MessageBox.Show(e.ToString());
        }
    }
}

    public class ClipboardChangedEventArgs : EventArgs
    {
        public readonly IDataObject DataObject;

        public ClipboardChangedEventArgs(IDataObject dataObject)
        {
            DataObject = dataObject;
        }
    }
}

答案 6 :(得分:4)

here是使用AddClipboardFormatListener的一个很好的例子。

为了做到这一点,我们需要对AddClipboardFormatListenerRemoveClipboardFormatListener进行异议。

/// <summary>
/// Places the given window in the system-maintained clipboard format listener list.
/// </summary>
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool AddClipboardFormatListener(IntPtr hwnd);

/// <summary>
/// Removes the given window from the system-maintained clipboard format listener list.
/// </summary>
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool RemoveClipboardFormatListener(IntPtr hwnd);

/// <summary>
/// Sent when the contents of the clipboard have changed.
/// </summary>
private const int WM_CLIPBOARDUPDATE = 0x031D;

然后我们需要通过使用窗口的句柄作为参数调用AddClipboardFormatListener方法将我们的窗口添加到剪贴板格式侦听器列表。将以下代码放在主窗口窗体构造函数或其任何加载事件中。

AddClipboardFormatListener(this.Handle);    // Add our window to the clipboard's format listener list.

覆盖WndProc方法,以便我们可以在发送 WM_CLIPBOARDUPDATE 时捕获。

protected override void WndProc(ref Message m)
{
    base.WndProc(ref m);

    if (m.Msg == WM_CLIPBOARDUPDATE)
    {
        IDataObject iData = Clipboard.GetDataObject();      // Clipboard's data.

        /* Depending on the clipboard's current data format we can process the data differently.
         * Feel free to add more checks if you want to process more formats. */
        if (iData.GetDataPresent(DataFormats.Text))
        {
            string text = (string)iData.GetData(DataFormats.Text);
            // do something with it
        }
        else if (iData.GetDataPresent(DataFormats.Bitmap))
        {
            Bitmap image = (Bitmap)iData.GetData(DataFormats.Bitmap);
            // do something with it
        }
    }
}

最后确保在关闭表单之前从剪贴板格式侦听器列表中删除主窗口。

private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
    RemoveClipboardFormatListener(this.Handle);     // Remove our window from the clipboard's format listener list.
}

答案 7 :(得分:1)

SharpClipboard作为一种库可能会更有好处,因为它将相同的功能封装到一个精细的组件库中。然后,您可以访问其ClipboardChanged事件,并在剪切/复制它们时检测各种数据格式。

您可以选择要监视的各种数据格式:

var clipboard = new SharpClipboard();

clipboard.ObservableFormats.Texts = true;
clipboard.ObservableFormats.Files = true;
clipboard.ObservableFormats.Images = true;
clipboard.ObservableFormats.Others = true;

以下是使用其ClipboardChanged事件的示例:

private void ClipboardChanged(Object sender, ClipboardChangedEventArgs e)
{
    // Is the content copied of text type?
    if (e.ContentType == SharpClipboard.ContentTypes.Text)
    {
        // Get the cut/copied text.
        Debug.WriteLine(clipboard.ClipboardText);
    }

    // Is the content copied of image type?
    else if (e.ContentType == SharpClipboard.ContentTypes.Image)
    {
        // Get the cut/copied image.
        Image img = clipboard.ClipboardImage;
    }

    // Is the content copied of file type?
    else if (e.ContentType == SharpClipboard.ContentTypes.Files)
    {
        // Get the cut/copied file/files.
        Debug.WriteLine(clipboard.ClipboardFiles.ToArray());

        // ...or use 'ClipboardFile' to get a single copied file.
        Debug.WriteLine(clipboard.ClipboardFile);
    }

    // If the cut/copied content is complex, use 'Other'.
    else if (e.ContentType == SharpClipboard.ContentTypes.Other)
    {
        // Do something with 'e.Content' here...
    }
}

您还可以找出发生剪切/复制事件的应用程序及其详细信息:

private void ClipboardChanged(Object sender, SharpClipboard.ClipboardChangedEventArgs e)
{
    // Gets the application's executable name.
    Debug.WriteLine(e.SourceApplication.Name);
    // Gets the application's window title.
    Debug.WriteLine(e.SourceApplication.Title);
    // Gets the application's process ID.
    Debug.WriteLine(e.SourceApplication.ID.ToString());
    // Gets the application's executable path.
    Debug.WriteLine(e.SourceApplication.Path);
}

还有其他事件,例如MonitorChanged事件,它在禁用剪贴板监视时会监听,这意味着您可以在运行时启用或禁用监视剪贴板。

除此之外,由于它是组件,因此您可以通过将其拖放到Windows窗体中,在 Designer View 中使用它,这使任何人都可以非常轻松地自定义其选项并处理其内置事件。

SharpClipboard似乎是.NET中剪贴板监视方案的最佳选择。

答案 8 :(得分:0)

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

        private void Form1_Load(object sender, EventArgs e)
        {
            _ClipboardViewerNext = SetClipboardViewer(this.Handle);
        }

        protected override void WndProc(ref System.Windows.Forms.Message m)
        {
            const int WM_DRAWCLIPBOARD = 0x308;

            switch (m.Msg)
            {
                case WM_DRAWCLIPBOARD:
                    //Clipboard is Change 
                    //your code..............
                    break; 
                default:
                    base.WndProc(ref m);
                    break;
            }
        }

答案 9 :(得分:0)

剪贴板查看器可能会遇到的另一个问题:一段时间后它停止接收 WM_DRAWCLIPBOARD 消息(似乎剪贴板链不知何故损坏)。 我找到的唯一解决方案是在发现链断裂时重新注册剪贴板查看器。

为了我的需要,我创建了 nuget 包 https://github.com/magicmanam/windows-clipboard-viewer,它包装了所有需要的 Windows 消息的处理,并提供了刷新剪贴板查看器的方法。包的描述包含使用示例