Total Commander Forum Index Total Commander
Форум поддержки пользователей Total Commander
Сайты: Все о Total Commander | Totalcmd.net | Ghisler.com | RU.TCKB
 
 RulesRules   SearchSearch   FAQFAQ   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Буфер обмена с++
Goto page 1, 2  Next
 
Post new topic   Reply to topic    Total Commander Forum Index -> Программное обеспечение printer-friendly view
View previous topic :: View next topic  
Author Message
Baz



Joined: 06 Mar 2006
Posts: 34

Post (Separately) Posted: Thu Apr 22, 2010 20:08    Post subject: Буфер обмена с++ Reply with quote

Так и не нашёл подходящего мануала по буферу обмена. Поскажите кто знает как это реализовать попроще.

Что я хочу:
Планирую сделать маленькую программку для себя, которая отслеживает изменение буфера и вытаскивает оттуда ссылки определённого формата. Ссылки берутся по одиночке или копированием всей страницы и сохраняются в текстовый файл немного преобразоввываясь.
Back to top
View user's profile Send private message
Вахмурка



Joined: 27 Dec 2004
Posts: 2584
Location: Большая деревня Москва

Post (Separately) Posted: Thu Apr 22, 2010 23:05    Post subject: Reply with quote

В PowerPro (любая ссылка под данным постом) богатейшие возможности работы с буфером. По крайней мере, ничего более разнообразного в данной области встречать не довелось. Написан, кстати, на с++ (см. заголовок топика). Кроме встроенных возможностей, есть плагин clip. Сам я буфер использую только как "перевалочную базу".
_________________
Сайт PowerPro+Total Commander
Скрипты PowerPro для Total Commander
* * *
«Не усматривайте злого умысла в том, что вполне объяснимо глупостью» (Р. Хэнлон)
Back to top
View user's profile Send private message
Loopback



Joined: 07 Sep 2009
Posts: 1279

Post (Separately) Posted: Fri Apr 23, 2010 10:24    Post subject: Reply with quote

Baz
Насчет мануала не знаю, но описания функций в MSDN вполне достаточно для понимания.

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

Вроде еще есть еще возможность использовать глобальный хук (тогда окно по-идее не нужно), но с этим дела не имел.
Back to top
View user's profile Send private message
Baz



Joined: 06 Mar 2006
Posts: 34

Post (Separately) Posted: Fri Apr 23, 2010 11:47    Post subject: Reply with quote

Loopback
Окна ерунда, прятать их я пока не собираюсь. А в MSDN муторно всё получается: разные форматы и т.д., а я надеялся получить доступ к простому тексту вида char* и тупо его разбирать. Видимо всё же придётся разбираться с наворотами msdn.
Back to top
View user's profile Send private message
MVV



Joined: 15 Oct 2009
Posts: 4811
Location: Ростов-Дон

Post (Separately) Posted: Fri Apr 23, 2010 12:58    Post subject: Reply with quote

Baz wrote:
Окна ерунда, прятать их я пока не собираюсь. А в MSDN муторно всё получается: разные форматы и т.д., а я надеялся получить доступ к простому тексту вида char* и тупо его разбирать. Видимо всё же придётся разбираться с наворотами msdn.

Loopback имел в виду, что если у тебя еще нет окна, можно создать невидимое, чтобы оно не мешало юзеру работать. Ну а если есть - наверное, можно использовать любое из имеющихся - какая тебе разница, какое из них получает уведомление, главное - его обработать. Кстати, при уничтожении этого окна надо вызывать ChangeClipboardChain для удаления окна из списка рассылки (ну и передавать уведомление по эстафете при его получении).
_________________
TCFS2 + TCFS2Tools: Полноэкранный режим и многое другое (обсуждение)
WINCMD.RU: AskParam, CopyTree, NTLinks, Sudo, VirtualPanel…
Back to top
View user's profile Send private message
Baz



Joined: 06 Mar 2006
Posts: 34

Post (Separately) Posted: Fri Apr 23, 2010 16:44    Post subject: Reply with quote

MVV
Ваобще то я знаю только основы с++, поэтому не откажусь от более подробной информации об этих умных словах "уведомление" и "рассылка". А Visual Studio я начал пользоваться потому что там очень удобный и мощный отладчик ))
Back to top
View user's profile Send private message
MVV



Joined: 15 Oct 2009
Posts: 4811
Location: Ростов-Дон

Post (Separately) Posted: Fri Apr 23, 2010 21:20    Post subject: Reply with quote

Ну, слова не такие и умные - под "уведомлением" я подразумевал сообщение WM_DRAWCLIPBOARD, уведомляющее о том, что содержимое буфера обмена изменилось. Далее, согласно справке по функции SetClipboardViewer, эта функция добавляет указанное окно в список окон, которые хотят получать указанное уведомление - этот список я и называл "списком рассылки". При этом функция вернет дескриптор следующего окна (или предыдущего, не важно), которое хочет получать уведомление, и каждое окно при получении этого уведомления должно передавать его по цепочке окну, дескриптор которого вернула функция SetClipboardViewer.
Использование Windows API предполагает использование одинакового набора функций в любой среде разработки, Visual Studio это, MASM32 или Delphi (если в какой-то среде есть свои методы, это уже не Windows API).
Да, в Windows API порой разобраться без поллитры сложно. Я сегодня пробовал открыть буфер обмена и получить текст, но ничего не вышло, каждый раз функция GetClipboardData возвращала 0, а GetLastError - ошибку 1418 ("Буфер обмена для потока команд не открыт. " - кстати, в Visual Studio в меню Инструменты (или Tools) есть пункт Error Lookup - утилита для расшифровки кодов ошибок, которые возвращает GetLastError). Я так и не понял, в чем была причина такого поведения - возможно, в том, что я передавал 0 в качестве окна (ну нету у моего процесса окна, что мне, сидеть без буфера обмена, что ли?). В общем, много видел я нелогичного в Windows API, но работа с буфером обмена - нечто претендующее на призовые места. Рекомендую поискать в инете реальные исходники с примерами работы с буфером обмена (или дизассемблить че-нить Rolling Eyes).
_________________
TCFS2 + TCFS2Tools: Полноэкранный режим и многое другое (обсуждение)
WINCMD.RU: AskParam, CopyTree, NTLinks, Sudo, VirtualPanel…
Back to top
View user's profile Send private message
Batya



Joined: 15 Dec 2004
Posts: 2218
Location: Москва, Россия

Post (Separately) Posted: Fri Apr 23, 2010 23:09    Post subject: Reply with quote

А ещё можно воспользоваться чем-нибудь уже выложенным на этом сайте.
FS-плагины:
decClipboardFS
FSClipboard
Утилиты:
Camp
ClpBrd
_________________
Нет, я не сплю. Я просто медленно моргаю.
Back to top
View user's profile Send private message
Baz



Joined: 06 Mar 2006
Posts: 34

Post (Separately) Posted: Sat Apr 24, 2010 00:01    Post subject: Reply with quote

MVV
Это будет кстати - я то собирался вручную по привычке проверять обновление буфера. А в интернете я видел целую кучу копий или незначительных вариаций одного и того же кода.
Batya
Обана, да это же сайт моего любимого и великопепного decGet.
Ваобще мне нужно что бы хотя бы можно было сохранять весь текст буфера в простой текстовый фал с которым я уже умею работать, но как мне кажется ни одно из предложенных готовых решений не подходит.
Back to top
View user's profile Send private message
Вахмурка



Joined: 27 Dec 2004
Posts: 2584
Location: Большая деревня Москва

Post (Separately) Posted: Sat Apr 24, 2010 00:51    Post subject: Reply with quote

