Введение
Пожалуй, буфер обмена — одно из найболее часто используемых функций в Windows, да и в других операционных системах. Сегодня мы взглянем на то, как при помощи.net Framework и несколькихКласс Clipboard
В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 в обратном случае.
Последние два
Уведомления об изменении содержимого буфера обмена
Что нужно сделать, чтобы узнать, когда содержимое буфера изменилось? Простейшее решение — воспользоваться таймером и периодически опрашивать буфер на предмет изменения. Это не лучшее решение — содержимое буфера может измениться несколько раз между итерациями таймера, а слишком частый опрос может быть губительным для производительности.В этой задаче нам на помощь приходят три
//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)


