При этом заодно приходится разрешать/запрещать отдельные пункты меню в зависимости от наличия выделенного текста в редакторе (Cut, Copy) и наличия нужных данных в буфере обмена (Paste).
Для передачи обычному редактору команд для обмена данными с буфером служат сообщения
WM_PASTE 0 0L
вставить текст из Clipboard в текущую позицию каретки
WM_COPY 0 0L
скопировать выделенный текст из редактора в Clipboard
WM_CUT 0 0L
скопировать выделенный текст и удалить его из редактора
Мы должны просто послать нужное сообщение редактору и он выполнит все остальное. Обратите внимание на то, что эти сообщения не являются специфичными для редактора – они имеют префикс WM_ – то есть это сообщения для обычного окна.
Если Ваше окно будет поддерживать работу с буфером обмена, то настоятельно рекомендуется поддерживать эти сообщения.
Для того, что бы разрешать или запрещать нужные пункты меню можно воспользоваться функцией IsClipboardFormatAvailable для разрешения/запрещения операции Paste и передачей сообщения EM_GETSEL для выяснения возможности операций Cut и Copy.
Если Вы вводите новые форматы данных в буфер обмена, то вам может понадобиться новая программа просмотра данных буфера обмена, которая позволит просматривать Ваши данные. В Windows принято, что программа просмотра должна обновлять отображаемую информацию при смене данных в буфере обмена. Для этого программа просмотра получает сообщение
WM_DRAWCLIPBOARD 0 0L
Однако одновременно может работать несколько программ (окон) просмотра. При этом возникает необходимость где-то удерживать список всех таких программ (окон) и посылать им всем соответствующие сообщения. Для этого организуется цепочка программ просмотра, которую они сами поддерживают в корректном состоянии.
В какой-то степени это похоже на обработку прерываний DOS - система удерживает хендл только первого окна просмотра, тот - хендл следующего и так далее.
Когда Вы запускаете свою программу просмотра, она регистрируется в качестве программы просмотра буфера обмена с помощью функции
HWND SetClipboardViewer( hWnd );
при этом он становится на первое место в цепочке, а функция SetClipboardViewer возвращает хендл следующего за ним (или 0, если других программ (окон) просмотра нет). Этот хендл должен быть сохранен.
Далее, при обновлении данных в буфере обмена, Ваше окно получает сообщение WM_DRAWCLIPBOARD. Обычнаяобработкаэтогосообщения:
case WM_DRAWCLIPBOARD: if ( hWndNextViewer ) PostMessage( hWndNextViewer, wMsg, wParam, lParam ); InvalidateRect( hWnd, NULL, TRUE ); return 0;
В результате обработки этого сообщения окно программы просмотра перерисовывается, и такое же сообщение получает другая программа.
Во время нормальной работы какая-либо из программ просмотра заканчивает функционирование прежде остальных. При этом цепочка программ просмотра изменяется. Для того, чтобы корректно изменить цепочку, применяется функция
BOOL ChangeClipboardChain( hWndViewer, hWndNextViewer );
Что значит: вместо окна hWndViewer будет использоваться окно hWndNextViewer. Эта функция обычно вызывается при обработке сообщения WM_DESTROY. В результате выполнения этой функции первая программа просмотра в цепочке получит сообщение (WM_CHANGECBCHAIN, hWndRemoved, hWndNext). Это сообщение обрабатывается так:
case WM_CHANGECBCHAIN: if ( wParam == hWndNextViewer ) { hWndNextViewer= LOWORD( lParam ); } else if ( hWndNextViewer ) { PostMessage( hWndNextViewer, wMsg, wParam, lParam ); } return 0;
В результате такой обработки, если наше окно стоит в цепочке до удаляемого, но не прямо перед ним, оно передаст сообщение дальше; если оно стоит прямо перед удаляемым, то оно исправит свой хендл следующего окна просмотра и не пустит сообщение дальше, так как цепочка уже исправлена.
Отображение данных программой просмотра происходит также, как и их обычное чтение (открыл–прочитал–закрыл).
Пока что мы рассмотрели только один способ обмениваться данными между разными приложениями - буфер обмена. Однако этот способ не всегда эффективен. В большинстве случаев он ограничен применением в редакторах.
Однако необходимость обмена данными между разными приложениями реально существует, поэтому Windows содержит еще одно средство для такого обмена — DDE (Dynamic Data Exchange).
DDE основан на использовании сообщений для передачи данных между двумя приложениями. При этом одно из приложений (обычно содержащее данные) называется сервером (server), а другое (обычно требующее данных) – клиентом (client). В более общем виде: клиент выступает в роли активного приложения, требующего, что бы его запросы были обслужены сервером. Сам процесс обмена данными средствами DDE между клиентом и сервером называется DDE–разговором (DDE–conversation).
Обычно протокол обмена данными между клиентом и сервером выглядит примерно следующим образом:
· клиент передает всем доступным приложениям сообщение о том, что ему надо.
· если в системе находится подходящий сервер, он отвечает клиенту о возможности обслуживания.
· клиент посылает запрос(ы) серверу для выполнения требований.
· в некоторых случаях сервер может информировать клиента об изменении или наличии тех или иных данных. Для этого клиент должен подписаться (advise) на необходимые данные.
· обмен между клиентом и сервером может продолжаться до тех пор, пока один из них не потребует прекращения.
В процессе DDE–разговора происходит обмен сообщениями между двумя приложениями. Понятно, что адресатами таких сообщений являются не сами приложения, а их окна. В большинстве случаев для нормального ведения DDE–разговора создаются специальные скрытые (невидимые) окна, которые и осуществляют обмен сообщениями между собой. Конечно, в DDE могут участвовать окна одного приложения, не только разных, но надо учесть, что DDE для обмена данными в пределах одного приложения является неэффективным. Так как процессы подготовки и приема данных могут занимать значительное время, то требуется, чтобы для обмена сообщениями использовалась функция PostMessage, а не SendMessage (кроме установления DDE–разговора). Это, правда, создает дополнительную сложность — необходимость синхронизации действий разных окон между собой.
В Windows содержится все необходимое для организации DDE, причем в двух экземплярах. Помимо старого способа, существующего с первых версий Windows, в Windows версии 3.1 была добавлена специальная библиотека DDEML (DDE Management Library), являющаяся “надстройкой” над старым способом и предназначенная для формализации протоколов обмена. Существенная разница между этими способами связана с тем, что для организации DDE старым способом вы должны предусмотреть обработку специальных сообщений необходимыми Вам окнами, а библиотека DDEML сама создает необходимые скрытые окна и организует обмен сообщениями; при этом для взаимодействия с Вашим приложением DDEML будет вызывать специально разработанную Вами CALLBACK процедуру (не являющую оконной процедурой).
Считается, что при использовании DDEML несколько упрощается написание приложений, так как она берет на себя вопросы синхронизации обмена. Однако на практике не было замечено реального упрощения работы в связи с применением библиотеки. Более того, исходный текст может оказаться даже больше, чем при использовании старого метода.
Поэтому мы будем рассматривать только старый способ организации DDE. Предварительно мы введем несколько терминов, используемых в DDE. Когда клиент начинает DDE или требует данные, он посылает серверу спецификацию того, что он требует.
Эта спецификация состоит из 3 пунктов:
· имя приложения — application (в DDEML называется сервис (service)); так как в большинстве случаев в документации применяется термин service, то его мы и будем использовать дальше. Хотя, надо отметить, этот параметр обычно задает условное имя приложения–сервера.
· тему DDE–разговора — topic
· требуемые данные — item
Имя сервиса и тема DDE–разговора используютсяпри установлении связи. В результате этого в системе должно быть установлен обмен данными между одной или несколькими парами окон, которые поддерживают данные сервис и тему. Одно из окон в каждой паре будет являться клиентом, а другое — сервером. В процессе дальнейшего DDE–разговора может происходить обмен данными, имя которых задается отдельно и является возможным именем данных для данного сервиса и темы.
Эти три имени представлены в виде атомов (ATOM), поэтому нам надо разобраться с атомами и правилами их применения перед продолжением разговора о DDE.
Атомами в Windows называются нумерованные строки текста, хранимые в специальной таблице. Максимальный размер строки 255 символов. При помещении строки в таблицу ей присваивается уникальный номер, который собственно и используется вместо самой строки. Часто атомом называют именно эти номера, а строки — именами атомов. Обе платформы (Windows 3.x и Win32) используют для задания атомов 16-ти разрядные числа, называемые ATOM, что позволяет в одном двойном слове передавать до двух атомов.
Если добавлять две одинаковых строки в таблицу атомов, то они получат одинаковый номер. На этом основан один из способов сравнения строк — добавить обе строки в таблицу и сравнить атомы (не забудьте после этого удалить добавленный атом, даже если это было надо только для проверки).
Для каждого приложения доступны две таблицы атомов - локальная и глобальная. Так как DDE поддерживается между разными приложениями то, очевидно, должна применяться только глобальная таблица атомов.
Для работы с глобальными атомами предназначены следующие функции:
ATOM GlobalAddAtom( lpszName ); UINT GlobalGetAtomName( aAtom, lpsBuffer, nMaxCount ); ATOM GlobalFindAtom( lpszName ); ATOM GlobalDeleteAtom( aAtom );
С помощью этих функций можно добавить атомы, узнать имя конкретного атома, найти нужный атом в таблице и удалить атом. Причем, сколько раз добавлялся атом с одним именем, столько раз он должен быть удален, что бы он действительно был удален из таблицы.
Для работы с локальными атомами используются аналогичные функции, но не имеющие в начале слова “Global”. Помимо этого для работы с локальной таблицей атомов добавлена еще одна функция: