2

クリップボードの変更を監視する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回発生します。なぜこれが起こっているのか分かりません。誰かが私を助けることができますか?

4

1 に答える 1

1

さて、私はこれの助けを借りてコードを変更しました、そして明らかに私は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;
    }
}
于 2013-03-11T19:29:15.680 に答える