2013-03-11 66 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; 
    } 
}