Quote:
любимого и великопепного decGet
Присоединяюсь. Удобная примочка.
Quote:
сохранять весь текст буфера в простой текстовый фал
PowePro: clip.save, clip.get, особенно clip.tofile
_________________
Сайт PowerPro+Total Commander
Скрипты PowerPro для Total Commander
* * *
«Не усматривайте злого умысла в том, что вполне объяснимо глупостью» (Р. Хэнлон)
Back to top
View user's profile Send private message
MVV



Joined: 15 Oct 2009
Posts: 4811
Location: Ростов-Дон

Post (Separately) Posted: Sat Apr 24, 2010 00:56    Post subject: Reply with quote

В общем, вроде заработало оно у меня...

Но пожалуй функциям работы с буфером обмена в Windows я бы дал первое место по тупости (хотя, не знаю, что тупее организовано - работа с буфером или редирекция 32-битной папки System32 в 64-битной винде, которой вообще могло не существовать, сделай они папку System64 для 64-битных библиотек)...

Во-первых, пока одна программа открыла буфер, остальные вообще не могут ниче сделать.

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

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

Наконец, вспомним о том, что функция SendMessage не возвращает управление до тех пор, пока сообщение не обработано - получается, что окно, обработавшее уведомление и вызвавшее SendMessage для передачи уведомления далее по эстафете будет самым явным образом висеть до тех пор пока оконные процедуры всех последующих "глядителей буфера" не закончат обработку уведомления. Exclamation Как вариант, возможно, можно было бы вызывать PostMessage, которая не блокирует управление (хотя в MSDN явно сказано вызывать SendMessage) - но если содержимое буфера вдруг изменится, вторая волна уведомлений может начаться до того как закончится первая...


Итак, лирическое отступление закончилось. Вот код для получения текста из буфера (и выдачи его в сообщении):
Code:
   if (OpenClipboard(0)) {
      if (IsClipboardFormatAvailable(CF_UNICODETEXT)) {
         HANDLE hclip=GetClipboardData(CF_UNICODETEXT);
         if (hclip) MessageBoxW(0, (wchar_t*)hclip, L"Clipboard text (Unicode)", MB_ICONINFORMATION);
      }
      else if (IsClipboardFormatAvailable(CF_OEMTEXT)) {
         HANDLE hclip=GetClipboardData(CF_OEMTEXT);
         if (hclip) MessageBoxA(0, (char*)hclip, "Clipboard text (ANSI)", MB_ICONINFORMATION);
      }
      else MessageBox(0, "Clipboard contains non-text data.", "Clipboard viewer", MB_ICONINFORMATION);
      CloseClipboard();
   }

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

А вот какие варианты uMsg нужно добавить в оконную процедуру для обработки:
Code:
      case WM_DRAWCLIPBOARD:
         if (OpenClipboard(hWnd)) {
            if (IsClipboardFormatAvailable(CF_UNICODETEXT)) {
               HANDLE hclip=GetClipboardData(CF_UNICODETEXT);
               if (hclip) MessageBoxW(hWnd, (wchar_t*)hclip, L"Clipboard text (Unicode)", MB_ICONINFORMATION);
            }
            else if (IsClipboardFormatAvailable(CF_OEMTEXT)) {
               HANDLE hclip=GetClipboardData(CF_OEMTEXT);
               if (hclip) MessageBoxA(hWnd, (char*)hclip, "Clipboard text (ANSI)", MB_ICONINFORMATION);
            }
            else MessageBox(hWnd, "Clipboard contains non-text data.", "Clipboard viewer", MB_ICONINFORMATION);
            CloseClipboard();
         }
         if (hNextClipboardViewer) {
            SendMessage(hNextClipboardViewer, WM_DRAWCLIPBOARD, wParam, lParam);
            MessageBox(hWnd, "Finished processing WM_DRAWCLIPBOARD message.", "Clipboard viewer", MB_ICONINFORMATION);
         }
         break;
      case WM_CHANGECBCHAIN:
         if ((HWND)wParam==hNextClipboardViewer) hNextClipboardViewer=(HWND)lParam;
         else if (hNextClipboardViewer) SendMessage(hNextClipboardViewer, WM_CHANGECBCHAIN, wParam, lParam);
         return 0;
      case WM_CREATE:
         hNextClipboardViewer=SetClipboardViewer(hWnd);
         ... // дальнейшие команды, выполняемые при создании окна
      case WM_DESTROY:
         ChangeClipboardChain(hWnd, hNextClipboardViewer);
         ... // дальнейшие команды, выполняемые при уничтожении окна

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

