Tuesday, June 1, 2010

Уведомление о изменении буфера обмена (Clipboard) Windows в С#, часть 1

Введение

Пожалуй, буфер обмена — одно из найболее часто используемых функций в Windows, да и в других операционных системах. Сегодня мы взглянем на то, как при помощи.net Framework и нескольких API-функций Windows создать простое приложение, которое будет следить за буфером обмена и сообщать нам о изменении его содержимого.

Класс Clipboard

В .net-приложении буфер обмена Windows доступен в любой момент при помощи класса
System. Windows. Forms. Clipboard. Для получения данных, которые находятся в буфере, предназначены четыре метода:
public static string Clipboard.GetText();
public static string Clipboard.GetText(TextDataFormat format);
public static Object Clipboard.GetData(string format);
public static IDataObject Clipboard.GetDataObject();
Найболее простой из них — первый метод, который получает текстовые данные из буфера обмена. Если в буфере не текстовые данные (а, например, изображение), то результатом будет пустая строка.
Метод GetText (TextDataFormat format) отличается от собственной перегрузки без параметров тем, что позволяет получить текст из буфера обмена в определенном формате (всего их пять — Text, UnicodeText, Rtf, Html и CommaSeparatedValue). Опять же, если в буфере обмена не текстовые данные, либо данные не запрошенного формата, результатом вызова метода будет пустая строка.
Для проверки того, содержит ли буфер текст нужного формата, существуют следующие методы:
public static bool ContainsText();
public static bool ContainsText(TextDataFormat format);
Синтаксис их вызова идентичен методам GetText, но результатом будет true, если буфер обмена содержит текст заданного формата и false в обратном случае.
Последние два Get-метода предназначены для получения объекта определенного формата из буфера (о других форматах мы поговорим в следущей части статьи) и имеют соответствующие Contains-методы.

Уведомления об изменении содержимого буфера обмена

Что нужно сделать, чтобы узнать, когда содержимое буфера изменилось? Простейшее решение — воспользоваться таймером и периодически опрашивать буфер на предмет изменения. Это не лучшее решение — содержимое буфера может измениться несколько раз между итерациями таймера, а слишком частый опрос может быть губительным для производительности.
В этой задаче нам на помощь приходят три WinAPI-функции:
//Register a window handle as a clipboard viewer
[DllImport("User32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SetClipboardViewer(IntPtr hWnd);
//Remove a window handle from the clipboard viewers chain
[DllImport("User32.dll", CharSet = CharSet.Auto)]
public static extern bool ChangeClipboardChain(
    IntPtr hWndRemove,  // handle to window to remove
    IntPtr hWndNewNext  // handle to next window
    );
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);
Функция SetClipboardViewer регистрирует окно для получения сообщения WM_DRAWCLIPBOARD (о нём ниже), ChangeClipboardChain предназначена для удаления идентификатора окна из цепочки получения WM_DRAWCLIPBOARD, а SendMessage — функция WinAPI для отправки сообщений Windows другим окнам.
Сообщение WM_DRAWCLIPBOARD рассылается системой при изменении содержимого буфера обмена. С помощью функции SetClipboardViewer мы подпишем наше приложение в цепочку получателей этого сообщения. Небольшой сложностью этого подхода будет то, что наше приложение должно будет передать сообщение дальше по цепочке — адрес окна, которому нужно будет передать сообщение, вернет функция SetClipboardViewer.
Кроме обработки WM_DRAWCLIPBOARD, нашему приложению придётся обрабатывать также и сообщение WM_CHANGECBCHAIN. Система рассылает это сообщение, когда исключает одно из окон из цепочки рассылки.

Время для кода

Итак, откроем Visual Studio, создадим новый проект типа Windows Forms Application, и приступим к работе. Создадим простой интерфейс для приложения:
Кнопки Start и Stop Monitoring будут соответственно регистрировать окно нашего приложения в цепочке уведомлений и удалять его оттуда, а в текстовое поле будет выводиться изменения в буфере и прочая диагностическая информация. Для этого будет предназначена вспомогательная функция Output:
private void Output(string message)
{
    tbOutput.Text += message + Environment.NewLine; // добавить наше сообщение
    tbOutput.SelectionStart = tbOutput.Text.Length; // установить курсор в конец текста
    tbOutput.ScrollToCaret(); // прокрутка до курсора для показа сообщения
}
Добавим переменную, в которой будем хранить дескриптор окна, которому будем передавать сообщения WM_DRAWCLIPBOARD и WM_CHANGECBCHAIN:
private IntPtr nextClipboardViewer;
Добавим обработчики событий для кнопок:
private void btnStart_Click(object sender, EventArgs e)
{
    nextClipboardViewer = SetClipboardViewer(this.Handle);
    Output("Registered clipboard viewer.");
}

private void btnStop_Click(object sender, EventArgs e)
{
    ChangeClipboardChain(this.Handle, nextClipboardViewer);
    Output("Unregistered clipboard viewer.");
}
Переопределим функцию WndProc, чтобы обработать пришедшие нам сообщения:
protected override void WndProc(ref Message m)
{
    switch (m.Msg)
    {
        case WM_DRAWCLIPBOARD:
            // содержимое буфера обмена изменилось
            {
                // реагируем на изменение буфера
                ClipboardChanged();
                // пересылаем сообщение следующему окну в цепочке
                SendMessage(nextClipboardViewer, WM_DRAWCLIPBOARD, IntPtr.Zero, IntPtr.Zero);
                break;
            }
        case WM_CHANGECBCHAIN:
            // цепочка приложений, подписанных на WM_DRAWCLIPBOARD изменилась
            {
                if (m.WParam == nextClipboardViewer)
                {
                    // окно, которому мы передавали WM_DRAWCLIPBOARD, удалено из
                    // цепочки, и нам нужно обновить переменную
                    nextClipboardViewer = m.LParam;
                }
                else
                {
                    // просто передаем новости дальше
                    SendMessage(nextClipboardViewer, WM_CHANGECBCHAIN, m.WParam, m.LParam);
                }
                m.Result = IntPtr.Zero; // уведомляем систему об обработке сообщения
                break;
            }
        default:
            // поведение для остальных сообщений - по умолчанию
            {
                base.WndProc(ref m); 
                break;
            }
    }
}
И последнее, что осталось сделать — реализовать метод ClipboardChanged:
private void ClipboardChanged()
{
    Output("The clipboard content has been changed.");
    string s = "New clipboard content: ";
    if (Clipboard.ContainsText()) // будем выводить только текстовое содержимое
    {
        s += Clipboard.GetText();
    }
    else
    {
        s += "[non-text]";
    }
    Output(s);
}
Готово! Монитор буфера обмена готов к работе:
Обратите внимание, что при регистрации монитора Windows тут же присылает нам сообщение о изменении буфера — что-то вроде «добро пожаловать» :)
В следующей части мы рассмотрим работу с другими форматами данных буфера обмена, и пропагрейдим наш монитор до менеджера буфера обмена — с историей и прочими полезными функциями.
Исходник проекта (Visual Studio 2008)
SetClipboardViewer Function (Windows)
Clipboard Class (System. Windows. Forms)

