Модель программирования Component Object Model. Разработка COM-сервера, страница 18

Обратите внимание на то, что поддержка заглушек proxy-stub выполнена в отдельном проекте ATestPS и будет расположена в отдельной DLL. В окне ClassView откройте папку ATest4Globals и просмотрите список экспортируемых функций. Откройте файл ATest.cpp и просмотрите их коды. Как видите работа идет с глобально объявленным объектом _AtlModule, который является объектом класса CATestModule, скроенного по шаблону CAtlDllModuleT. Буква T в конце имени означает, что это не класс, а шаблон классов. Итак, шаблон создал для нас класс CATestModule, который обладает набором методов, смысл которых вам знаком: DllMain, DllGetClassObject, DllCanUnloadNow и т.д.

Братом (sibling template class) шаблона CAtlDllModuleT является шаблон классов CAtlExeModuleT, который позволяет создавать классы приложений, отличающихся от приложений, поддерживаемых классом CWinApp (MFC). Отличие, как было сказано, состоит в скорости выполнения.

Откройте сгенерированный мастером файл описания интерфейсов ATest.idl и убедитесь в том, что этот файл пока готов лишь к созданию только библиотеки типов, но он не содержит описания COM-объекта. Итак, COM DLL inproc-сервер (или дом для ко-классов) готов. Теперь можно начать процесс начинки его классами (или одним классом), которые (в свою очередь) являются вместилищами экспонируемых интерфейсов. Говорят, что ко-класс реализует или экспонирует интерфейсы. Этот процесс тоже автоматизирован. Для создания ко-класса надо воспользоваться еще одним мастером ATL.

Как работает DLL

Вы уже знаете, что созданный и подключенный компоновщиком динамический модуль система интегрирует в пространство другого (клиентского) процесса, загрузив его по определенному базовому адресу. Любая динамически загружаемая библиотека экспортирует функции, которые пишутся в расчете на то, что их будет вызывать клиентское приложение или другая DLL. Глобальная функция DllMain представляет собой точку входа в динамически подключаемую библиотеку. Она является некоторого рода заглушкой (placeholder) для реального, определяемого библиотекой имени функции. Так же как и в случае с WinMain, мы не вызываем DllMain, это делает операционная система.

Первый параметр DllMain, подан системой и представляет собой Windows-описатель DLL. Его можно использовать при вызове функций, требующих этот описатель, например, при вызове FreeLibrary или GetModuleFileName. Второй параметр указывает причину вызова DLL. Он может принимать одно из четырех значений:

¨  DLL_PROCESS_ATTACH — указывает на то, что DLL загружается в виртуальное адресное пространство процесса, так как стартовал сам процесс (неявный вызов DLL) или была вызвана функция LoadLibrary (явный вызов DLL).

¨  DLL_THREAD_ATTACH — указывает на то, что текущий процесс создает новый поток (thread). В этот момент система вызывает все DLL, которые уже загружены в пространстве процесса, с тем, чтобы они учли новый поток в TLS-слотах (Thread Local Storage).

¨  DLL_THREAD_DETACH — указывает на то, что поток завершается и DLL может освободить динамические ресурсы, связанные с данным потоком (если они были).

¨  DLL_PROCESS_DETACH — указывает на то, что DLL выгружается из адресного пространства процесса, либо в результате завершения процесса, либо потому, что процесс вызвал функцию FreeLibrary. В этом случае DLL может освободить память (TLS).

Если DllMain вернет FALSE, или 0, то клиентское приложение завершится с кодом ошибки. Характерно, что стратегия работы с COM-объектами сходна со стратегией, используемой при работе с DLL. Последняя заключается в том, что каждый вызов функции LoadLibrary увеличивает на единицу счетчик числа пользователей библиотеки. Вызов функции FreeLibrary уменьшает значение счетчика. Обнаружив, что счетчик числа пользователей равен нулю, система автоматически выгрузит ее. Если после этого вызвать какую-либо экспортируемую DLL функцию, то это вызовет исключение Access Violation, так как код по указанному адресу уже не отображается на адресное пространство процесса.

Создание COM-класса