Уведомление об изменении буфера обмена? - PullRequest
15 голосов
/ 23 июня 2011

Я знаю, как помещать содержимое и извлекать содержимое из буфера обмена.

Однако между этими двумя операциями возможна другая операция для изменения содержимого буфера обмена.

ЕстьЕсть ли способ получить уведомление, когда какое-либо приложение изменяет буфер обмена?

Ответы [ 4 ]

20 голосов
/ 05 октября 2012

Я знаю, что этот ответ слишком поздний Единственная ссылка, которую вам нужно добавить, - это окна Windows в вашем приложении wpf. Я создал обертку для функциональности, которую я нашел в Интернете. Большинство примеров делают больше, чем мне нужно, поэтому я решил создать свой собственный класс. Мне нравится выделять проблемы, поэтому этот класс просто слушает, когда меняется буфер обмена, и сообщает вам тип данных, находящихся в буфере обмена. Например, это текст? картинка? или что?

В любом случае, вот моя обертка:

using System;
using System.Windows.Forms;
using System.Threading;
using System.Runtime.InteropServices;

public static class ClipboardMonitor 
{
    public delegate void OnClipboardChangeEventHandler(ClipboardFormat format, object data);
    public static event OnClipboardChangeEventHandler OnClipboardChange;

    public static void Start()
    {
        ClipboardWatcher.Start();
        ClipboardWatcher.OnClipboardChange += (ClipboardFormat format, object data) =>
        {
            if (OnClipboardChange != null)
                OnClipboardChange(format, data);
        };
    }

    public static void Stop()
    {
        OnClipboardChange = null;
        ClipboardWatcher.Stop();
    }

    class ClipboardWatcher : Form
    {
        // static instance of this form
        private static ClipboardWatcher mInstance;

        // needed to dispose this form
        static IntPtr nextClipboardViewer;

        public delegate void OnClipboardChangeEventHandler(ClipboardFormat format, object data);
        public static event OnClipboardChangeEventHandler OnClipboardChange;

        // start listening
        public static void Start()
        {
            // we can only have one instance if this class
            if (mInstance != null)
                return;

            Thread t = new Thread(new ParameterizedThreadStart(x =>
            {
                Application.Run(new ClipboardWatcher());
            }));
            t.SetApartmentState(ApartmentState.STA); // give the [STAThread] attribute
            t.Start();
        }

        // stop listening (dispose form)
        public static void Stop()
        {
            mInstance.Invoke(new MethodInvoker(() =>
            {
                ChangeClipboardChain(mInstance.Handle, nextClipboardViewer);
            }));
            mInstance.Invoke(new MethodInvoker(mInstance.Close));

            mInstance.Dispose();

            mInstance = null;
        }

        // on load: (hide this window)
        protected override void SetVisibleCore(bool value)
        {
            CreateHandle();

            mInstance = this;

            nextClipboardViewer = SetClipboardViewer(mInstance.Handle);

            base.SetVisibleCore(false);
        }

        [DllImport("User32.dll", CharSet = CharSet.Auto)]
        public static extern IntPtr SetClipboardViewer(IntPtr hWndNewViewer);

        [DllImport("User32.dll", CharSet = CharSet.Auto)]
        public static extern bool ChangeClipboardChain(IntPtr hWndRemove, IntPtr hWndNewNext);

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern int SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);

        // defined in winuser.h
        const int WM_DRAWCLIPBOARD = 0x308;
        const int WM_CHANGECBCHAIN = 0x030D;

        protected override void WndProc(ref Message m)
        {
            switch (m.Msg)
            {
                case WM_DRAWCLIPBOARD:
                    ClipChanged();
                    SendMessage(nextClipboardViewer, m.Msg, m.WParam, m.LParam);
                    break;

                case WM_CHANGECBCHAIN:
                    if (m.WParam == nextClipboardViewer)
                        nextClipboardViewer = m.LParam;
                    else
                        SendMessage(nextClipboardViewer, m.Msg, m.WParam, m.LParam);
                    break;

                default:
                    base.WndProc(ref m);
                    break;
            }
        }

        static readonly string[] formats = Enum.GetNames(typeof(ClipboardFormat));

        private void ClipChanged()
        {
            IDataObject iData = Clipboard.GetDataObject();

            ClipboardFormat? format = null;

            foreach (var f in formats)
            {
                if (iData.GetDataPresent(f))
                {
                    format = (ClipboardFormat)Enum.Parse(typeof(ClipboardFormat), f);
                    break;
                }
            }

            object data = iData.GetData(format.ToString());

            if (data == null || format == null)
                return;

            if (OnClipboardChange != null)
                OnClipboardChange((ClipboardFormat)format, data);
        }


    }
}

