На этом шаге мы рассмотрим содержимое файла EncodeServer.cpp.
На вкладке ClassView рабочей области есть узел Globals, развернув который, Вы увидите несколько глобальных функций (с префиксом Dll) и один глобальный объект _Module.
Рис.1. Вкладка ClassView
Эти элементы находятся файле EncodeServer.cpp, они добавлены в проект мастером ATL COM AppWizard для предоставления функций точки входа, экспортируемых из DLL и вызываемых из СОМ и других системных утилит. Вот какой код находится в файле EncodeServer.cpp:
#include "stdafx.h" #include "resource.h" #include <initguid.h> #include "EncodeServer.h" #include "EncodeServer_i.c" #include "Encoder.h" CComModule _Module; BEGIN_OBJECT_MAP(ObjectMap) OBJECT_ENTRY(CLSID_Encoder, CEncoder) END_OBJECT_MAP() ///////////////////////////////////////////////////////////////////////////// // Точка входа в DLL extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/) { if (dwReason == DLL_PROCESS_ATTACH) { _Module.Init(ObjectMap, hInstance, &LIBID_ENCODESERVERLib); DisableThreadLibraryCalls(hInstance); } else if (dwReason == DLL_PROCESS_DETACH) _Module.Term(); return TRUE; // ok } ///////////////////////////////////////////////////////////////////////////// // Здесь проверяем, может ли OLE выгрузить нашу DLL STDAPI DllCanUnloadNow(void) { return (_Module.GetLockCount()==0) ? S_OK : S_FALSE; } ///////////////////////////////////////////////////////////////////////////// // Возвращает фабрику классов для создания объекта нужного типа STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) { return _Module.GetClassObject(rclsid, riid, ppv); } ///////////////////////////////////////////////////////////////////////////// // DllRegisterServer помещает записи в реестр // (регистрирует сервер) STDAPI DllRegisterServer(void) { // registers object, typelib and all interfaces in typelib return _Module.RegisterServer(TRUE); } ///////////////////////////////////////////////////////////////////////////// // DllUnregisterServer удаляет записи из реестра // (удаляет регистрационную запись сервера) STDAPI DllUnregisterServer(void) { return _Module.UnregisterServer(TRUE); }
В первой половине файла находится объявление объекта _Module класса CComModule. В классе CComModule реализован модуль СОМ-сервера, позволяющий клиенту запрашивать его компоненты. ССоmModule поддерживает как внешние модули, так и модули, встраиваемые в процесс. Внешние серверы (расположенные в ЕХЕ-файлах) используют объект, реализующий особые функции приложения и являющийся производным от CComModule.
Экземпляр класса CComModule имеет таблицу, называемую картой объекта (object map), которая хранит набор определений объекта. Внутренние механизмы карты объекта создают и поддерживают массив структур _ATL_OBJMAP_ENTRY, которые содержат информацию, используемую каркасом для:
Макрос OBJECT_ENTRY помещается в карте каждого СОМ-объекта данной DLL. Ему передаются два параметра. Первый из них - это GUID, уникальный идентификатор СОМ-объекта, а второй - имя реализующего его класса. На основе этих сведений макрос создает для указанного объекта соответствующую запись _ATL_OBJMAP_ENTRY.
При загрузке DLL в ответ на клиентский запрос СОМ вызывает функцию DllGetClassObject(), которая в свою очередь для вызова фабрики классов указанного объекта обращается к ССоmModule::GetClassObject() - функции базового класса, находящейся в глобальном объекте _Module. CComModule::GetClassObject() обращается к таблице в карте объекта и получает указатель на метод CreateInstance() фабрики классов. С помощью этого указателя GetClassObject() создает СОМ-объект, выполняет его функцию QueryInterface() и возвращает в СОМ указатель на интерфейс.
В серверах, находящихся в ЕХЕ-файлах, Dll-функции не реализованы; вместо этого при запуске вызывается СОМ-функция CoRegisterClassObject() для каждой реализованной в ней фабрики классов. Указатели на фабрики классов кэшируются во внутренней таблице. Функции DllRegisterServer() и DllUnregisterServer() реализуют саморегистрирующийся СОМ-объект, встраиваемый в процесс. Программы типа консольной утилиты RegSvr32.exe способны вызывать их для добавления в реестр или удаления из него сведений о СОМ-объекте. Информацию о том, какие действия нужно предпринять - добавить или удалить записи, ЕХЕ-серверы получают из параметров командной строки: RegServer или UnregServer соответственно. И ЕХЕ, и DLL в конце концов обращаются к функциям CComModule::RegisterServer() и CComModule::UnregisterServer(). Задача этих функций - регистрация или дерегистрация всех объявленных в карте объектов на основе информации из сценария реестра.
На следующем шаге мы рассмотрим сценарий реестра.