Как видно из примера, дескриптор, возвращаемый функцией GetClipboardData для форматов CF_UNICODETEXT и CF_OEMTEXT - тупо указатель на строку в соответствующей кодировке. Видимо, размер строки будет определяться позицией нулевого символа, раз способа получить длину строки API не предоставляет.


Вместо вывода замечу, что категорически не рекомендую использовать рабочее окно своей программы как "глядителя буфера" - лучше создать дополнительное невидимое окно, и пусть оно работает себе в фоне, чтобы не вешало основное. Ну а при необходимости можно посылать основному окну уведомление о том, что содержимое буфера изменилось - например, в аккурат перед передачей уведомления остальным подписчикам - тогда основное окно спокойно обработает буфер и продолжит работу, не зависая на неопределенное время. Хотя, наверное, лучше вообще всю грязную работу с буфером свалить на вспомогательное окно, а основному просто посылать через PostMessage специальное пользовательское сообщение (каждый может зарегистрировать себе новое сообщение для своих нужд) с указателем на выделенный блок памяти со скопированным из буфера обмена текстом - подчеркиваю, не оригинальным указателем, возвращенным функцией GetClipboardData, а указателем на самостоятельно выделенный блок памяти. При этом основное окно, получив такое сообщение, обрабатывает текст и освобождает присланный блок памяти (или вызывает фоновый обработчик, который затем освободит память).


Напоследок - примеры проявления фактов, замеченных в ходе выполнения лабораторки по исследованию API для работы с буфером обмена.

Выдача сообщения о завершении работы с буфером позволяет наглядно убедиться, что программа будет висеть, пока остальные подписчики на уведомления об изменении буфера "читают почту". Запустим два экземпляра программы (буду звать их первым и вторым). Далее копируем в буфер любой текст. Второй экземпляр покажет сообщение с новым текстом окна, далее такое же сообщение покажет первый экземпляр... но пока это окно сообщения открыто, второй экземпляр программы будет висеть, и только после закрытия всех окон сообщений первого позволит продолжить работу с его окном.

Еще забавно выполнять строчку с вызовом SetClipboardViewer дважды - при этом окно будет слать сообщение об изменении буфера само себе и зациклится при оном. Laughing

И можно проверить, как работает убийство процесса, окно которого следит за буфером - при этом нарушается цепочка уведомлений, и все подписчики, подписанные на рассылку ранее, "почты" уже не получат. Запустите, скажем, программу clipbrd (показывающую содержимое буфера), а затем - программу с этим примером. Убедитесь, что обе программы реагируют на изменение содержимого буфера. Теперь убейте процесс программы с примером. Всё! От программы clipbrd теперь толку как с козла молока - она уже не видит изменений буфера, так как умершая программа должна была сообщать ей о его изменениях. Cool


PS. Что-то разошёлся я, прям статья получилась... Повод хороший попался))
_________________
TCFS2 + TCFS2Tools: Полноэкранный режим и многое другое (обсуждение)
WINCMD.RU: AskParam, CopyTree, NTLinks, Sudo, VirtualPanel…
Back to top
View user's profile Send private message
Baz



Joined: 06 Mar 2006
Posts: 34

Post (Separately) Posted: Sat Apr 24, 2010 11:43    Post subject: Reply with quote

