2013-03-11 82 views
2

我正在編寫監視剪貼板更改的WPF應用程序。ClipboardInterop內容更改了兩次觸發

我寫了下面的類:

public class ClipboardInterop : IDisposable 
{ 
    public event EventHandler ClipboardContentChanged; 

    private void OnClipboardContentChanged() 
    { 
     var handlers = ClipboardContentChanged; 
     if (handlers != null) 
     { 
      handlers(this, new EventArgs()); 
     } 
    } 

    public static ClipboardInterop GetClipboardInterop(Window window) 
    { 
     var hwndSource = PresentationSource.FromVisual(window) as HwndSource; 
     if (hwndSource == null) return null; 

     return new ClipboardInterop(hwndSource); 
    } 

    private IntPtr _thisHandle; 
    private IntPtr _nextHandle; 
    private HwndSource _hwndSource; 

    public bool IsListening { get; private set; } 

    private ClipboardInterop(HwndSource hwndSource) 
    { 
     _hwndSource = hwndSource; 
     _thisHandle = hwndSource.Handle; 
     IsListening = false; 
    } 

    public bool StartViewingClipboard() 
    { 
     Win32.SetLastError(0); 
     _nextHandle = Win32.SetClipboardViewer(_thisHandle); 
     if (_nextHandle == IntPtr.Zero) 
     { 
      UInt32 eCode = Win32.GetLastError(); 
      if (eCode != 0) 
      { 
       return false; 
      } 
     } 

     _hwndSource.AddHook(HwndSourceHook); 
     IsListening = true; 
     return true; 
    } 


    public bool StopViewingClipboard() 
    { 
     Win32.SetLastError(0); 
     Win32.ChangeClipboardChain(_thisHandle, _nextHandle); 
     UInt32 eCode = Win32.GetLastError(); 
     IsListening = false; 
     return eCode == 0; 
    } 

    private IntPtr HwndSourceHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) 
    { 
     switch (msg) 
     { 
      case Win32.WM_CHANGECBCHAIN: 
       _nextHandle = lParam; 
       if (_nextHandle != IntPtr.Zero) 
       { 
        Win32.SendMessage(_nextHandle, (UInt32)msg, wParam, lParam); 
       } 

       break; 

      case Win32.WM_DRAWCLIPBOARD: 

       OnClipboardContentChanged(); 

       if (_nextHandle != IntPtr.Zero) 
       { 
        Win32.SendMessage(_nextHandle, (UInt32)msg, wParam, lParam); 
       } 

       break; 
     } 
     return IntPtr.Zero; 
    } 

    public void Dispose() 
    { 
     if (IsListening) 
      StopViewingClipboard(); 
     _hwndSource = null; 
     _nextHandle = IntPtr.Zero; 
     _thisHandle = IntPtr.Zero; 
    } 
} 

Win32.cs樣子:

internal static class Win32 
{ 
    /// <summary> 
    ///  The WM_DRAWCLIPBOARD message notifies a clipboard viewer window that 
    ///  the content of the clipboard has changed. 
    /// </summary> 
    internal const int WM_DRAWCLIPBOARD = 0x0308; 

    /// <summary> 
    ///  A clipboard viewer window receives the WM_CHANGECBCHAIN message when 
    ///  another window is removing itself from the clipboard viewer chain. 
    /// </summary> 
    internal const int WM_CHANGECBCHAIN = 0x030D; 

    [DllImport("user32.dll", SetLastError = true)] 
    public static extern IntPtr SetClipboardViewer(
     IntPtr hWndNewViewer); 

    [DllImport("user32.dll")] 
    [return: MarshalAs(UnmanagedType.Bool)] 
    public static extern bool ChangeClipboardChain(
     IntPtr hWndRemove, 
     IntPtr hWndNewNext); 

    [DllImport("user32.dll", SetLastError = true)] 
    public static extern IntPtr SendMessage(
     IntPtr hWnd, 
     UInt32 msg, 
     IntPtr wParam, 
     IntPtr lParam); 

    [DllImport("kernel32.dll")] 
    public static extern void SetLastError(
     UInt32 errorCode); 

    [DllImport("kernel32.dll")] 
    public static extern UInt32 GetLastError(); 
} 

MainWindow.xaml

