我正在编写一个监视剪贴板更改的WPF应用程序。
我写了以下课程:
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");
}
}
所以现在当我启动应用程序时一切正常,当我将文本复制到剪贴板时,事件会被触发一次。当我单击该按钮并再次将文本复制到剪贴板时,该事件将被触发两次。点击另一个按钮,该事件被触发三次。我不知道为什么会这样。有人可以帮帮我吗?
答案 0 :(得分: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;
}
}