Контролируем буфер обмена

Привет. В delphi для работы с буфером обмена есть модуль ClipBoard, ещё можно работать с буфером через api. Но мы сегодня не будем углубляться в работу с буфером. Наша задача — отследить изменение буфера обмена.

Это всё сводится к обработке сообщения WM_DRAWCLIPBOARD, но дело в том, что никто просто так нам его не отправит. 

Немного теории.

Есть такое понятие, как «Clipboard chain» — назовём это цепочкой буфера обмена. Фактически в ней содержится список handle’ов окон, которым посылается сообщение WM_DRAWCLIPBOARD. На самом деле оно посылается только первому окну в цепочке. И каждое окно в этой цепочке должно (обязано :) ) обрабатывать это сообщение и ещё одно, о котором речь пойдёт дальше.

Поехали!

Итак, как же нам попасть в эту цепочку? Для этого есть функция SetClipboardViewer.
Создадим новый проект, на событие создания формы (или в любой другой нужный нам момент) напишем:

nwnd:=SetClipboardViewer(Handle);

nwnd — глобальная переменная типа Cardinal (тот же THandle), в неё мы получили следующий в цепочке handle, она нам пригодится в других процедурах.

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

procedure TForm1.FormDestroy(Sender: TObject);
begin
  ChangeClipboardChain(Handle, nwnd);
end;

Функция  ChangeClipboardChain удаляет нас из цепочки. Функция рассылает сообщение WM_CHANGECBCHAIN окнам цепочки, нам это сообщение также предстоит обработать. Обычно возвращает false, true только в случае, если наше окно в цепочке было единственным. Нам результат этой функции не очень-то и важен.

Обработаем WM_CHANGECBCHAIN. Для тех, кто подзабыл: в раздел private типа TForm1 пишем procedure WMChangeCBChain(var msg: TWMChangeCBChain); message WM_CHANGECBCHAIN; и обрабатываем:

procedure TForm1.WMChangeCBChain(var msg: TWMChangeCBChain);
begin
 if msg.Remove=nwnd then
   nwnd:=msg.Next
 else
   SendMessage(nwnd, WM_CHANGECBCHAIN, msg.Remove, msg.Next);
end;

Здесь всё тоже несложно: если удалилось следующее в цепочке окно, берём на заметку — меняем nwnd на теперь действительное следующее окно. Иначе уведомляем следующее окно, как этого требует цепочка.

Осталось только обработать событие  WM_DRAWCLIPBOARD:

procedure TForm1.WMDrawClipBoard(var msg: TWMDrawClipboard);
begin
 if ClipBoard.HasFormat(CF_TEXT) then
   ShowMessage(ClipBoard.AsText)
 else
   ShowMessage('скопирован не текст');
 SendMessage(nwnd, WM_DRAWCLIPBOARD, 0, 0);
end;

Вот так вот, всё просто. Смотрим, что скопировано, делаем, что нам нужно, и отправляем сообщение по цепочке дальше.

Не зная об этом, я раньше ставил таймер, который постоянно следил за буфером, —  систему не тормозило, но это был обходной путь. Всё равно, что арендовать квартиру на месяц, когда нужна только посуточная аренда.

Для ленивых предлагаю скачать исходник.

(c) crystalbit, http://parsers.info
При копировании материала обязательно ставить прямую ссылку на источник.

59 ответов к «Контролируем буфер обмена»

  1. Интересная статья. Раньше программировал на делфях, но потом забросил. Сейчас снова хочется писать программы, поскольку появились интересные компоненты и модули.

  2. Программирую на делфе уже несколько месяцев. Не больно-то и получается, но в принципе некоторые вещички получаются очень неплохо.
    Пока что еще застой, связанный с неимением идей о написании программы.

  3. прошлый не прошлый, а живет и пользуется вниманием, так что не надо таких категоричных заявлений еще и без аргументов!

  4. Делфи сейчас весьма популярен, но думаю что это еще не все понял, что его возможности безграничны, в скором времени думаю его сделаю еще более простым и функциональным.

    1. Да вот времени совсем нету :)
      Не то, что совсем, но уже отошёл от программирования.
      Есть пара заготовок для серьёзных постов, может как-нибудь и допишу :)

  5. Делфи сейчас популярен, тока думаю что это еще не все понял что и как , что его возможности безграничны по идее , в скором времени думаю его сделаю еще более простым и функциональным и наворотами и писать моно будет еще быстрее.

  6. По-моему, делфи будет жить ровно столько, сколько на нем будут писать. По мне, то все языки должны умереть, которые не компиляца на linux и windows вместе

  7. C и Qt еще не каждому дадутся, а делфи — язык для новичков, позволяющий на базовых основах понять программирование. Только, зараза, мороки с ним много.

  8. Спасибо за сатью, очень интересно написано, С базовый язык программирвания, лучше ни чего нет!Любую задачу можно реализовть, сам сейчас новичок в этом деле!

  9. Огромное тебе спасибо. В первых пяти ссылках гугла пишут, что отследить буфер не реально. Я уже было им поверил… Ты меня очень выручил, у меня к тебе огромная благодарность.

    В благодарность добавлю некоторые корректировки в статью:

    В самом начале:
    nhwnd:=SetClipboardViewer(Handle);
    nhwnd – глобальная …

    А далее по статье вместо nhwnd идет nwnd.

    Кстати, его надо объявить, записав в private раздела type:
    nwnd: HWND;

    Объявление для DRAWCLIPBOARD, также надо записать в private раздела type:
    procedure WMDrawClipBoard(var msg: TWMDrawClipboard); message WM_DRAWCLIPBOARD;

    1. Для WMDrawClipBoard да, просто не расписывал так подробно, там же исходник прилагается. А nwnd я просто глобальным делал, не приписывая к форме, тогда ещё классы не так активно использовал, как сейчас.
      За опечатку спасибо, поправил

  10. Говно-копи-код.

    «список handle’ов окон». Эта штука «handle» называется дескриптор.
    SetClipboardViewer — что делает эта функция копипастерам понятно? Нет.
    Где говорится о возвращаемых значениях? Где проверка на возвращаемые значения?

    Следует отметить, что «цепочка» работает по образу стека. SetClipboardViewer помещает окно на вершину и возвращает дескриптор следующего окна, при ошибке — 0. Система направляет сообщения первому окну в этом стеке, которое в свою очередь должно его передать дальше. И так по всей глубине. MSDN(SetClipboardViewer)

    1. Спасибо за замечание.
      А я всегда handle’ом называл)
      Переводил бы дословно msdn, было бы и о возвращаемых значениях. Приведён пример кода, если нужно, всегда можно сделать все проверки. Там в исходнике разве нет необходимых (не достаточных) проверок? Я, честно, уже не помню.
      А что там копипастерам понятно – у них и спросите.

  11. Добрый вечер.

    У меня наблюдается странное поведение — перехват буфера обмена выполняется два раза. Т.е. при копировании любой информации в буфер, появляется два сообщения с текстом. Перепробовал несколько разных кодов для перехвата, везде одно и то же. Скачал Ваш исходник — то же самое. Встречались ли Вы когда-либо с таким и как это исправить?

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *