Что такое "технология COM" и как с ней бороться?№22
От каменного века к бронзовому
Наша
рассылка, как и сама эволюция, - не стоит на месте. Мы с вами добрались до того места в
изложении, когда в самый раз написать второй пример, иллюстрирующий наш подход. И
мы его напишем.
Я
сразу хочу сказать - я не сторонник больших
и универсальных примеров. Пример должен
быть настолько краток, чтобы
демонстрировать именно то, для чего он
написан и ничего больше. Поэтому мы с вами
возьмем предыдущий пример и исправим
обнаруженные в нём недостатки тем самым
программным механизмом, который мы только
что изучили во всех промежуточных между
этими двумя примерами выпусках. Я
напомню - прошлый пример страдал двумя
фундаментальными недостатками: 1) он делал
объекты, но не позволял их уничтожать, когда
надобность в них исчезает; 2) он не позволял
на стороне клиента "знать" что же за
указатель мы получили. И если объект
реализовывал более одного интерфейса, то
это обстоятельство здорово сковывало
свободу клиента - в "обычном попроектном
программировании" клиент мог с
указателем делать значительно больше.
Исправим
пример. Ход исправления - сделаем наш
интерфейс NeoInterface наследником системно-определенного
интерфейса IUnknown. Напишем реализацию
методов интрефейса IUnknown в составе наших
объектов, а реализацию "полезных"
методов трогать не будем - мы только их
оформим "как положено" - с возвратом
типа HRESULT.
Мы
не будем трогать и конструкцию COM-сервера,
удовлетворимся на этот раз тем, что "тот"
сервер работал. Как должен выглядеть "настоящий
COM-сервер", наверное, будет примером №3.
Мы
перепишем клиент: наша задача в данный раз -
показать как функционирует объект "под
управлением IUnknown", а не то, что
концептуальная схема "вообще
работоспособна". Поэтому мы изменим окно
главного диалога - оно теперь не будет
вызывать методы двух объектов CBanzai и
CHello, а
будет порождать отдельные окна -
графические объекты. Эти графические
объекты будут относительно самостоятельны
- при создании "родительское окно"
снабдит их указателем на интерфейс
соответствующего объекта, и уже "маленькие
окна" будут вызывать методы объектов
сервера. Маленьких окон можно породить
много - они немодальны, поэтому методы
объектов сервера, сцепленных с окном, можно
вызывать в произвольном порядке и
наблюдать за их поведением в разных
ситуациях. Я обращаю внимание - в данном
случае вы будете вызывать методы уже настоящихCOM-объектов.
Исходные проекты этого
примера находятся здесь. С ними нужно
поступить так же, как и с проектами
предыдущего примера - собрать, и
скопировать исполняемые модули в один
каталог. Не забывайте, наш помощник Иван
Верба выложил свои модули - синхронный
перевод С++ текстов этого примера на Pascal в
среде Delphi - здесь. Я рекомендую
исследовать и протестировать и их, и - в
попарном сочетании клиента и сервера
вместе с моими модулями.
Для
тестирования запустите EXE. Программа
покажет диалог с тремя кнопками - "Создать
Banzai", "Создать Hello", "Закрыть
панель". Кнопка "Закрыть панель"
завершает весь процесс. Кнопки "Создать..."
создают новое "маленькое окно" и
получают у сервера новый экземпляр
указателя на COM-интерфейс. В составе "маленького
окна" имеются кнопки вызова
соответствующих методов - Show, Sound, Release
и кнопка "Клонировать". Кнопка "Клонировать"
"размножает диалог", т.е. она создаёт
точно такое же окно - сёстринское по
отношению к себе - и наделяет его копией
существующего указателя на COM-интерфейс.
Продвигая при этом счётчик ссылок.
Объекты
CBanzai и CHello остались теми же -
CBanzai остался
статическим объектом сервера, а CHello -
получается в динамической памяти. Поэтому
"управлять временем жизни" имеет и смысл
и видимый результат только для объекта CHello.
Это не запрещено и для CBanzai, но вы увидите
разницу сами.
Как
следствие, вы можете получить объект и
клонировать его, скажем, раза два... Три раза
(при получении и дважды при клонировании)
продвинется счётчик ссылок. А потом вы в
произвольном порядке можете вызывать Release
из всех этих "маленьких окон" - объект
уничтожится только после третьего вызова Release. Операции
new/delete в составе сервера
переопределены на собственную реализацию.
Она при вызове delete выдаёт на экран
сообщение "освобождается блок памяти",
т.е. вы воочию увидите когда именно объект
уничтожается - когда срабатывает delete
this. "Маленькое
окно" закрывается кнопкой системного
меню (крестик), а не кнопкой Release - это
сделано для того, чтобы вы могли
попробовать получить три копии объекта, а
вызвать метод Release на один раз больше... У нас -
небольшие модули, поэтому они и
обрушиваются тоже быстро и без побочных
последствий :)
Как
обычно - пример хорошо откомментирован "по
месту", мне здесь в рассылке больше
добавить нечего. В нём всё должно быть
понятно, а если что-то непонятно - напишите
мне. Как работает пример и почему именно так
- мы рассмотрим в следующем выпуске
рассылки, вместе с ответами на ваши
возможные вопросы. Не затягивайте их
задавание - к "вообще конструкции COM-объекта"
мы больше возвращаться не будем, далее мы
будем рассматривать только те или иные
особенности, ссылаясь на этот
пример как на основу. А "что есть вообще COM-объект" уже должно быть понятно "практически".
Не
задавайте мне сейчас вопросы "почему
именно так подсчитываются ссылки" (не в
смысле реализации AddRef/Release, а в смысле когда
и откуда их вызывать) - это самостоятельная
тема и на то будет специальный выпуск
рассылки, а, может, и не один.
Ну
и в заключение - следующий выпуск рассылки
выйдет с немного большим интервалом, чем
обычно. Я обязательно дождусь ваших
вопросов...