22 comments:

  1. В свое время столкнулся с непонятными (поначалу) ошибками при работе с буфером обмена, когда нужно было программно вставить в него некий текст. Как оказалось, это применимо и к SetClipboardViewer :)
    Поэтому предлагаю для решения простую задачку: посмотреть, как работает пример из этого поста при запущенной вот этой маленькой программке. Выяснить, что и почему происходит, и как с этим бороться.
    Чур без рефлектора :)

    ReplyDelete
  2. А не сохранилось ли у вас исходника проекта?

    ReplyDelete
  3. advanced-systemcare-pro-crack out from the Audience. By way of instance, many modern PCs have SSDs that do not profit from defragging (it may decrease their lifespan), therefore even though Advanced SystemCare carries a defrag tool.
    new crack

    ReplyDelete
  4. paragon ntfs khokharpc Thanks for this post, I really found this very helpful. And blog about best time to post on cuber law is very useful.

    ReplyDelete
  5. ccleaner pro atozcracksoft Thank you, I’ve recently been searching for information about this subject for a long time and yours is the best I have found out so far.

    ReplyDelete
  6. This article is so innovative and well constructed I got lot of information from this post. Keep writing related to the topics on your site. /wondershare-allmytube-crack

    ReplyDelete
  7. Thanks for sharing such great information, I highly appreciate your hard-working skills which are quite beneficial for me. easeus-partition-master-crack

    ReplyDelete
  8. https://dotnetshack.blogspot.com/2010/06/clipboard-windows-1.html?showComment=1534655272377#c263583032197941751

    ReplyDelete
  9. https://dotnetshack.blogspot.com/2010/06/clipboard-windows-1.html?showComment=1534655272377#c263583032197941751pykirill.blogspot.com/2012/01/blog-post.html?showComment=1534655248224#c4894164982216525810

    ReplyDelete
  10. Great article. I really like this article. I really very impressed by this post very much. This post gave me a lot of information on this topic. Thanks for sharing it with us.
    lansweeper free download
    origin pro crack
    voicemod torrent
    quicktime pro torrent
    clean master pro free
    Crack Like

    ReplyDelete
  11. This is a fantastic blog! Your website is also very quick to load!
    What kind of web host are you using? Is it possible for you to send me your affiliate link for your web host?
    I wish my website was as quick to load as yours.
    imageranger pro edition crack
    activepresenter professional edition crack
    wondershare filmora crack
    microsoft office professional crack

    ReplyDelete
  12. https://newcrackkey.com/flvto-youtube-downloader-crack-latest/ You don’t have to set up a separate area to spread your imaginative textures as this program allows you to do this task anytime, anywhere?

    ReplyDelete
  13. Hey! This is my first visit to your blog.
    We are a collection of volunteers starting with one
    a new project in the community in the same niche.
    Your blog has provided us with useful information to work with. YOU
    did a fantastic job!
    macrium reflect crack
    norton antivirus crack
    magix video pro crack
    coreldraw graphics suite crack

    ReplyDelete
  14. Hey! This is my first visit to your blog.
    We are a collection of volunteers starting with one
    a new project in the community in the same niche.
    Your blog has provided us with useful information to work with. YOU
    did a fantastic job!
    Downie 4.4.4 for Mac
    WonderPen 2.0.5 for Mac
    Bigasoft Audio Converter 5.6.0 for Mac

    ReplyDelete
  15. Many thanks to the owner of this site for sharing this interesting post in this site. It's really fun. You are a very experienced blogger.
    I have joined your RSS feed and look forward to some of your best posts.
    Also, I have shared your site on my social network. Wow, amazing blog design! How long have you been blogging?
    you make blogging easier.
    zorin os crack
    speedcommander pro crack
    x mirage crack

    ReplyDelete

  16. Hey! This is my first visit to your blog.
    We are a collection of volunteers starting with one
    a new project in the community in the same niche.
    Your blog has provided us with useful information to work with. YOU
    did a fantastic job! mountain duck crack
    coreldraw x
    macrium reflect with crack
    quarkxpress v15 2 1 crack
    windows 10 iso all in one
    macx dvd ripper pro crack
    vivaldi crack
    skype crack

    ReplyDelete
  17. I am very impressed with your post because this post is very beneficial for me and provides new knowledge to me.
    CCleaner Pro
    DVDFab Enlarger AI
    360 Total Security

    ReplyDelete