【.net 深呼吸】监听剪贴板更新(针对Vista之后系统)

针对 XP 及以前的监视剪贴板更改的方法就不讲了,因为 XP 已严重过时。本篇老周介绍的方法面向 Vista 以上的系统。

在托管应用程序中监听剪贴板更新行为必须用到 Win 32 API ,具体做法,我先简单说一下。

首先,调用 AddClipboardFormatListener 函数来向窗口注册监听行为,它需要一个窗口句柄作为传入参数,该句柄所指的窗口即是监听剪贴板更新的窗口。

然后,当剪贴板的内容被更新,处理程序会收到一条 WM_CLIPBOARDUPDATE 消息。我们在应用程序中,只要收到这条消息,就说明剪贴板的内容已被更新。

 

WM_CLIPBOARDUPDATE 消息的宏定义如下:

#define WM_CLIPBOARDUPDATE              0x031D

这个消息的 wParam 和 lParam 参数都不曾使用,所以我们不必理会这两个参数值。如果用户已处理该消息,应当返回 0。

 

AddClipboardFormatListener 函数的原型如下:

BOOL WINAPI AddClipboardFormatListener(
  _In_ HWND hwnd
);

在托管代码中调用它,要先进行导入。

        [DllImport("User32.dll")]
        static extern bool AddClipboardFormatListener(IntPtr hwnd);

 

好,基本理论说完了,下面我们来看看如何在WPF程序中监听剪贴板更新。

由于此功能实为WPF与 Win32 的交互操作,因此,要用到 HwndSource 类,这个类公开了一个 AddHook 方法,调用这个方法可以添加一个 HwndSourceHook 委托实例,当窗口接收到消息时,就会调用这个委托。

该委托的定义如下。

delegate IntPtr HwndSourceHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled);

你一定会猛然发现,这个委托很像 WinProc 函数指针。msg 参数就是被拦截到的消息。在与该委托绑定的方法中,我们可以对收到的消息进行筛选,因为我们这里只关心 WM_CLIPBOARDUPDATE 消息,其他的咱们不管。

        private IntPtr OnHooked(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            if (msg == WM_CLIPBOARDUPDATE)
            {
                ClipboardUpdated?.Invoke(this, EventArgs.Empty);
                return IntPtr.Zero;
            }
            return IntPtr.Zero;
        }

ClipboardUpdated 事件是我封装时定义的,这是为了方便引发。

  public event EventHandler ClipboardUpdated;

 

还有一件事,各位会发现,HwndSource 实例创建时需要与一个窗口的句柄绑定,那么,如何获取到 Window 实例的句柄呢,这就要用到一个帮助类—— WindowInteropHelper。有了它,想得到窗口的句柄就很简单了。

            WindowInteropHelper helper = new WindowInteropHelper(window);
            _hwndSource = HwndSource.FromHwnd(helper.Handle);

 

在添加 hook 处理之前,一定要记得调用 AddClipboardFormatListener 函数为窗口注册监听行为。

bool r = AddClipboardFormatListener(_hwndSource.Handle);

要是监听行为注册成功,就可以添加 hook 了。

            if (r)
            {
                _hwndSource.AddHook(new HwndSourceHook(OnHooked));
            }

 

那么,咱们封装的这些代码如何用到窗口代码中呢。Window 有一个 SourceInitialized 事件,当句柄初始化完成就会发生。我们可以重写 OnSourceInitialized 方法,然后在方法中使用我们上面封装的代码。

        ClipboardHooker m_clipboardHooker;
        protected override void OnSourceInitialized(EventArgs e)
        {
            base.OnSourceInitialized(e);
            m_clipboardHooker = new ClipboardHooker(this);
            m_clipboardHooker.ClipboardUpdated += OnClipboardUpdated;
        }

        private void OnClipboardUpdated(object sender, EventArgs e)
        {
            tb.Text = "老板,有人修改了剪贴板。";
            IDataObject data = Clipboard.GetDataObject();
            string[] fs = data.GetFormats();
            tb.Text += $"\n数据格式:{string.Join("", fs)}";
        }

 

只要监听到剪贴板被更新,那么要获取剪贴板上的数据就很容易了,因为System.Windows下面已经有一个 Clipboard 类,它有一堆静态方法,可以直接读写剪贴板上的内容。

 

运行程序后,随便复制点东东到剪贴板中,就会看到程序有反应了。如下面高清无码截图所示。

 

好了,本文就扯到这里了。

本文示例源代码下载

 

posted @ 2017-07-15 12:14  东邪独孤  阅读(2015)  评论(0编辑  收藏  举报