- •Содержание
- •Управление памятью: хорошо, плохо и ужасно
- •Сегментированная память
- •Промежуточные решения
- •И, наконец, 32 бита
- •Выделение памяти
- •Библиотечные функции C
- •Фундаментальное выделение памяти в Windows 95
- •Перемещаемая память
- •Удаляемая память
- •Другие функции и флаги
- •Хорошо ли это?
- •Функции работы с "кучей"
- •Файловый ввод/вывод
- •Старый путь
- •Отличия Windows 95
- •Ввод/вывод с использованием файлов, проецируемых в память
- •Режимы многозадачности
- •Многозадачность в DOS
- •Невытесняющая многозадачность
- •Решения, использующие многопоточность
- •Многопоточная архитектура
- •Коллизии, возникающие при использовании потоков
- •Преимущества Windows
- •Новая программа! Усовершенствованная программа! Многопоточная!
- •Многопоточность в Windows 95
- •И снова случайные прямоугольники
- •Задание на конкурсе программистов
- •Решение с использованием многопоточности
- •О пользе использования функции Sleep
- •Синхронизация потоков
- •Критический раздел
- •Объект Mutex
- •Программа BIGJOB1
- •Объект Event
- •Локальная память потока
- •Печать, буферизация и функции печати
- •Контекст принтера
- •Формирование параметров для функции CreateDC
- •Измененная программа DEVCAPS
- •Вызов функции PrinterProperties
- •Проверка возможности работы с битовыми блоками (BitBlt)
- •Программа FORMFEED
- •Печать графики и текста
- •Каркас программы печати
- •Прерывание печати с помощью процедуры Abort
- •Реализация процедуры прерывания
- •Добавление диалогового окна печати
- •Добавление печати к программе POPPAD
- •Обработка кодов ошибок
- •Техника разбиения на полосы
- •Разбиение на полосы
- •Реализация разбиения страницы на полосы
- •Принтер и шрифты
- •Глава 16 Буфер обмена
- •Простое использование буфера обмена
- •Стандартные форматы данных буфера обмена
- •Передача текста в буфер обмена
- •Получение текста из буфера обмена
- •Открытие и закрытие буфера обмена
- •Использование буфера обмена с битовыми образами
- •Метафайл и картина метафайла
- •Более сложное использование буфера обмена
- •Использование нескольких элементов данных
- •Отложенное исполнение
- •Нестандартные форматы данных
- •Соответствующая программа просмотра буфера обмена
- •Цепочка программ просмотра буфера обмена
- •Функции и сообщения программы просмотра буфера обмена
- •Простая программа просмотра буфера обмена
- •Основные концепции
- •Приложение, раздел и элемент
- •Типы диалогов
- •Символьные строки и атомы
- •Программа сервер DDE
- •Программа DDEPOP1
- •Сообщение WM_DDE_INITIATE
- •Оконная процедура ServerProc
- •Функция PostDataMessage программы DDEPOP1
- •Сообщение WM_DDE_ADVISE
- •Обновление элементов данных
- •Сообщение WM_DDE_UNADVISE
- •Сообщение WM_DDE_TERMINATE
- •Программа-клиент DDE
- •Инициирование диалога DDE
- •Сообщение WM_DDE_DATA
- •Сообщение WM_DDE_TERMINATE
- •Управляющая библиотека DDE
- •Концептуальные различия
- •Реализация DDE с помощью DDEML
- •Элементы MDI
- •Windows 95 и MDI
- •Пример программы
- •Три меню
- •Инициализация программы
- •Создание дочерних окон
- •Дополнительная информация об обработке сообщений в главном окне
- •Дочерние окна документов
- •Освобождение захваченных ресурсов
- •Сила оконной процедуры
- •Основы библиотек
- •Библиотека: одно слово, множество значений
- •Пример простой DLL
- •Разделяемая память в DLL
- •Библиотека STRLIB
- •Точка входа/выхода библиотеки
- •Программа STRPROG
- •Работа программы STRPROG
- •Разделение данных между экземплярами программы STRPROG
- •Некоторые ограничения библиотек
- •Динамическое связывание без импорта
- •Библиотеки, содержащие только ресурсы
- •Глава 20 Что такое OLE?
- •Основы OLE
- •Связь с библиотеками OLE
- •Расшифровка кода результата
- •Интерфейсы модели составного объекта (COM-интерфейсы)
- •Услуги интерфейса IUnknown
- •Является ли OLE спецификацией клиент/сервер?
- •Сервер закрытого компонента
- •IMALLOC.DLL
- •Теперь о макросах
- •Услуги, предоставляемые интерфейсом IUnknown
- •Клиент закрытого компонента
- •Сервер открытого компонента
- •Назначение реестра
- •Способы генерации и использования идентификаторов CLSID
- •Компонент фабрика классов
- •Управление временем жизни сервера
- •Клиент открытого компонента
- •Заключение
116
и программисту нужно быть готовым к тому, что придется столкнуться с некоторыми достаточно изощренными проблемами.
Основные концепции
Когда клиент запрашивает у сервера данные, он должен иметь возможность идентифицировать запрашиваемые данные. Это делается с помощью трех символьных строк, которые называются "приложение" (application), "раздел" (topic) данных и "элемент" (item) данных.
Приложение, раздел и элемент
Назначение приложения, раздела и элемента станет понятнее из примера. В первой половине этой главы будет показано, как написать Windows-программу-сервер DDE, которая называется DDEPOP1. В этой программе содержатся данные о населении Соединенных Штатов, полученные по итогам переписей населения в 1970, 1980 и 1990 годах. На основе квадратичной экстраполяции программа может рассчитать численность населения любого штата или Соединенных Штатов в целом в любой момент времени.
Любой, кто пишет программу сервера DDE должен документировать то, как с помощью трех символьных строк идентифицировать данные сервера:
•Имя приложения сервера. В данном примере им является просто "DDEPOP1". Каждый сервер имеет только одно имя приложения, каковым является имя программы.
•Имя раздела. Все серверы DDE поддерживают по меньшей мере один раздел. В случае программы DDEPOP1, поддерживается один раздел, он идентифицируется строкой "US_Population". Очевидно, что программа DDEPOP1 могла бы быть дополнена информацией о площади штатов в квадратных милях. В таком случае поддерживался бы второй раздел с именем "US_Area".
•Имя элемента. Внутри каждого раздела сервер DDE поддерживает один или более элементов данных. В программе DDEPOP1 элемент идентифицирует штат с помощью стандартной двухсимвольной почтовой аббревиатуры, например "NY" для Нью-Йорка, "CA" для Калифорнии и "US" для страны в целом. В программе DDEPOP1 поддерживается 52 элемента — 50 штатов, округ Колумбия ("DC") и страна в целом.
Описание этих данных важно при использовании сервера с другими Windows-программами, которые могут выступать в качестве клиента, например, Microsoft Excel. Для использования программы DDEPOP1 вместе с Microsoft Excel, в ячейке электронной таблицы программы Microsoft Excel можно набрать следующую строку:
=DDEPOP1 | US_Population ! US
В этих трех словах заданы приложение, раздел и элемент (в данном случае все население Соединенных Штатов). Если файл DDEPOP1.EXE еще не запущен, Microsoft Excel попытается запустить его. (Программа DDEPOP1 должна находиться в текущем каталоге или в списке каталогов переменной окружения PATH.) При удачном запуске Microsoft Excel инициирует с программой DDEPOP1 диалог DDE, получит данные о населении и выведет эти данные в виде числа в ячейке электронной таблицы. Этот результат, обозначающий численность населения, может быть отформатирован, представлен в графическом виде, использован в вычислениях.
Самым замечательным является то, что цифры, обозначающие в электронной таблице численность населения, будут периодически обновляться. Этот процесс называется горячей связью (hot link) или (в более простом варианте) теплой связью (warm link). Каждые 5 секунд программа DDEPOP1 пересчитывает данные о населении и уведомляет клиента при изменении элемента. В случае с населением США, число будет увеличиваться на 1 почти каждые 15 секунд.
Типы диалогов
В DDE имеется три основных типа диалога — холодная связь (cold link), горячая связь и теплая связь. В этих диалогах используются сообщения DDE, определенные в заголовочном файле DDE.H. Простейшим из трех диалогов является холодная связь.
Холодная связь
Диалог холодной связи начинается, когда клиент широковещательно рассылает сообщение WM_DDE_INITIATE, идентифицирующее приложение и раздел, который требуется клиенту. (Чтобы начать диалог с любым приложением-сервером или любым разделом данных, приложение и раздел, соответственно, могут быть равны NULL.) Приложение-сервер, которое поддерживает заданный раздел, отвечает клиенту сообщением
WM_DDE_ACK (подтверждение, acknowledge):
117
Затем клиент запрашивает конкретный элемент данных, посылая сообщение WM_DDE_REQUEST. Если у сервера имеется этот элемент данных, то он отвечает, отправляя клиенту сообщение WM_DDE_DATA:
Здесь также показано, что клиент может уведомить сервер о получении сообщения WM_DDE_DATA. Это не обязательно (что обозначено посредством заключения в скобки сообщения WM_DDE_ACK). Необходимость подтверждения сервер показывает с помощью флага, передаваемого с сообщением WM_DDE_DATA. Флаг, передаваемый с сообщением WM_DDE_ACK, показывает, что данные получены успешно.
Если клиент посылает серверу сообщение WM_DDE_REQUEST, а у сервера нет запрашиваемого элемента данных, то сервер посылает клиенту негативное сообщение WM_DDE_ACK:
Диалог DDE продолжается до тех пор, пока клиент отправляет серверу сообщения WM_DDE_REQUEST — с запросами о том же самом или другом элементе данных — и сервер отвечает сообщениями WM_DDE_DATA или WM_DDE_ACK. Диалог заканчивается, когда клиент и сервер посылают друг другу сообщения
WM_DDE_TERMINATE:
Хотя здесь показано, что клиент первым посылает сообщение WM_DDE_TERMINATE, это не всегда так. Сервер может первым послать сообщение WM_DDE_TERMINATE, и клиент должен ответить на него.
Горячая связь
Одной из проблем, связанной с холодной связью, является то, что данные, к которым сервер имеет доступ, во время передачи могут меняться. (Это происходит в случае программы DDEPOP1, рассчитывающей прирост населения.) При использовании холодной связи клиент не знает, когда изменяются данные. Эту проблему решает горячая связь.
Также, как при холодной связи диалог DDE начинается с сообщения WM_DDE_INITIATE и сообщения
WM_DDE_ACK:
Клиент задает требуемый ему элемент данных, посылая серверу сообщение WM_DDE_ADVISE. Отвечая сообщением WM_DDE_ACK, сервер показывает, есть ли у него доступ к этому элементу:
118
Позитивное подтверждение показывает, что сервер может дать клиенту нужные данные, негативное — что не может.
С этого момента сервер обязан извещать клиента об изменении значения элемента данных, когда бы оно не произошло. Для этого извещения используется сообщение WM_DDE_DATA, на которое клиент (в зависимости от состояния флага в сообщении WM_DDE_DATA) может отвечать или не отвечать сообщением WM_DDE_ACK:
Если клиенту больше не нужно знать об изменении элемента данных, он посылает серверу сообщение WM_DDE_UNADVISE, и сервер отвечает подтверждением:
Диалог завершается после обмена сообщениями WM_DDE_TERMINATE:
Холодная связь и горячая связь не являются взаимоисключающими. В рамках одного диалога DDE, клиент может запросить какие-либо данные с помощью сообщения WM_DDE_REQUEST (холодная связь), а другие данные с помощью сообщения WM_DDE_ADVISE (горячая связь).
Теплая связь
Теплая связь сочетает в себе элементы холодной и горячей связи. Диалог начинается как обычно:
Как и в случае горячей связи клиент посылает серверу сообщение WM_DDE_ADVISE, а сервер отвечает на него либо позитивно, либо негативно:
Однако, флаг, передаваемый с сообщением WM_DDE_ADVISE, показывает, что клиенту достаточно только узнавать о каждом изменении данных, а немедленно получать новые данные не нужно. Поэтому сервер, при любых изменениях данных, посылает клиенту сообщение WM_DDE_DATA с элементом данных равным NULL:
119
Теперь клиент знает, что конкретный элемент данных изменился. Для получения этого элемента, точно также, как и при холодной связи, клиент использует сообщение WM_DDE_REQUEST:
Также, как при горячей связи клиент может остановить сообщения, извещающие его об изменении элементов данных, отправив серверу сообщение WM_DDE_UNADVISE:
Диалог завершается после обмена сообщениями WM_DDE_TERMINATE:
Вэтих трех типах диалога используются все сообщения DDE, за исключением двух: WM_DDE_POKE (в котором клиент передает серверу данные, которые сервер не запрашивал) и WM_DDE_EXECUTE (в котором клиент посылает серверу командную строку). В этой главе эти сообщения рассматриваться не будут.
Взаголовочном файле DDE.H также определены четыре структуры:
•DDEACK (используется в сообщении WM_DDE_ACK)
•DDEADVISE (используется в сообщении WM_DDE_ADVISE)
•DDEDATA (используется в сообщении WM_DDE_DATA)
•DDEPOKE (используется в сообщении WM_DDE_POKE)
Впримерах программ этой главы мы коснемся первых трех представленных здесь структур.
Символьные строки и атомы
Ранее было показано, как клиент и сервер DDE идентифицируют данные с помощью трех символьных строк — приложения, раздела и элемента. Но в реальных сообщениях между клиентом и сервером эти символьные строки не появляются; вместо них используются атомы (atoms).
Атомы — это значения типа WORD, которые соответствуют символьным строкам, причем регистр строк значения не имеет. Атомы можно использовать в программе для работы с символьными строками, в этом случае таблица атомов (таблица соответствия значений атомов символьным строкам) хранится в области данных программы.
Атом определяется следующим образом:
ATOM aAtom;
С помощью функции AddAtom к таблице атомов можно добавить строку:
aAtom = AddAtom(pString);