Глава 28. COM-cерверы и их клиенты
Рассмотрим различные серверы ActiveX, включая внедряемые серверы и серверы автоматизации.
Введение в серверы ActiveX
Серверы ActiveX делятся на три типа. Рассмотрим их предназначение.
Первым является полный сервер (full server) — неудачное название, поскольку можно подумать, что речь идет о полнофункциональном сервере ActiveX. На самом деле это не так. Прилагательное полный означает в данном случае, что он может выполняться и как сервер, и как полноценное приложение . Например, Microsoft Word можно запустить в виде самостоятельного приложения, создать документ или брошюру и затем сохранить содержание в виде doc-файла. Или же можно запустить другое приложение, например WordPad, и включить в него содержание, созданное в Microsoft Word.
Второй тип серверов является противоположностью первому и тоже назван неудачно — мини-сервер (mini-server). Это название означает, что сервер может использоваться только для включения его содержимого в другие приложения . Например, с Microsoft Word поставляется множество аплетов, помогающих создать документы профессионального вида. Одно из них называется WordArt. Если попытаться запустить эту программу, то выдается сообщение о том, что это приложение может выполняться только при запуске из другого приложения.
Поговорим теперь о третьем типе серверов. Здесь начинаются сложности. Третий тип серверов — серверы автоматизации (automation servers) — не имеет никакого отношения к полным или мини-серверам. Фактически такой сервер не позволяет включать свое содержимое в приложение, которое его выполняет. Вместо этого сервер автоматизации чаще всего просто предоставляет специальные объекты, методы и свойства, позволяющие управлять этим сервером . Предположим, например, что требуется объединить основной документ, созданный в Microsoft Word с документом-источником, хранящемся в базе данных Microsoft Access. Естественно, можно запустить Microsoft Word и выбрать в меню команду Слияние (наряду с десятком других) и достичь своей цели. Или же можно написать приложение на Visual Basic (или любом другом языке, поддерживающем автоматизацию) и удаленно контролировать Word. Таким образом можно настроить программу на автоматическое выполнение каждый день в 16:00 без вмешательства
пользователя.Существует два типа серверов автоматизации: серверы процесса (in-process servers) и локальные серверы (out-of-process servers). Коротко говоря, серверы процесса создаются на основании класса, хранящегося в файле DLL, загружаемого и выполняемого в том же адресном пространстве, что и само приложение . Все экземпляры класса имеют один и тот же код, но каждый имеет собственную область данных.
Локальные серверы работают в своем адресном пространстве . Этот тип серверов выполняется в виде exe-файлов (например, Word или Excel), которые могут либо управлять несколькими собственным экземплярами, либо запускать новую копию каждый раз при создании объекта сервера.
Автоматизация как механизм обмена информацией
Вероятнее всего, что в ближайшее время наиболее важными из всех указанных типов серверор станут серверы автоматизации ActiveX.
Автоматизация - это механизм обмена информацией между процессами (interprocess communications, IPC) в среде Windows, с помощью которого одна прикладная программа или компонент может управлять другой или автоматизировать (automate) ее. Система OLE-автоматизация отличается от высокопроизводительных программ IPC, предусмотренных в Win32, поскольку она обеспечивает лишь выполнение базовых функций, позволяющих связываться и обмениваться информацией обособленным программным модулям . Задача же разработчика - назначить имена межпрограммных интерфейсов и указать способы их использования.
Традиционно проблемы, связанные с IPC, решались в Windows с помощью динамического обмена данными (DDE)
. DDE - это метод обмена сообщениями, при котором одна программа (клиент DDE) может запрашивать данные у другой (сервера DDE). Существуют три основных типа транзакций или процедур DDE.- "Холодная" связь, при которой клиент запрашивает данные у сервера.
- "Теплая" связь, при которой клиент запрашивает у сервера уведомление о любом изменении указанных данных.
- "Горячая" связь, при которой сервер посылает клиенту как уведомление, так и сами указанные данные при каждом их изменении.
Все эти сценарии можно реализовать и с помощью средств OLE-автоматизации, причем этот путь обладает некоторыми очевидными преимуществами.
Во-первых, механизм DDE обеспечивает обмен информацией между процессами с помощью системы обработки сообщений , принятой в Windows, в то время как OLE-автоматизация базируется на функциях : она позволяет использовать предоставленные компонентом функциональные объекты для обращения к возможностям сервера и информации.
Во-вторых, система сообщений DDE налагает некоторые ограничения: и программы-клиенты, и программы-серверы DDE должны быть разработаны таким образом, чтобы прикладная программа корректно использовала DDE. Благодаря автоматизации разработчик получает возможность построить сервер, максимально точно соответствующий потребностям программ-клиентов, которые будут использовать предоставляемые им функции. Разработчик сервера составляет сценарии, описывающие, как будет использоваться сервер, указывая, какие методы и свойства (properties) автоматизации следует применять, когда и почему. Следовательно, сервер-автомат имеет более гибкую организацию, чем сервер DDE; эта организация не определяется заранее установленным протоколом, а может применяться в соответствии с конкретными требованиями.
В-третьих, процесс автоматизированного взаимодействия между двумя обособленными программами происходит косвенно через пару посредник-заглушка (proxy-stub) компонентной модели объекта, но возможно и непосредственное соединение между клиентом-автоматом и основанным на использовании DLL рабочим (внутренним, in
-process) сервером-автоматом. Средства автоматизации, как правило, работают быстрее, чем DDE, а производительность рабочих (внутренних) серверов-автоматов всегда будет превосходить производительность DDE-серверов.И наконец, протокол DDE никогда не был хорошо документирован, и его конкретные реализации зачастую несовместимы между собой. Поскольку система OLE Automation - это прежде всего платформа, на которой разработчик программы-сервера может строить собственные интерфейсы, а разработчик программы-клиента получать доступ к ним, она не страдает такого рода недостатками.
Разработка трехуровневых приложений с помощью компонентов серверов
Традиционно термин клиент/сервер соотносится с приложением, выполняемым на PC, которое связывается с базой данных, выполняемой на сервере. Это хорошее решение для мелкого и среднего предприятия с количеством пользователей от 5 до 50. Однако, когда количество пользователей достигает 500 и более, поддержка такой системы становится проблематичной (процесс увеличения числа пользователей от одного до сотен тысяч обычно называется масштабируемостью (scalability)). При каждом изменении в программном обеспечении необходимо переустановить новое приложение на каждом клиентном компьютере. Этот процесс требует много времени и средств, учитывая необходимость совместимости всех компонентов.
Что касается приложений Internet и intranet, то они должны основываться на работе с броузерами. Впрочем, если попытаться обслужить тысячи пользователей с помощью простых сценариев CGI, то все закончится обеспечением одновременного доступа к файлам или базе данных.
Сегодня компания Microsoft продвигает технологии OLE и ActiveX (термины, по сути, являющиеся синонимами), которые предусматривают так называемую удаленную автоматизацию. Фактически, удаленная автоматизация (remote automation) позволяет приложению, выполняемому на компьютере РС1, соединиться и создать объект автоматизации на компьютере РС2. Наиболее существенным здесь является не то, что можно заниматься распределенной разработкой без углубленного знания протоколов TCP/IP или NetBEUI, а переход к трехуровневой разработке приложений на основе продуктов Microsoft.
Серьезной проблемой удаленной автоматизации является то, что этот процесс не обладает какой-либо степенью безопасностью, не имеет сервера для управлениями транзакциями или соединениями с базой данных и никогда не предполагался для использования в Internet или intranet.
Позднее Microsoft выпустила спецификацию распределенной модели СОМ (Distributed СОМ — DCOM), улучшающую распределенный аспект объектов модели СОМ, включая поддержку приложений Internet и intranet, но не затрагивающую вопросы безопасности.
Недавно компания Microsoft объявила о выпуске сервера транзакций Microsoft Transactions Server (MTS) . Он не только решает все проблемы безопасности, названные ранее, но и поддерживает обработку транзакций. Фактически теперь серверы автоматизации OLE можно создавать в виде небольших, простых компонентов, не нуждающихся в информации о внешнем мире.
Давайте рассмотрим пример применения сервера MTS. Располагая номером банковского счета, можно создать два сервера автоматизации: один для внесения денежного вклада со счета и другой для зачисления денег на счет. Теперь необходимо сообщить об этих компонентах серверу MTS (запомните слово компоненты). Для этого они устанавливаются не на клиентный компьютер, а на сервер, выполняющий MTS.
Остается написать небольшое приложение, которое будет соединяться с сервером MTS, начинать транзакцию, вызывать компонент для зачисления денег на счет, вызывать компонент для снятия денег со счета и затем завершать транзакцию. Обработка процессов, выполненных компонентами, ложится на сервер MTS. Если любой из них заканчивается ошибкой, то сервер автоматически аннулирует всю транзакцию.
Такая идеология выглядит странной. Требуется время на то, чтобы понять ее преимущества. MTS выполняет серию транзакции, базирующихся на компонентах. Обычно для этого используется язык SQL, который теперь становится ненужным. Сервер MTS в состоянии обработать транзакцию самостоятельно с помощью механизма MSDTC.
Основная идея этого раздела заключена в том, чтобы отойди от двухуровневой концепции клиент/сервер и взять на вооружение трехуровневую модель , в которой приложение-клиент является чрезвычайно простым (приложение с облегченной клиентной частью ), поскольку выполняет только основные функции вызова сервера автоматизации и отображения результатов (для этого может использоваться как тонкий клиент, так и броузер Web). Второй уровень — сервер приложений, выполняющий основную работу. На нем могут быть установлены сотни и даже тысячи небольших компонентов. И третий уровень представляет собой собственно базу данных
.Сравнение библиотек MFC и ATL
Перед тем как непосредственно перейти к созданию серверов ActiveX, важно разобраться в причинах, по которым Microsoft выпустила набор шаблонов для создания компонентов ActiveX (включая серверы автоматизации, полные и мини-серверы, контейнеры и элементы управления).
Библиотека MFC среды Developer Studio представляет собой прекрасную иерархию классов, но в некоторых случаях пользоваться ею неудобно. В частности, это касается создания небольших компонентов ActiveX, предназначенных для использования в Web. Глупо тратить время на загрузку нескольких DLL, которые оказываются ненужными. Если же статически связать элемент
управления MFC или компонент, то получится очень большой файл EXE, DLL или ОСХ.Итак, после продолжительных дебатов было решено создать новую библиотеку, названную библиотекой активных шаблонов (Active Template Library, ATL). Впервые она была включена в Mi
crosoft Visual C++ 4.2.Библиотека ATL является прекрасным решением для создания небольших компонентов,
поскольку оно не связано с использованием библиотеки MFC (если только программист специально не укажет включить такую поддержку).Недостатком библиотеки ATL является нехватка специалистов, с которыми можно проконсультироваться, и дефицит практических примеров, помогающих в ее освоении. Кроме того, отсутствует возможность использовать мастер Class Wizard для создания приложения. К счастью, можно еще использовать мастера СОМ Wizard и несколько диалоговых окон для добавления классов, методов и свойств.
Какой способ использовать — решать программисту, но настоятельно рекомендуется при возможности использовать библиотеку ATL, поскольку она обладает рядом преимуществ, например, легкостью в создании серверов автоматизации .
Мини-сервера или полные сервера, а также контейнеры составных документов, лучше всего разрабатывать с помощью MFC, которая обеспечивает работу с документами на высоком уровне. Напомним, что выбор типа приложения (контейнер или сервер, тип сервера) осуществляется в третьей диалоговой панели, которая появляется в процессе работы с мастером MFC AppWizard (EXE) при создании SDI- или MDI-приложения.
MFC-cерверы автоматизации
Итак, автоматизация представляет собой механизм обмена информацией между взаимодействующими приложениями. Приложения, являющиеся серверами автоматизации , обладают способностью отображать свои объекты с помощью свойств и методов. Приложения, которые являются клиентами автоматизации или контроллерами автоматизации , могут получить доступ к этим отображаемым свойствам и методам через OLE-интерфейс IDispatch.
Рассмотрим, как создать собственый сервер автоматизации с помощью библиотеки MFC. Для быстрого и легкого создания необходимого класса и методов используются мастера AppWizard и ClassWizard
.DLL-серве р автоматизации
Итак, это будет внутренний сервер Automation. То есть он будет работать в том же адресном пространстве, что и процесс клиента, и, следовательно, выполняться как DLL модуль. Для создания внутреннего сервера средствами Visual C++ следует просто подготовить фрагмент программы для оболочки DLL. Затем для организации с ним взаимодействия добавить описание интерфейса объекта Automation, указав все его параметры и методы.
При помощи мастера MFC AppWizard (dll) следует подготовить проект приложения SvrDll. В случае проекта DLL требуется только один шаг: задать тип создаваемого DLL модуля и указать, необходимо ли подключение механизма автоматизации.
Выберем вариант
"Regular DLL using shared MFC DLL" (стандартный DLL модуль с разделяемым DLL модулем - MFC). Стандартный DLL модуль это модуль, с которым может работать люба прикладная программа Windows, а не только созданная на базе MFC. Возможен другой вариант - "Regular DLL with MFC statically linked" (стандартный DLL модуль с постоянной привязкой к MFC), но постоянно привязанные к создаваемому DLL-модулю фрагменты библиотеки MFC значительно увеличат его размер. Выбор первого из названных вариантов (shared MFC DLL) означает, что комплект поставки создаваемого сервера придется дополнять модулем DLL-библиотеки MFC; однако в этом случае размер файла, содержащего сервер, будет сравнительно небольшим. (Остается еще один вариант, который неприемлем для DLL модулей автоматизированного сервера : "MFC-Extension DLL (using shared MFC DLL)" (расширение DLL при использовании разделяемой MFC DLL). Это расширение DLL предназначено для создания DLL модулей с классами, производными от MFC, которые могут применяться в прикладных программах на базе MFC, и только в них.)Кроме того, следует выбрать еще одну возможность – автоматизацию (включить флажок
"Automation" ), что приводит к добавлению в файл определенного программного кода, необходимого для формирования автоматизированного сервера.В результате создания файлов проекта и текста программы имеется обобщенный вариант внутреннего сервера ; осталось лишь описать и разработать конкретный объект и его методы и параметры.
Класс сервера
Пока имеется только оболочка автоматизированного сервера, а в программе для DLL-модуля еще нет самого объекта. Мастер AppWizard создал лишь единственный класс MFC - CSvrDllApp, определив его как производный от класса CWinApp библиотеки MFC. Согласно принятым правилам, в любом EXE- или DLL-модуле, создаваемом на базе MFC, должен быть предусмотрен класс, производный от CWinApp. В нем должен содержаться измененный вариант CWinApp::InitInstance - виртуальной функции, активизируемой автоматически при первом вызове механизмом MFC данного EXE- или DLL- модуля. (В случае EXE-модулей MFC функци CWinApp::InitInstance вызывается из процедуры WinMain; а если это DLL-модули - из DllMain.)
Созданный мастером AppWizard вариант InitInstance содержит всего несколько строк:
BOOL CSvrDllApp::InitInstance() { COleObjectFactory::RegisterAll(); return TRUE; }
COleObjectFactory - это особый класс MFC, содержащий фабрику класса OLE (OLE class factory); любая из фабрик класса MFC-сервера является экземпляром этого исходного класса. При фактическом создании каждой фабрики класса COleObjectFactory записывает его адрес в свой внутренний связанный список, так что всегда может получить доступ к любому или сразу ко всем фабрикам класса сервера.
Любая фабрика класса запускается функцией COleObjectFactory::RegisterAll, которая и регистрирует его как способного к созданию определенного типа объектов. Будучи статической функцией (то есть доступной глобально и не требующей для своего вызова обращения через некоторый экземпляр класса), RegisterAll просматривает связанный список всех фабрик классов, чтобы зарегистрировать каждую из них. Как только они начнут работать, к ним можно обращаться для создания обслуживаемых ими экземпляров OLE объектов. А поскольку InitInstance вызывается из функции DllMain внутреннего сервера (при первой загрузке этого DLL модуля), все его
фабрики классов с самого начала готовы производить экземпляры предоставляемых этим сервером объектов.Так как при работе с мастером AppWizard, был избран вариант с автоматизацией, появились еще и следующие три функции: DllGetClassObject, DllCanUnloadNow и
DllRegisterServer. Все они относятся к типу STDAPI, т. е. подчиняются стандартному соглашению о вызове функций Windows API, не входят в состав ни одного из классов Cи++ и могут вызываться из любой программы Windows (как любая другая функция Windows API).Первые две функции обеспечивают взаимодействие внутреннего сервера с библиотекой функций OLE Component Object Model . Например, обратившись к функции CoCreateInstance из OLE API, COM-клиент (или контейнер) может создать экземпляр предоставляемого сервером объекта. В свою очередь, эта функция обратится к CoGetClassObject, чтобы получить указатель на интерфейс IUnknown фабрики класса требуемого объекта, и затем, используя имеющийся в этом интерфейсе указатель, вызовет IClassFactory::CreateInstance. Эта функция
запустит фабрику класса, чтобы создать экземпляр запрашиваемого объекта.DllGetClassObject - это стандартная функция API, которую должен содержать любой внутренний сервер, чтобы обеспечить работу функции CoGetClassObject: если сервер организован в виде DLL модуля, то CoGetClassObject будет вызывать DllGetClassObject . Мастер AppWizard формирует следующий фрагмент программы для этой функции с реализацией вызова внутренней функции MFC - AfxDllGetClassObject:
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); return AfxDllGetClassObject(rclsid, riid, ppv); }
Функции AfxDllGetClassObject просматривает имеющийся на сервере связанный список фабрик объектов (предоставляемый COleObjectFactory) в поиске экземпляра, который позволит получить нужный тип объекта, и передает в вызывающую программу указатель на его интерфейс IUnknown.
Аналогично обстоят дела с функцией
DllCanUnloadNow . Время от времени OLE клиент может вызывать функцию OLE API - CoFreeUnusedLibraries, чтобы высвободить ресурсы и выгрузить из памяти неиспользуемые больше DLL модули. Внутри CoFreeUnusedLibraries организуется вызов функции DllCanUnloadNow, осуществляемый для каждого из имеющихся внутренних серверов; именно поэтому мастер App Wizard добавил экземпляр и этой функции в нашем примере:STDAPI DllCanUnloadNow(void) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); return AfxDllCanUnloadNow(); }
DllCanUnloadNow обращается к внутренней функции MFC - AfxDllCanUnloadNow. Первым делом из нее вызывается другая внутренняя функция - AfxOleCanExitApp. Сначала она быстро проверяет, больше ли нуля общее количество объектов, имеющихся у этой прикладной программы или DLL модуля MFC. Если активных объектов нет, в принципе работа данного модуля может быть завершена. В этом случае функция AfxDllCanUnloadNow просматривает содержащийся в COleObjectFactory связанный список, чтобы отыскать хотя бы одну задействованную фабрику класса (учитывая также фабрики в DLL-модулях, загруженных из текущего модуля)
. Если таковые отсутствуют, функция сообщает, что данный DLL модуль можно выгрузить из памяти. Таким образом, использование функции DllCanUnloadNow служит гарантией того, что внутренний сервер MFC будет выгружен клиентом из памяти, как только он перестанет использовать его объекты или фабрики классов.Если же внутренний сервер не содержит функции DllCanUnloadNow и не экспортирует ее извне, тогда его DLL модуль будет оставаться в памяти до тех пор, пока клиент не вызовет функцию
CoUninitialize ; именно ее использует OLE клиент (или контейнер), чтобы отказаться от OLE библиотеки после закрытия рабочих окон программы и завершения основного цикла обработки сообщений. В случае когда клиент использует несколько серверов, те из них, которые не содержат функцию DllCa nUnloadNow, будут оставаться в памяти еще достаточно долго с момента последнего к ним обращения, возможно завладев при этом значительной долей памяти и системных ресурсов. Функция DllCanUnloadNow используется для того, чтобы выгрузить из памяти внутренний сервер сразу после окончания работы с ним, высвободив тем самым занятые им память и ресурсы.Теперь подробно рассмотрим третью введенную мастером AppWizard функцию - DllRegisterServer.
Регистрация сервера
После компиляции программы автоматизированного сервера следует произвести его регистрацию в системе, чтобы он мог быть использован клиентами . Именно здесь понадобится третья из предложенных мастером AppWizard функций -
DllRegisterServer . Осуществить регистрацию всех автоматизированных объектов, присутствующих на сервере, может любая программа Windows, обратившаяся к этой функции. В системные файлы Windows входит программа regsvr32, которая загрузит DLL модуль сервера и вызовет функцию DllRegisterServer (команда regsvr32.exe SvrDll):STDAPI DllRegisterServer(void) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); COleObjectFactory::UpdateRegistryAll(); return S_OK; }
DllRegisterServer обращается к функции COleObjectFactory::UpdateRegistryAll, котора просматривает связанный список имеющихся фабрик класса и сообщает, что каждая из них должна обновить системный реестр для своих объектов.
Достаточно странно, но мастер AppWizard не предоставляет отдельной функции для удаления информации о серверах из реестра. Единственное, что придется сделать, чтобы восполнить пробел, - это ввести дополнительную функцию DllUnregisterServer, практически идентичную DllRegisterServer:
STDAPI DllUnregisterServer(void) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); COleObjectFactory::UpdateRegistryAll(FALSE); return S_OK; }
Отличается она лишь аргументом, передаваемым функции UpdateRegistryAll, благодаря которому каждому генератору класса будет выставлено требование удалить из реестра информацию относительно своих объектов Automation. Теперь при вызове модуля regsvr32 с параметром /u (команда regsvr32.exe /u SvrDll) произойдет запуск данной функции и информация о сервере в реестре будет уничтожена.
Усовершенствование сервера – добавление COM-объекта
Разработав программу внутреннего сервера обобщенного типа, для реализации предоставляемых сервером COM-объектов можно приступать к дополнению его классами MFC. Чтобы создать класс для организации взаимодействия с механизмом, можно использовать любой класс, производный от CCmdTarget библиотеки MFC (CWnd и многие другие).
Итак, чтобы сделать сервер более практичным, необходимо создать новый класс на основе CCmdTarget — основного класса автоматизации в библиотеке MFC. Для этого следует выполнить следующие действия:
- выберите команду “New Class” мастера ClassWizard;
- в поле “Name” ввести имя класса, например CSimpleMFC;
- в поле “Base Class” указать CCmdTarget;
- в разделе “Automation” указать “Createable by type ID” и убедиться, что в поле введен текст, например SvrDll.SimpleMFC.
Остановимся подробнее на последнем пункте. Для того чтобы новый класс работал с механизмом автоматизации, следует выбрать один из следующих двух вариантов раздела “Automation”:
- Automation, предоставляющий таблицу свойств и методов; или
- Createable by type ID (Создаваемый по идентификатору типа объекта), который, помимо этого, присваивает некоторое имя и параметр GUID для идентификации объекта в системном реестре.
Вариант Createable следует использовать, когда необходимо предусмотреть возможность создания объекта автоматизации внешними средствами , например с помощью функции CreateObject из арсенала Visual Basic. Вариант Automation пригодится лишь в случае, когда классы, обладающие средствами работы с механизмом автоматизации, будут создаваться только через другой аналогичный объект - например как составная часть иерархии объектов.
Выбрав вариант Createable, следует ввести псевдоним (programmatic ID) для своего объекта - т. е. наименование, по которому его можно будет найти в системном реестре Этот параметр всегда строится по следующему принципу: ИмяМодуля.ИмяОбъекта[.номер_версии] . Рекомендуется PROGID сделать максимально информативным.
Созданный мастером ClassWizard класс CSimpleMFC и является С++ -классом COM-объекта SvrDll.SimpleMFC, реализующим его интерфейс IID_ISimpleMFC (он наследуется от интерфейса IDispatch).
Для добавления в интерфейс COM-объектов специализированных методов (реализовываться они будут в классе CSimpleMFC) следует:
- запустить ClassWizard;
- переключится на его панель “Automation”;
- нажать кнопку “Add Method…”, в результате появится диалоговое окно “Add Method”;
- в этом диалоге указать название метода (поле “External name”), имя компонентной функции Cи++ (метод класса CSimpleMFC), которая будет выполняться при выборе данного метода (поле “Internal name”), тип возвращаемого значения этого метода и его аргументы, если таковые имеются.
При добавлении новых методов автоматизации (видимых клиентом) ClassWizard вносит в таблицу параметров и методов (в схему диспетчеризации BEGIN_DISPATCH_MAP - END_DISPATCH_MAP класса CSimpleMFC), новый элемент. Допустим, добавлен новый метод, имеющий следующее объявление: afx_msg void Initialize (long initializer);. Тогда в схему диспетчеризации класса будет добавлена строка в следующем формате:
DISP_FUNCTION(CSimpleMFC, "Initialize", Initialize, VT_EMPTY, VTS_I4)
Для того, чтобы добавить COM-объекту параметр (свойство)
следует- выбрать панель “Automation” мастера ClassWizard;
- нажать кнопку “Add Property...”, чтобы перейти в диалоговое окно с таким же именем;
- в этом диалоге выбрать один из двух представленных вариантов реализации нового параметра: через пару принадлежащих классу функций Get/Set или через комбинацию какого-либо элемента данных (для хранения значения этого параметра) и функции реакции на изменение этого параметра.
В первом варианте обеспечивается большая степень контроля при считывании или задании клиентом нового значения для параметра; однако в этом случае в классе не выделяется память под новый параметр. Во втором варианте выделение памяти для параметра предусмотрено, однако COM-объект будет лишь пассивно предупрежден об изменении его значения: функция реакции вызывается механизмом MFC лишь после того, как параметр изменился.
Допустим, вводится новое свойство NewProp типа short и выбран второй вариант реализации параметра. В ответ мастер ClassWizard добавит в определение класса CSimpleMFC следующие описания:
short m_newProp; afx_msg void OnNewPropChanged();
В реализации класса CSimpleMFC появится пока еще "пустая" функция реакции на изменение параметра CSimpleMFC::OnNewPropChanged. В схему диспетчеризации при этом будет добавлена следующая строка:
DISP_PROPERTY_NOTIFY(CSimpleMFC,"NewProp",m_newProp, OnNewPropChanged,VT_I2)
После компиляции и компоновки приложения следует зарегистрировать DLL-сервер в реестре операционной среды Windows. Для этого следует выбрать команду “RegisterControl” меню “Tools” среды Microsoft Developer Studio.
EXE-сервер автоматизации
Рассмотрим кратко процесс создания exe-сервера автоматизации без пользовательского интерфейса.
Сначала при помощи средства MFC AppWizard (exe) следует создать проект приложения SvrExe, основанного на диалоговой панели. При создании проекта необходимо включить отметку флажка “Automation” (для поддержки приложением автоматизации). В результате получится простейшее приложение OLE-автоматизации.
Затем необходимо удалить из проекта (и из каталога) файлы, относящиеся к диалоговой панели класса CSvrExeDlg – SvrExeDlg.h, SvrExeDlg.cpp, DlgProxy.h, DlgProxy.cpp. Из ресурсов приложения следует удалить шаблон диалоговой панели с идентификатором IDD_SVREXE_DIALOG.
Далее нужно изменить файл SvrExe.cpp (файл реализации класса приложения-сервера) следующим образом:
#include "stdafx.h" #include "SvrExe.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif BEGIN_MESSAGE_MAP(CSvrExeApp, CWinApp) //{{AFX_MSG_MAP(CSvrExeApp) // NOTE - the ClassWizard will add and remove mapping macros here. // DO NOT EDIT what you see in these blocks of generated code! //}}AFX_MSG END_MESSAGE_MAP() CSvrExeApp::CSvrExeApp() { } CSvrExeApp theApp; BOOL CSvrExeApp::InitInstance() { // Инициализация библиотек OLE if (!AfxOleInit()) { AfxMessageBox(IDP_OLE_INIT_FAILED); return FALSE; } // Проверка командной строки – проверка на // запуск в качестве сервера автоматизации if (RunEmbedded() || RunAutomated()) { // Регистрация всех фабрик OLE-сервера как выполняемых. // Это позволит OLE создавать объекты из других приложений. COleTemplateServer::RegisterAll(); // Приложение продолжает работу, так как запущено в // качестве сервера автоматизации return TRUE; } else { // Регистрация приложения качестве сервера COleObjectFactory::UpdateRegistryAll(); // Приложение запущено без параметров – прекращение работы return FALSE; } }
С помощью ClassWizard добавить в проект приложения SvrExe класс CSimpleMFC, в качестве базового для него указать класс CCmdTarget (класс, поддерживающий автоматизацию). Обязательно в группе “Automation” включить переключатель “Createable by type ID” и в качестве псевдонима ввести SvrExe.SimpleMFC . Созданный мастером ClassWizard класс CSimpleMFC и является С++ -классом COM-объекта SvrExe.SimpleMFC, реализующим его интерфейс IID_ISimpleMFC (он наследуется от интерфейса ID
ispatch).При помощи вкладки “Automation” панели ClassWizard и кнопки “Add property…” для COM-объекта добавляются необходимые ему свойства. А с помощь вкладки “Automation” панели ClassWizard и кнопки “Add method…” в интерфейс COM-объекта добавляются метод автоматизации (например, метод afx_msg void Initialize (long initializer);).
Для регистрации приложения в качестве сервера автоматизации следует запустить откомпилированное и скомпанованное приложение на выполнение, не передавая ему при этом никаких параметров (созданный сервер является саморегистрирующимся). В этом случае сервер только регистрируется и на этом прекращает свою работу.
ATL-cервер ы автоматизации
В этом разделе рассмотрим процессы создания серверов автоматизации с помощью более новой и более компактной библиотеки активных шаблонов (Active Template Library — ATL). Основным преимуществом библиотеки ATL является компактность, но она достигается за счет усложнения процесса разработки. Создание простых приложений остается довольно простым, но разработка сложных представляет большую проблему. Если сервер, к тому же должен обеспечивать развитой пользовательский интерфейс, уметь работать с базами данных, то средств ATL для этого явно недостаточно, поэтому в таких ситуациях рекомендуется создавать MFC-сервера.
Создание сервера автоматизации
Рассмотрим создание сервера процесса, предоставляющего класс под названием SimpleATL.
Среди проектов Developer Studio следует выбрать пункт ATL СОМ AppWizard и в поле “Project Name” ввести имя проекта SvrDll (или SvrExe в случае локального сервера). Мастер ATL СОМ AppWizard создаст сервер за один шаг. Следует только в появившейся диалоговой панели отметить обе предлагаемые дополнительные опции “Allow merging of proxy/stub code” и “Support MFC”, а затем выбрать тип сервера “Dynamic Link Library (DLL)” (сервер в процессе) или “Executable (EXE)” (локальный сервер). Заметим, что включение опции “Allow merging of proxy/stub code” позволяет мастеру разместить в COM-сервере вспомогательный код для MIDL.
Мастер ATL СОМ AppWizard елает еще меньше, чем мастер из библиотеки MFC: он вообще не генерирует никаких классов, только глобальные объявления, необходимые для создания файла DLL. Все остальное разработчику необходимо выполнить самостоятельно.
Усовершенствование сервера
Попросту говоря, сейчас необходимо выполнить всю основную работу. Для этого следует:
- выбрать команду “New ATL Object” меню “Insert” среды Developer Studio;
- в окне мастера ATL Object Wizard следует выбрать категорию “Objects” и элемент “Simple Object”, затем нажать кнопку “Next”.
- в поле ”Short Name” закладки “Names” появившегося диалогового окна “ATL Object Wizard Properties” необходимо указать имя объекта SimpleATL;
- перейти к закладке “Attributes” этого же диалогового окна и выбрать свойства: “Threading model” – “Free”, “Interface” – “Dual”, “Aggregation” – “No”. Также следует отметить (выбрать) опции “Support ISupportErrorInfo”, “Support Connection Points”, “Free Threaded Marshaler”, а затем щелкнуть на кнопке ОК.
Теперь в рабочем окне классов проекта повляется два новых узла. Первый узел – это класс CSimpleATL, основанный на нескольких различных интерфейсах. Второй узел – это непосредственно интерфейс ISimpleATL, основанный на интерфейсе IDispatch. Мастер ATL Object Wizard генерирует класс CSimpleATL и файл IDL для данного проекта. Этот файл используется для создания библиотеки типов для нескольких интерфейсов COM.
Следует обратить внимание, что интерфейс ISimpleATL представлен в рабочем окне проекта в виде одного из подузлов. Это показывает, что класс CSimpleATL реализует по крайней мере интерфейс ISimpleATL.
Теперь необходимо добавить методы и свойства указанному интерфейсу. Для этого необходимо выполнить следующие действия:
- выбрать узел ISimpleATL и щелкнуть на нем правой кнопкой мыши;
- выбрать команду “Add Method” или “AddProperty”;
- в случае добавления метода в поле “Method Name” ввести имя метода, а в поле “Parameters” ввести параметры, их типы и характеристики (как в описании методов IDL-файле). Реализации методов ATL должны возвращать HRESULT для всех методов и свойств. Характеристика [in] соответствует входным параметрам, а обозначение [out] — выходным, обозначение [out, retval] соответствует возвращаемому параметру (он должен быть только один, причем последним в списке параметров);
- в случае добавления свойства следует ввести тип свойства и его имя, а также вариант реализации нового параметра (например, через пару функций Get/Put).
Как и раньше, следует откомпилировать сервер и зарегистрировать сервер автоматизации в операционной системе. Для регистрации DLL-сервера следует выбрать команду “RegisterControl” меню “Tools”. Локальный сервер регистрируется при его запуске с параметром /RegServer (наприме, SvrExe.exe /RegServer). В этом случае сервер регистрируется и на этом прекращает свою
работу. Заметим, что для удаления информации о сервере из реестра Windows используется запуск сервера с ключом /UnregServer (команда SvrExe.exe /UnregServer).MFC-к онтроллер автоматизации
Для создания COM-клиентов можно использовать различные среды разработки. Рассмотрим, как создавать контроллеры автоматизации в cреде Microsoft Developer Studio с помощью MFC. Создадим, например, при помощи средства MFC AppWizard (exe) проект приложения Cnt, основанного на диалоговой панели.
Затем в проект приложения-контроллера Cnt следует добавить специальный контроллерный класс, обеспечивающий работу приложения с COM-объектом. Для этого необходимо вызвать ClassWizard, нажать на кнопку “Add class…” и выбрать пункт “From a type library…”. В появившемся окне следует из каталога проекта сервера автоматизации выбрать файл SvrExe.tlb (или файл библиотеки типов другого COM-сервера). Сгенерированные мастером ClassWizard файлы SvrExe.cpp и SvrExe.h автоматически добавятся в проект контроллера автоматизации Cnt.
Для того, чтобы создать COM-объект сервера, необходимо сначала объявить объект obj контроллерного класса ISimpleMFC (того класса, который создал мастер ClassWizard по библиотеке типов сервера SvrExe.tlb), наследованного от класса ColeDispatchDriver
.Затем для объекта obj следует вызвать его метод CreateDispatch (метод его базового класса), который загружает серверную программу и получает указатель на интерфейс IDispatch COM-объекта (сохраняемый в переменной m_lpDispatch класса COleDispatchDriver):
… AfxOleInit(); // инициализация библиотек OLE BOOL result=obj.CreateDispatch("SvrExe.SimpleMFC"); if(result!=TRUE) AfxMessageBox("COM-object didn't create"); …
Для вызова методов COM-объекта, входящих в состав интерфейса ISimpleMFC, следует воспользоваться вызовами в следующем формате:
ТипВозвращаемогоЗначения ИмяПеременной=obj.ИмяМетода(Параметры);
например,
obj.Initialize(2L);