Наблюдение за буфером обмена

117
10 августа 2019, 08:10

Нужно, чтобы код, который проверяет буфер обмена, работал постоянно. Сейчас он у меня срабатывает только по нажатию на кнопку. Как это можно организовать ?

 int i = 0;
            if (e.KeyCode == Keys.A)
            {
                stroka = Clipboard.GetText();
                for (i = 0; i < array.Length; i++)
                {
                    if (array[i] == "")
                    {
                        array[i] = stroka;
                        break;
                    }
                    else if (array[9] != "")
                    {
                       DialogResult dr = MessageBox.Show("Ошибка. Буфер переполнен. Хотите очистить буфер ?","Предупреждение",MessageBoxButtons.OKCancel);
                       if (dr == DialogResult.OK)
                       {
                           for (i = 0; i < array.Length; i++)
                           {
                               array[i] = "";
                           }
                       }
                        break;
                    }
                }
            }
                if (e.KeyCode == Keys.D0)
                {
                    textBox3.Text = array[0];
                }
                if (e.KeyCode == Keys.D1)
                {
                    textBox3.Text = array[1];
                }
                if (e.KeyCode == Keys.D2)
                {
                    textBox3.Text = array[2];
                }
                if (e.KeyCode == Keys.D3)
                {
                    textBox3.Text = array[3];
                }
                if (e.KeyCode == Keys.D4)
                {
                    textBox3.Text = array[4];
                }
                if (e.KeyCode == Keys.D5)
                {
                    textBox3.Text = array[5];
                }
                if (e.KeyCode == Keys.D6)
                {
                    textBox3.Text = array[6];
                }
                if (e.KeyCode == Keys.D7)
                {
                    textBox3.Text = array[7];
                }
                if (e.KeyCode == Keys.D8)
                {
                    textBox3.Text = array[8];
                }
                if (e.KeyCode == Keys.D9)
                {
                    textBox3.Text = array[9];
                }

Копируется что-то в буфер, нажимается кнопка А и из буфера значение заносится в массив, потом снова копируется значение, нажимается А и значение копируется в массив на позицию i+1. А мне нужно, чтобы это происходило без нажатия на кнопку А. Бесконечным циклом не вышло:программа просто не запускается.

Answer 1

Конечно, можно запустить бесконечный цикл где-нибудь в отдельном потоке, но на мой взгляд такое решение как минимум некорректно. Windows предоставляет возможность отслеживать изменения буфера обмена - т.е., изменения будут отслеживаться и обрабатываться в реальном времени. Как я понимаю, вас интересует именно это, а не while (true) :)

В статье за авторством Тома Арчера рассказывается, как организовать отслеживание в WinForms. Приведу краткое переложение раздела "Пошаговые инструкции" (с переводом и некоторыми изменениями в коде).

1

Необходимо вызвать пару функций из библиотеки Win32 —SetClipboardViewer, ChangeClipboardChain, и SendMessage Для того, чтобы сделать это в приложении .NET, необходимо импортировать их, используя атрибут DllImport. В примере ниже эти функции импортируются в приложение WinForms:

using System.Runtime.InteropServices;
...
public partial class frmMain : Form
{
  [DllImport("User32.dll")]
  protected static extern int
            SetClipboardViewer(int 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);
  ...

2

Определим член класса для помещения текущего окна в цепь уведомлений буфера Оригинальный текст: Define a class member to hold the current first window in the Clipboard notification chain:

public partial class frmMain : Form
    {
        ...
        IntPtr nextClipboardViewer;

3

Вызовем функцию SetClipboardViewer. В примере она вызывается в конструкторе формы:

public frmMain()
{
    InitializeComponent();
    nextClipboardViewer = (IntPtr)SetClipboardViewer((int)
            this.Handle);
}

4

Внутри класса frmMain переопределим метод WndProc. В примере обрабатываются только два сообщения: WM_DRAWCLIPBOARD и WM_CHANGECBCHAIN.

  • В коде, обрабатывающем WM_DRAWCLIPBOARD, вызывается пользовательский метод DisplayClipboardData(), который выводит содержимое буфера; затем то же сообщение передаётся следующему в цепи окну.

  • В коде, обрабатывающем WM_CHANGECBCHAIN, проверяется, является ли окно, удалённое из цепи буфера, следующим (по отношению к текущему). Если да, nextClipboardViewer присваивается окно, следующее за удалённым.

    protected override void WndProc(ref Message m)

    {
            const int WM_DRAWCLIPBOARD = 0x308;
            const int WM_CHANGECBCHAIN = 0x030D;
        switch (m.Msg)
        {
            case WM_DRAWCLIPBOARD:
                DisplayClipboardData();
                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;
        }
    }
    

5

Наконец, окно удаляется из цепи буфера, когда .NET Runtime вызывает метод Dispose класса frmMain

protected override void Dispose( bool disposing )
{
  ChangeClipboardChain(this.Handle, nextClipboardViewer);
  ...

В инструкции не описан, собственно, метод DisplayClipboardData. У меня он выглядит так:

private void DisplayClipboardData()
{
    txtBox.Text = Clipboard.GetText();
}

Т.е., в текстовое поле на форме просто выводится текст из буфера. В вашем случае нужно вставить обработку массива.

На всякий случай, полный листинг того, что вышло у меня:

using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace clipborad
{
    public partial class frmMain : Form
    {
        Random r = new Random();
        [DllImport("User32.dll")]
        protected static extern int
          SetClipboardViewer(int 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);
        IntPtr nextClipboardViewer;
        public frmMain()
        {
            InitializeComponent();
            nextClipboardViewer = (IntPtr)SetClipboardViewer((int)
                         this.Handle);
        }
        protected override void WndProc(ref System.Windows.Forms.Message m)
        {
            // defined in winuser.h
            const int WM_DRAWCLIPBOARD = 0x308;
            const int WM_CHANGECBCHAIN = 0x030D;
            switch (m.Msg)
            {
                case WM_DRAWCLIPBOARD:
                    DisplayClipboardData();
                    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;
            }
        }
        private void DisplayClipboardData()
        {
            txtBox.Text = Clipboard.GetText();
        }
    }
}

Плюс не забываем добавить строку ChangeClipboardChain(this.Handle, nextClipboardViewer); в метод Dispose в файле frmMain.Designer.cs

Код компилируется и прекрасно работает в Win10 x64. Текст из буфера вставляется в текстовое поле сразу после копирования, никаких кнопок дополнительно нажимать не надо

READ ALSO
PHP - удалить из строки HEX c2a00a

PHP - удалить из строки HEX c2a00a

Есть результат вывода строки через функцию bin2hex :

110
PHP Получить переменную из другого файла, не выполняя прочих функций

PHP Получить переменную из другого файла, не выполняя прочих функций

В одном файле, скажем 1php формируется переменная, номер заказа:

123
оптимизация работы с API

оптимизация работы с API

Есть сайт, при загрузке одной страницы идут несколько десятков одинаковых запросов к апи(/api/products, /api/objects), нужно сделать оптимизацию запросов...

116