<Window x:Class="WpfApplication1.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     Title="MainWindow" Height="350" Width="525" 
     Loaded="MainWindow_OnLoaded"> 
    <Button Content="Toggle" Click="ButtonBase_OnClick"></Button> 
</Window> 

MainWindow.xaml.cs

public partial class MainWindow : Window 
{ 
    private ClipboardInterop _clipboardInterop; 

    public MainWindow() 
    { 
     InitializeComponent(); 
    } 

    private void ButtonBase_OnClick(object sender, RoutedEventArgs e) 
    { 
     _clipboardInterop.StopViewingClipboard(); 
     _clipboardInterop.StartViewingClipboard(); 
    } 

    private void MainWindow_OnLoaded(object sender, RoutedEventArgs e) 
    { 
     _clipboardInterop = ClipboardInterop.GetClipboardInterop(this); 
     _clipboardInterop.StartViewingClipboard(); 
     _clipboardInterop.ClipboardContentChanged += 
      (o, args) => Debug.WriteLine(DateTime.Now.ToLongTimeString() + " Content changed"); 
    } 
} 

所以現在當我啓動應用程序時一切正常,當我將文本複製到剪貼板時,事件被觸發一次。當我點擊按鈕並再次將文本複製到剪貼板時,該事件將被觸發兩次。點擊另一個按鈕並且事件被觸發三次。我不知道爲什麼會發生這種情況。有人可以幫我嗎?

回答

1

那麼我修改我的代碼this的幫助下,顯然我忘了刪除StopViewingClipboard中的鉤子。

這裏的固定碼:

public class ClipboardInterop : IDisposable 
{ 
    public event EventHandler ClipboardContentChanged; 

    private void OnClipboardContentChanged() 
    { 
     var handlers = ClipboardContentChanged; 
     if (handlers != null) 
     { 
      handlers(this, new EventArgs()); 
     } 
    } 

    public static ClipboardInterop GetClipboardInterop(Window window) 
    { 
     var wih = new WindowInteropHelper(window); 
     var hwndSource = HwndSource.FromHwnd(wih.Handle); 
     if (hwndSource == null) 
     { 
      return null; 
     } 

     return new ClipboardInterop(hwndSource); 
    } 

    private IntPtr _hWndNextViewer; 
    private HwndSource _hWndSource; 

    public bool IsViewing { get; private set; } 

    private ClipboardInterop(HwndSource hwndSource) 
    { 
     _hWndSource = hwndSource; 
     IsViewing = false; 
    } 

    public bool StartViewingClipboard() 
    { 
     Win32.SetLastError(0); 
     _hWndNextViewer = Win32.SetClipboardViewer(_hWndSource.Handle); 
     if (_hWndNextViewer == IntPtr.Zero) 
     { 
      UInt32 eCode = Win32.GetLastError(); 
      if (eCode != 0) 
      { 
       return false; 
      } 
     } 
     _hWndSource.AddHook(WinProc); 
     IsViewing = true; 
     return true; 
    } 


    public bool StopViewingClipboard() 
    { 
     Win32.SetLastError(0); 
     Win32.ChangeClipboardChain(_hWndSource.Handle, _hWndNextViewer); 
     _hWndNextViewer = IntPtr.Zero; 
     _hWndSource.RemoveHook(WinProc); 
     UInt32 eCode = Win32.GetLastError(); 
     IsViewing = false; 
     return eCode == 0; 
    } 

    private IntPtr WinProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) 
    { 
     switch (msg) 
     { 
      case Win32.WM_CHANGECBCHAIN: 
       if (wParam == _hWndNextViewer) 
       { 
        _hWndNextViewer = lParam; 
       } 
       else if (_hWndNextViewer != IntPtr.Zero) 
       { 
        Win32.SendMessage(_hWndNextViewer, msg, wParam, lParam); 
       } 
       break; 

      case Win32.WM_DRAWCLIPBOARD: 
       OnClipboardContentChanged(); 
       Win32.SendMessage(_hWndNextViewer, msg, wParam, lParam); 
       break; 

     } 
     return IntPtr.Zero; 
    } 

    public void Dispose() 
    { 
     if (IsViewing) 
      StopViewingClipboard(); 
     _hWndSource = null; 
     _hWndNextViewer = IntPtr.Zero; 
    } 
}