Всем спасибо, постараюсь разобраться со всеми ответами.

MVV
А мы об одном и том же буфере говорим? Везде в мануалах написано "открыть, прочитать нужный формат, закрыть" и нигде про рассылку я не видел ничего. Вот весьма типичный пример http://www.cplusplus.com/forum/beginner/676/
Back to top
View user's profile Send private message
MVV



Joined: 15 Oct 2009
Posts: 4811
Location: Ростов-Дон

Post (Separately) Posted: Sat Apr 24, 2010 14:43    Post subject: Reply with quote

Дык он, бедняжка, один единственный на всю операционку, как можно говорить о разных. Wink
Везде пишут, как открыть, прочитать, закрыть... А тебе нужно еще и следить за изменениями. Вот для этого и нужно подписываться на рассылку уведомления WM_DRAWCLIPBOARD и читать буфер при получении этого уведомления (вот тут и используются эти самые "открыть, прочитать нужный формат, закрыть") - то есть когда система сообщает, что в буфере появилось что-то новенькое - я думал, после прошлого объяснения ты это понял. Smile
Baz wrote:
Это будет кстати - я то собирался вручную по привычке проверять обновление буфера.


Итак, чтобы просто прочитать содержимое буфера, надо вызвать OpenClipboard, проверить, есть ли данные в нужном формате функцией IsClipboardFormatAvailable и получить указатель на данные, вызвав GetClipboardData. Далее как можно быстрее обработать данные и вызывать CloseClipboard.
Чтобы следить за изменениями буфера, нужно подписаться на рассылку уведомления, вызвав (ровно 1 раз) SetClipboardViewer, далее при получении сообщения-уведомления WM_DRAWCLIPBOARD обрабатывать данные и отправлять сообщение следующему окну, следящему за изменениями буфера, с помощью функции SendMessage. Помимо того, обязательно реализовать обработку сообщения WM_CHANGECBCHAIN для корректировки очереди при удалении из нее какого-либо окна, ну и не забыть вызвать ChangeClipboardChain при удалении окна, чтобы удалить свое окно из очереди и не нарушить очередь (иначе окна, ставшие в очередь до твоего, останутся без уведомлений об изменении буфера). Всё это реализовано в примере из моего предыдущего сообщения - просто добавляешь новые варианты в оператор выбора оконной процедуры.
_________________
TCFS2 + TCFS2Tools: Полноэкранный режим и многое другое (обсуждение)
WINCMD.RU: AskParam, CopyTree, NTLinks, Sudo, VirtualPanel…
Back to top
View user's profile Send private message
Baz



Joined: 06 Mar 2006
Posts: 34

Post (Separately) Posted: Sat Apr 24, 2010 19:43    Post subject: Reply with quote

MVV wrote:
я думал, после прошлого объяснения ты это понял.

Да я такой - "а" и "б" вижу, а связать обычно не получается.
Back to top
View user's profile Send private message
VadiMGP



Joined: 21 Mar 2007
Posts: 1625

Post (Separately) Posted: Sun Apr 25, 2010 01:22    Post subject: Reply with quote

MVV wrote:
Итак, чтобы просто прочитать содержимое буфера, надо вызвать OpenClipboard, проверить, есть ли данные в нужном формате функцией IsClipboardFormatAvailable
Уточнение. Для вызова IsClipboardFormatAvailable не нужно вызывать OpenClipboard. Даже рекомендуется наоборот, сначала убедиться, что есть нужный нам формат, и только в этом случае открывать буфер.

Еще одно, хотя тут голову на отсечение не дам, но насколько я помню нет необходимости сначала проверять CF_UNICODETEXT, а потом CF_OEMTEXT. Эти форматы автоматически конвертируются один в другой и всегда присутствуют вместе. И CF_TEXT тоже.
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    Total Commander Forum Index -> Программное обеспечение All times are GMT + 4 Hours
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group