public enum ClipboardFormat : byte
{
    /// <summary>Specifies the standard ANSI text format. This static field is read-only.
    /// </summary>
    /// <filterpriority>1</filterpriority>
    Text,
    /// <summary>Specifies the standard Windows Unicode text format. This static field
    /// is read-only.</summary>
    /// <filterpriority>1</filterpriority>
    UnicodeText,
    /// <summary>Specifies the Windows device-independent bitmap (DIB) format. This static
    /// field is read-only.</summary>
    /// <filterpriority>1</filterpriority>
    Dib,
    /// <summary>Specifies a Windows bitmap format. This static field is read-only.</summary>
    /// <filterpriority>1</filterpriority>
    Bitmap,
    /// <summary>Specifies the Windows enhanced metafile format. This static field is
    /// read-only.</summary>
    /// <filterpriority>1</filterpriority>
    EnhancedMetafile,
    /// <summary>Specifies the Windows metafile format, which Windows Forms does not
    /// directly use. This static field is read-only.</summary>
    /// <filterpriority>1</filterpriority>
    MetafilePict,
    /// <summary>Specifies the Windows symbolic link format, which Windows Forms does
    /// not directly use. This static field is read-only.</summary>
    /// <filterpriority>1</filterpriority>
    SymbolicLink,
    /// <summary>Specifies the Windows Data Interchange Format (DIF), which Windows Forms
    /// does not directly use. This static field is read-only.</summary>
    /// <filterpriority>1</filterpriority>
    Dif,
    /// <summary>Specifies the Tagged Image File Format (TIFF), which Windows Forms does
    /// not directly use. This static field is read-only.</summary>
    /// <filterpriority>1</filterpriority>
    Tiff,
    /// <summary>Specifies the standard Windows original equipment manufacturer (OEM)
    /// text format. This static field is read-only.</summary>
    /// <filterpriority>1</filterpriority>
    OemText,
    /// <summary>Specifies the Windows palette format. This static field is read-only.
    /// </summary>
    /// <filterpriority>1</filterpriority>
    Palette,
    /// <summary>Specifies the Windows pen data format, which consists of pen strokes
    /// for handwriting software, Windows Forms does not use this format. This static
    /// field is read-only.</summary>
    /// <filterpriority>1</filterpriority>
    PenData,
    /// <summary>Specifies the Resource Interchange File Format (RIFF) audio format,
    /// which Windows Forms does not directly use. This static field is read-only.</summary>
    /// <filterpriority>1</filterpriority>
    Riff,
    /// <summary>Specifies the wave audio format, which Windows Forms does not directly
    /// use. This static field is read-only.</summary>
    /// <filterpriority>1</filterpriority>
    WaveAudio,
    /// <summary>Specifies the Windows file drop format, which Windows Forms does not
    /// directly use. This static field is read-only.</summary>
    /// <filterpriority>1</filterpriority>
    FileDrop,
    /// <summary>Specifies the Windows culture format, which Windows Forms does not directly
    /// use. This static field is read-only.</summary>
    /// <filterpriority>1</filterpriority>
    Locale,
    /// <summary>Specifies text consisting of HTML data. This static field is read-only.
    /// </summary>
    /// <filterpriority>1</filterpriority>
    Html,
    /// <summary>Specifies text consisting of Rich Text Format (RTF) data. This static
    /// field is read-only.</summary>
    /// <filterpriority>1</filterpriority>
    Rtf,
    /// <summary>Specifies a comma-separated value (CSV) format, which is a common interchange
    /// format used by spreadsheets. This format is not used directly by Windows Forms.
    /// This static field is read-only.</summary>
    /// <filterpriority>1</filterpriority>
    CommaSeparatedValue,
    /// <summary>Specifies the Windows Forms string class format, which Windows Forms
    /// uses to store string objects. This static field is read-only.</summary>
    /// <filterpriority>1</filterpriority>
    StringFormat,
    /// <summary>Specifies a format that encapsulates any type of Windows Forms object.
    /// This static field is read-only.</summary>
    /// <filterpriority>1</filterpriority>
    Serializable,
}

Я знаю, что это может быть слишком много, но после упаковки это должно выглядеть так: enter image description here

А затем вы используете класс как:

    static void Main(string[] args)
    {

        ClipboardMonitor.Start();

        ClipboardMonitor.OnClipboardChange += new ClipboardMonitor.OnClipboardChangeEventHandler(ClipboardMonitor_OnClipboardChange);

        Console.Read();

        ClipboardMonitor.Stop(); // do not forget to stop



    }

    static void ClipboardMonitor_OnClipboardChange(ClipboardFormat format, object data)
    {
        Console.WriteLine("Clipboard changed and it has the format: "+format.ToString());
    }
4 голосов
/ 23 июня 2011

Все, что я мог найти, это Монитор буфера обмена , написанный на C # / VB.NET.Я вижу WPF и WinForms, поэтому я предполагаю это жизнеспособные варианты.

Включает пинвокинг некоторые методы из user32 dll.

EDIT

Во время редактирования исходная ссылка выше не работает.Вот снимок archive.org

1 голос
/ 16 августа 2011

Однако между этими двумя операциями можно изменить содержимое буфера обмена для другой операции.

Когда приложение вызывает OpenClipboard (), никакое другое приложение не может использоватьбуфер обмена.Как только приложение, которое заблокировало буфер обмена, вызывает метод CloseClipboard (), тогда любое приложение может использовать и заблокировать буфер обмена.

Есть ли способ получить уведомление, когда какое-либо приложение изменяет буфер обмена?

Да.См. API SetClipboardViewer () и сообщения WM_DRAWCLIPBOARD и WM_CHANGECBCHAIN.

Более подробная информация здесь: http://msdn.microsoft.com/en-us/library/ms649016(v=vs.85).aspx#_win32_Adding_a_Window_to_the_Clipboard_Viewer_Chain

0 голосов
/ 24 июня 2011

Вот статья, которую я сделал несколько лет назад, с подробным описанием буфера обмена, даже мониторинга через Windows API: http://www.clipboardextender.com/developing-clipboard-aware-programs-for-windows/6 Обратите особое внимание на раздел «Распространенные ошибки». Вряд ли кто-то делает это правильно с первого раза.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...