クリップボードの変更を監視する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");
}
}
したがって、アプリケーションを起動するとすべて問題なく、テキストをクリップボードにコピーするとイベントが1回発生します。ボタンをクリックして、テキストをクリップボードに再度コピーすると、イベントが2回発生します。別のボタンをクリックすると、イベントが3回発生します。なぜこれが起こっているのか分かりません。誰かが私を助けることができますか?