1. 程式人生 > >【.net 深呼吸】監聽剪貼板更新(針對Vista之後系統)

【.net 深呼吸】監聽剪貼板更新(針對Vista之後系統)

demo get protect empty helper obj 容易 urn 篩選

針對 XP 及以前的監視剪貼板更改的方法就不講了,因為 XP 已嚴重過時。本篇老周介紹的方法面向 Vista 以上的系統。

在托管應用程序中監聽剪貼板更新行為必須用到 Win 32 API ,具體做法,我先簡單說一下。

首先,調用 AddClipboardFormatListener 函數來向窗口註冊監聽行為,它需要一個窗口句柄作為傳入參數,該句柄所指的窗口即是監聽剪貼板更新的窗口。

然後,當剪貼板的內容被更新,處理程序會收到一條 WM_CLIPBOARDUPDATE 消息。我們在應用程序中,只要收到這條消息,就說明剪貼板的內容已被更新。

WM_CLIPBOARDUPDATE 消息的宏定義如下:

#define WM_CLIPBOARDUPDATE              0x031D

這個消息的 wParamlParam 參數都不曾使用,所以我們不必理會這兩個參數值。如果用戶已處理該消息,應當返回 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 類,它有一堆靜態方法,可以直接讀寫剪貼板上的內容。

運行程序後,隨便復制點東東到剪貼板中,就會看到程序有反應了。如下面高清無碼截圖所示。

技術分享

好了,本文就扯到這裏了。

本文示例源代碼下載

【.net 深呼吸】監聽剪貼板更新(針對Vista之後系統)