| Корм: монопольные примечания Название: Учебные примечания по «Принципам и приложениям» | Автор: FGS_FGS Комментарии |
| «Принципы и приложения" COM и примечания по исследованию - принципы части 1 COM http://savetime.delphibs.com Время начала: 2004.1.30 Последнее изменение: 2004.2.1 Формат этой статьи: Текст автоматически завершен окном; (Содержание этой статьи в основном выдерживается из книги «Принципы и приложения». Авторские права принадлежат автору Pan Aim. Пожалуйста, не используйте ее в общественных СМИ) Оглавление ============================================================================= ================= ============================================================= ========= ⊙ Обзор главы 1 Что такое ком COM объектов и интерфейсов COM Process Model COM повторный использование ⊙ ГЛАВА 2 COM Объектная модель Глобально уникальный идентификатор Com объект Com интерфейс Описание интерфейса язык IDL Интерфейс iunknown Принципы интерфейса для объектов COM ⊙ Глава 3 Реализация Com Компонентная информация о регистрации компонентов Зарегистрировать компоненты Com Функция класса и функция dllgetObjectClass Функция CogetClassObject Cocreateinstance / cocreateinstanceex Инициализация библиотеки COM Управление памятью библиотеки COM Загрузка и удаление компонентных программ Общие функции для библиотеки COM Тип HRESULT ⊙ ГЛАВА 4 COM Особенности Воспространенность: включение и агрегация Прозрачность процесса (для изучения) Безопасность (для изучения) Функции многопоточного чтения (для изучения) ⊙ Глава 5 Разработка приложений COM с визуальным C ++ Описание некоторых файлов заголовков, предоставленных Win32 SDK Некоторые макросы, связанные с интерфейсом com ============================================================================= ================= ============================================================= ========= текст ============================================================================= ================= ============================================================= ========= ⊙ Обзор главы 1 ============================================================================= ================= ============================================================= ========= Что такое ком ------------------------------------------------------ ------------------------------------------------------ ------------------------------------------------------ ---------------------------- COM - это стандарт компонента, предложенный Microsoft. В стандарте COM компонентная программа также называется модулем. Компонентная программа может содержать один или несколько объектов компонента. ) является кодовым носителем, который предоставляет COM -объекты. Объекты COM отличаются от концепции объекта в общих объектно-ориентированных языках (например, C ++). COM объекты не зависят от языка. Эта функция возможно взаимодействовать с компонентными объектами, разработанными различными языками программирования. ------------------------------------------------------ ------------------------------------------------------ ------------------------------------------------------ ---------------------------- COM объектов и интерфейсов ------------------------------------------------------ ------------------------------------------------------ ------------------------------------------------------ ---------------------------- Подобно концепции объекта в C ++, объект является экземпляром класса; Приложение, которое использует объект (или другой объект), называется клиентом, иногда также называется пользователем объекта. Интерфейс представляет собой набор логически связанных функций, а его функции также называются функциями элемента интерфейса. Согласно пользовательскому, имена интерфейса часто префикс с «I». Объекты предоставляют клиентам различные формы услуг с помощью функций членов интерфейса. В модели COM сам объект невидим для клиента, и когда клиент запрашивает обслуживание, его можно выполнить только через интерфейс. Каждый интерфейс идентифицируется 128-битным глобально уникальным идентификатором (GUID). Клиент получает указатель интерфейса через GUID, а затем передает указатель интерфейса, и клиент может вызвать соответствующую функцию члена. Подобно интерфейсам, каждый компонент также идентифицируется с помощью 128-битного GUID, называемого CLSID (идентификатор класса, идентификатор класса или идентификатор класса). Фактически, после того, как клиент успешно создает объект, он получает указатель на интерфейс объекта. Это все услуги. Согласно спецификации COM, если объект COM реализует несколько интерфейсов, любой другой интерфейс объекта может быть получен из определенного интерфейса. Из этого процесса мы также можем видеть, что клиент имеет дело с объектом COM только через интерфейсы, а объект - это просто набор интерфейсов для клиента. ------------------------------------------------------ ------------------------------------------------------ ------------------------------------------------------ ---------------------------- COM Process Model ------------------------------------------------------ ------------------------------------------------------ ------------------------------------------------------ ---------------------------- Объекты компонента сервиса, предоставленные COM, имеют две модели процесса при реализации: объекты в процессе и объекты вне процесса. Если это объект в процессе процесса, он работает в пространстве процесса клиента; Программа обслуживания в процессе: Сервисная программа загружается в процессовое пространство клиента. Местные программы обслуживания: Сервисная программа работает на той же машине, что и клиентская программа. Программа удаленного обслуживания: Сервисная программа работает на другой машине, чем клиент, и может быть либо модулем DLL, либо файлом EXE. Если программа удаленного обслуживания реализована в форме DLL, удаленная машина создает прокси -процесс. Хотя COM -объекты имеют разные модели процессов, эта разница прозрачна для клиентских программ. Однако при внедрении объектов COM вы все равно должны тщательно выбрать модель процесса. Преимущество моделей в процессе процесса состоит в том, что они эффективны, но нестабильные компоненты приведут к сбою клиента, поэтому компоненты могут подвергать опасности клиента; -Ф. Стабильность и процесс компонента не будут подвергаться опасности клиентской программы. ------------------------------------------------------ ------------------------------------------------------ ------------------------------------------------------ ---------------------------- COM повторный использование ------------------------------------------------------ ------------------------------------------------------ ------------------------------------------------------ ---------------------------- Поскольку стандарт COM основан на уровне двоичного кода, способность повторного использования COM-объектов отличается от процесса повторного использования объектов на общих объектно-ориентированных языках, таких как C ++. Для клиентской программы COM он использует услуги, предоставляемые объектом через интерфейс, и не знает процесса реализации внутри объекта. Вместо конкретной реализации. COM использует два механизма для реализации повторного использования объектов. Мы предполагаем, что есть два объекта COM, и объект 1 надеется повторно использовать функцию объекта 2. Мы называем объект 1 внешним объектом, а объект 2 - внутренний объект. (1) инклюзивный способ. Объект 1 содержит объект 2. Когда объект 1 необходимо использовать функцию объекта 2, он может просто передавать реализацию объекту 2. Хотя объект 1 и объект 2 поддерживают один и тот же интерфейс, объект 1 при реализации интерфейса , реализация объекта 2 называется. (2) Метод агрегации. Объект 1 просто подчиняет интерфейс объекта 2 клиенту. существовать. ============================================================================= ================= ============================================================= ========= ⊙ ГЛАВА 2 COM Объектная модель ============================================================================= ================= ============================================================= ========= Глобально уникальный идентификатор ------------------------------------------------------ ------------------------------------------------------ ------------------------------------------------------ ---------------------------- Спецификация COM использует 128-разрядную глобально уникальную идентификатор для определения объектов и интерфейсов, которые являются случайными числами и не требуют специализированных агентств для распределения и управления. Поскольку GUID является случайным числом, уникальность не является абсолютной гарантированной, но возможность удвоения идентификаторов очень мала. Теоретически, если машина генерирует 100 000 000 ГИД в секунду, можно гарантировать, что 3240 лет (в смысле вероятности) не повторяется. GUID можно описать в C/C ++ с использованием этой структуры: typedef struct _guid { DWORD DATA1; Word Data2; Word Data3; Byte Data4 [8]; } Guid; Пример: {64BF4372-1007-B0AA-444553540000} Вы можете определить GUID следующим образом: extern "c" const guid clsid_myspellchecker = {0x54bf0093, 0x1048, 0x399d, {0xb0, 0xa3, 0x45, 0x33, 0x43, 0x90, 0x47, 0x47}}; Visual C ++ предоставляет две программы для генерации GUID: uuidgen.exe (командная строка) и guidgen.exe (диалог). Библиотека COM предоставляет следующие функции API, которые могут генерировать GUID: HRESULT COCREATEGUID (GUID *PGUID); Если GUID создается успешно, функция возвращает S_OK, а PGUID указывает на полученное значение GUID. ------------------------------------------------------ ------------------------------------------------------ ------------------------------------------------------ ---------------------------- Com объект ------------------------------------------------------ ------------------------------------------------------ ------------------------------------------------------ ---------------------------- В спецификации COM объекты COM не определены строго, но COM предоставляет объектно-ориентированную модель компонентов, а компоненты COM предоставляют клиентам объекты, инкапсулированные в форме объекта. Сущность, в которой клиентская программа взаимодействует с программой COM, является объектом COM. ------------------------------------------------------ ------------------------------------------------------ ------------------------------------------------------ ---------------------------- Com интерфейс ------------------------------------------------------ ------------------------------------------------------ ------------------------------------------------------ ---------------------------- Технически говоря, интерфейс - это структура данных, которая содержит набор функций, с помощью которого клиент -код может вызвать функции компонентных объектов. Интерфейс определяет набор функций -членов, которые представляют собой информацию, выявленную объектами компонентов. Обычно мы называем таблицу виртуальной функции функции интерфейса (vtable), а указатель на Vtable - Pvtable. Для интерфейса определяется его таблица виртуальной функции, поэтому количество функций элемента интерфейса не изменилось, а порядок функций также не изменился; В определении интерфейса вся эта информация должна быть определена на двоичном уровне. Указатель интерфейса ----> Pvtable ----> Функция указателя 1-> | ----------- | M_DATA1 Функция указателя 2 -> | M_DATA2 Функция указателя 3-> | ------------ | Первый параметр каждой функции элемента интерфейса - это указатель на экземпляр объекта (= это). И информация, когда вы называете, интерфейс может знать, какой объект COM работает. В функциях элементов интерфейса строковые переменные должны использовать указатели символов Unicode. Следовательно, если символы ANSI используются в программе компонентов, следует преобразовать два выражения символов. Конечно, в случае создания компонентных программ и клиентских программ вы можете использовать типы параметров, которые вы определяете себя, если они совместимы с типами параметров, которые COM может распознавать. Visual C ++ обеспечивает два преобразования строки: namespace _com_util { BSTRTSTRINGTOBSTR (const char *psrc) Throw (_com_error); BSTR ConvertBStrtoString (BSTR PSRC) Throw (_COM_ERROR); } BSTR-это строка с двойной шириной, которая является наиболее часто используемым автоматизированным типом данных. ------------------------------------------------------ ------------------------------------------------------ ------------------------------------------------------ ---------------------------- Описание интерфейса язык IDL ------------------------------------------------------ ------------------------------------------------------ ------------------------------------------------------ ---------------------------- Спецификация COM использует спецификацию DCE OSF для описания IDL интерфейса удаленного вызова (язык описания интерфейса) и расширяется для формирования языка описания интерфейса COM. Описание интерфейса Язык обеспечивает метод описания для интерфейсов, которые не зависят от любого языка, поэтому он может стать общим языком между компонентными программами и клиентскими программами. Язык описания интерфейса IDL, используемый спецификацией COM, может использоваться не только для определения интерфейса COM, но и определить некоторые обычно используемые типы данных и пользовательские структуры данных. .. IDL поддерживает типы указателей, которые очень похожи на C/C ++. Например: Интерфейс ИДИЦИЯ { HRESULT Инициализировать () HRESULT LOADLIBRARY ([in] String); HRESULT INSERTWORD ([in] String, [in] String); HRESULT DELETEWORD ([in] String); HRESULT LOUSTUPWORD ([in] String, [out] String *); HRESULT RESTORELIBRARY ([in] String); HRESULT FREELIBRARY (); } Microsoft Visual C ++ предоставляет инструменты MIDL, которые могут компилировать файлы интерфейса IDL в C/C ++-совместимый интерфейс Описание Файлы заголовков (.h). ------------------------------------------------------ ------------------------------------------------------ ------------------------------------------------------ ---------------------------- Интерфейс iunknown ------------------------------------------------------ ------------------------------------------------------ ------------------------------------------------------ ---------------------------- Определение IDL IUNKNOWN: Интерфейс iunknown { HResult QueryInterface ([in] refiid iid, [out] void ** ppv); Ulong addref (void); Выпуск Ulong (void); } Определение C ++ Iunkown: класс iunknown { virus hresult _stdcall QueryInterface (const iid & iid, void ** ppv) = 0; Virtual Ulong _stdcall addRef () = 0; virus ulong _stdcall Release () = 0; } ------------------------------------------------------ ------------------------------------------------------ ------------------------------------------------------ ---------------------------- Принципы интерфейса для объектов COM ------------------------------------------------------ ------------------------------------------------------ ------------------------------------------------------ ---------------------------- Спецификация COM устанавливает следующие правила для функции QueryInterface: 1. Для различных указателей интерфейса одного и того же объекта интерфейс iunknown, полученный с помощью запроса, должен быть точно таким же. То есть указатель интерфейса iunknown для каждого объекта уникален. Следовательно, для двух указателей интерфейса мы можем определить, указывают ли они на один и тот же объект, оценив, равны ли интерфейсы iunknown, которые они запрашивают. 2. Рефлексивность интерфейса. Запрос сам интерфейс должен всегда добиваться успеха, например: Pidictionary-> QueryInterface (iid_dictionary, ...) должен вернуть s_ok. 3. Интерфейс симметрия. Если вы запросите по одному указателю интерфейса на другой указатель интерфейса, то возвращение со второго указателя интерфейса на первый указатель интерфейса должен быть успешным, например: Pidictionary-> QueryInterface (iid_spellcheck, (void **) & pispellcheck); Если поиск успешно, то проверка интерфейса IID_DICTIONARY от PispellCheck определенно будет успешной. 4. Интерфейс транзит. Если вы запросите второй указатель интерфейса из первого указателя интерфейса, а третий указатель интерфейса может быть запрошен из второго указателя интерфейса, то вы определенно можете запросить первый указатель интерфейса из третьего указателя интерфейса. 5. Время интерфейса не имеет значения. Если определенный указатель интерфейса может быть найден в определенный момент, то тот же указатель интерфейса будет проверен в любое время в будущем, и запрос определенно будет успешным. Короче говоря, независимо от того, с какого интерфейса мы начинаем, мы всегда можем достичь любого интерфейса, и мы всегда можем вернуться к исходному интерфейсу. ============================================================================= ================= ============================================================= ========= ⊙ Глава 3 Реализация Com ============================================================================= ================= ============================================================= ========= Компонентная информация о регистрации компонентов ------------------------------------------------------ ------------------------------------------------------ ------------------------------------------------------ ---------------------------- Информация обо всех компонентах на текущей машине HKEY_CLASS_ROOT/CLSID Компонент в процессе HKEY_CLASS_ROOT/CLSID/GUID/InProcserver32 Компонент вне процесса HKEY_CLASS_ROOT/CLSID/GUID/LOCALSERVER32 Категория, в которую компонент (Catid) HKEY_CLASS_ROOT/CLSID/GUID/Реализованные категории COM Информация о конфигурации интерфейса HKEY_CLASS_ROOT/Интерфейс Proxy dll/stub dll hkey_class_root/clsid/guid/proxystubclsid HKEY_CLASS_ROOT/CLSID/GUID/PROXYSTUBCLSID32 Информация о библиотеке типа hkey_class_root/typelib Наименование строки Progid hkey_class_root/ (например, "comctl.treectrl") Компонент wind hkey_class_root/comtrl.treecontrol/clsid Номер версии по умолчанию hkey_class_root/comtrl.treecontrol/curver (например, curver = "comtrl.treectrl.1", затем Hkey_class_root/comtrl.treecontrol.1 также существует) Все категории компонентов текущей машины HKEY_CLASS_ROOT/Component COM предоставляет две функции API ClSIDFROGID и PROGIDFROMCLSID для преобразования PROGID и CLSID. Если компонент COM поддерживает один и тот же набор интерфейсов, их можно классифицировать в один и тот же класс, а компонент может быть классифицирован на несколько классов. Например, если все объекты автоматизации поддерживают интерфейс idispatch, их можно классифицировать как «объекты автоматизации». Информация о категории также описана GID, называемой Catid. Основная цель категорий компонентов заключается в том, что клиенты могут быстро обнаружить конкретные типы объектов компонентов на машине. Интерфейс, теперь использующий категории компонентов, может сохранить процесс запроса. ------------------------------------------------------ ------------------------------------------------------ ------------------------------------------------------ ---------------------------- Зарегистрировать компоненты Com ------------------------------------------------------ ------------------------------------------------------ ------------------------------------------------------ ---------------------------- REGSRV32.exe используется для регистрации компонента в процессе, который вызывает функции DLLRegerServer и DllunRegerServer DLL для завершения регистрации и отмены программы компонентов. Если операция успешна, верните True, в противном случае верните False. Для программ, находящихся за пределами процесса компонентов, ситуация немного отличается, потому что она является самой исполняемой программой, и она не может предоставить функции входа для других программ для использования. Следовательно, в спецификации COM предусматривается, что компоненты вне процесса, которые поддерживают саморегистрацию, должны поддерживать два параметра командной строки /regserver и /unrecServer для завершения операций регистрации и отмены. Параметры командной строки зависят от случая, а «/» можно заменить на «-». Если операция успешна, программа возвращает 0, в противном случае, возвращение не 0 означает отказ. ------------------------------------------------------ ------------------------------------------------------ ------------------------------------------------------ ---------------------------- Функция класса и функция dllgetObjectClass ------------------------------------------------------ ------------------------------------------------------ ------------------------------------------------------ ---------------------------- Фабрика класса является производственной базой COM -объектов. Сама фабрика класса также является объектом COM, который поддерживает специальный интерфейс iclassfactory: класс iClassFactory: Public Iunknown { виртуальная hresult _stdcall createinstance (iunknown *punknownouter, const iid & iid, void ** ppv) = 0; Virtual HResult _stdcall Lockserver (Bool Block) = 0; } Функция члена CreateInstance используется для создания соответствующего объекта COM. Первый параметр Punknownouter используется для случая, когда класс объекта агрегирован, и обычно устанавливается на NULL; Указатель интерфейса. Функции членов LockServer используются для управления временем срока службы компонента. Заводский объект класса создается функцией, полученной из DLL DllgetClassObject: Hresult dllgetClassObject (const clsid & clsid, const iid & iid, (void **) ppv); Первым параметром функции dllgetClassObject является CLSID объекта, который будет создан. Поскольку компонент может реализовать несколько классов объектов COM, необходимо указать CLSID в параметрах функции dllgetClassObject для создания правильной фабрики класса. Два других параметра IID и PPV относятся к указанному интерфейсу IID и указателю интерфейса класса класса хранилища соответственно. После получения инструкции для создания объекта библиотека COM вызывает функцию DllgetClassObject компонента в процессе, создает объект Factory Class из функции и возвращает интерфейс -указатель заводского объекта класса. Как только библиотека COM или клиент получит указатель интерфейса на фабрику класса, они могут создать соответствующий объект COM через функцию члена CreateInstance IclassFactory. ------------------------------------------------------ ------------------------------------------------------ ------------------------------------------------------ ---------------------------- Функция CogetClassObject ------------------------------------------------------ ------------------------------------------------------ ------------------------------------------------------ ---------------------------- В библиотеке COM есть три API, которые можно использовать для создания объектов, а именно CogetClassObject, CocreateInstnace и CocreateInstanceex. Как правило, клиентская программа вызывает одну из них, чтобы завершить создание объекта и возвращает начальный указатель интерфейса объекта. Библиотека COM и фабрика класса также взаимодействуют через эти три функции. Hresult cogetclassObject (const clsid & clsid, dword dwclscontext, Coserverinfo *pserverinfo, const iid & iid, (void **) ppv); Функция CogetClassObject сначала находит фабрику класса класса COM, указанную CLSID, а затем подключается к объекту завода класса. Если это объект компонента в процессе процесса, CogetClassObject вызывает DllgetClassObject модуля DLL, чтобы ввести функцию, передавать параметры CLSID, IID и PPV функции dllgetClassObject и возвращает указатель интерфейса заводского объекта класса. Обычно IID является идентификатором IID_ICLASSFACTORY ICLASSFACTORY. Если объект Class Factory также поддерживает другие интерфейсы, которые можно использовать для создания операций, также могут использоваться другие идентификаторы интерфейса. Например, интерфейс iClassFactory2 может быть запрошен для проверки статуса лицензии пользователя во время создания. Интерфейс iClassFactory2 - это расширение для iClassFactory, которое повышает безопасность создания компонентов. Параметр dwclscontext определяет категорию компонентов, которая может быть указана в виде компонента в процессе, компонент вне процесса или объект управления в процессе (аналогично прокси-объекту из непроцессного компонента, в основном используемый в технологии OLE). Параметры IID и PPV соответствуют параметрам DllgetClassObject, соответственно, и используются для указания интерфейса IID и указателя интерфейса для хранения объекта класса. Параметр PServerInfo используется для указания информации о сервере при создании удаленных объектов. Ситуация гораздо сложнее, если объект класса завода, созданный функцией CogetClassObject, расположен в нестандартном компоненте. Во -первых, функция CogetClassObject запускает процесс компонента, а затем ожидает, пока процесс компонента не зарегистрирует фабрику класса объекта класса COM, который он поддерживает для com. Таким образом, функция CogetClassObject возвращает соответствующую заводскую информацию класса в com. Следовательно, когда библиотека COM запускается внекомпонентный процесс (с параметром командной строки «/Encedding»), он должен зарегистрировать поддерживаемые заводские объекты класса COM в COM через функцию CoreGisterClassObject, чтобы библиотека COM могла Создайте COM объектов для использования. Когда процесс выходит, функция CoreVokeClassObject должна быть вызвана для уведомления COM о том, что зарегистрированный объект класса, который он больше не является действительным. Компонентная программа вызывает функцию CoreGisterClassObject, и функция CoreVokeClassObject должна быть в паре, чтобы обеспечить согласованность информации COM. ------------------------------------------------------ ------------------------------------------------------ ------------------------------------------------------ ---------------------------- Cocreateinstance / cocreateinstanceex ------------------------------------------------------ ------------------------------------------------------ ------------------------------------------------------ ---------------------------- Hresult cocreateinstance (const clsid & clsid, iunknown *punknownouter, Dword dwclscontext, const iid & iid, (void **) ppv); CocreateInstance - это обернутая вспомогательная функция, которая на самом деле вызывает функцию CogetClassObject внутри. Значение параметров CLSID и DWCLSContext of CocreateInstance согласуется с соответствующими параметрами CogetClassObject (параметры IID и PPV CocreateInstance отличаются от CogetClassObject, один - информация о интерфейсе, представляющая объект, а другой - это интерфейс, представляющая класс, представляющая интерфейс, представляющая интерфейс информацию, представляющую класс, и другие - это интерфейс, представляющая интерфейс, представляющая интерфейс. фабрика). Параметр Punknownouter согласуется с соответствующими параметрами в создании интерфейса класса завода и в основном используется в случае, когда объекты агрегируются. Функция CocreateInstance инкапсулирует процесс создания объектов через фабрику класса. CocreateInstance может быть реализован с помощью следующего кода: (Savetime Примечание: применение указателя PPV в следующем коде, кажется, void **) Hresult cocreateinstance (const clsid & clsid, iunknown *punknownouter, Dword dwclscontext, const iid & iid, void *ppv) { IClassFactory *PCF; HRESULT HR; hr = cogetclassObject (clsid, dwclscontext, null, iid_iclassfactory, (void *) pcf); if (не удалось (HR) return HR; hr = pcf-> createinstance (punknownouter, iid, (void *) ppv); pfc-> release (); вернуть HR; } Из этого кода мы видим, что функция CocreateInstance сначала использует функцию CogetClassObject для создания объекта класса, а затем использует интерфейсную указатель объекта Factory для создания реального объекта COM. возвращен, так что класс используется. Тем не менее, использование CocreateInstance не создает объект на удаленной машине, поскольку при вызове CogetClassObject третий параметр, используемый для указания информации о сервере, устанавливается на NULL. Если вы хотите создать удаленный объект, вы можете использовать функцию расширения CocreateInstance CocreateInstanceex: Hresult cocreateinstanceex (const clsid & clsid, iunknown *punknownouter, Dword dwclscontext, coserverinfo *pserverinfo, dword dwcount, Multi_qi *rgmultiqi); Первые три параметра такие же, как и CocreateInstance. Указатели интерфейса. ------------------------------------------------------ ------------------------------------------------------ ------------------------------------------------------ ---------------------------- Инициализация библиотеки COM ------------------------------------------------------ ------------------------------------------------------ ------------------------------------------------------ ---------------------------- Перед вызовом функций библиотеки COM, чтобы сделать функцию допустимой, функция инициализации библиотеки COM должна быть вызвана: Hresult coinitialize (imalloc *pmalloc); PMALLOC используется для указания распределения памяти, и принцип распределения памяти может быть указан при приложении. Как правило, если мы напрямую установим параметр в NULL, библиотека COM будет использовать предоставленный распределитель памяти по умолчанию. Возвратное значение: S_OK означает, что инициализация успешна S_FALSE указывает, что инициализация является успешной, но этот вызов не первый раз, когда в этом процессе вызывается функция инициализации. S_UNEXPECTED указывает, что в процессе инициализации произошла ошибка, и приложение не может использовать библиотеку COM. Обычно процесс инициализирует библиотеку COM только один раз, и не имеет смысла инициализировать библиотеку COM несколько раз в одном и том же модуле. Единственная функция, которой не нужно инициализировать библиотеку COM, - это получить версию библиотеки COM: DWORD COBUILDVERSION (); Возвращаемое значение: высокий 16-битный номер основной версии Более низкий 16 -значный номер версии После того, как программа COM использует сервис библиотеки COM, обычно до выхода программы, необходимо вызвать завершенную функцию библиотечной службы COM, чтобы освободить ресурсы, поддерживаемые библиотекой COM: void countinialize (void); ПРИМЕЧАНИЕ. Любой процесс или программный модуль, который вызывает функцию Coinitialize и возвращает S_OK, должен иметь соответствующий вызов функции, чтобы гарантировать, что библиотека COM эффективно использует ресурсы. (? Если Coinitialize вызывается в модуле и возвращает S_OK, будут ли другие модули, которые также используют библиотеку COM, есть ошибки после вызова функции Gountialize? Или библиотека COM автоматически проверяет, какие модули используются?) ------------------------------------------------------ ------------------------------------------------------ ------------------------------------------------------ ---------------------------- Управление памятью библиотеки COM ------------------------------------------------------ ------------------------------------------------------ ------------------------------------------------------ ---------------------------- Поскольку программы компонентов COM и клиентские программы подключены через стандарты двоичного уровня, все операции, включающие взаимодействие памяти между клиентами, библиотеки COM и компоненты (распределение и выпуск не в одном и том же модуле) в приложениях COM должны использоваться последовательный диспетчер памяти. Стандарт управления памятью, предоставленный COM, на самом деле является интерфейсом iMalloc: // iid_imalloc: {00000002-0000-0000-c000-000000000046} Класс IMALLOC: Public Iunknown { void * alloc (ulong cb) = 0; void * realloc (void * pv, ulong cb) = 0; пустота свободна (void *pv) = 0; Ulong Getsize (void *pv) = 0; // Возвращает выделенный размер памяти int didalloc (void *pv) = 0; void heapminimize () = 0; // Операционная система для оптимизации производительности } Получите указатель интерфейса imalloc: HRESULT COGETMALLOC (DWORD DWMEMCONTEXT, IMALLOC ** PPMALLOC); Первый параметр функции Cogetmalloc DwmemContext используется для указания типа менеджера памяти. Библиотека COM содержит два менеджера по памяти, один из них - диспетчер памяти, указанный во время инициализации, или ее внутренний менеджер по умолчанию, также известный как менеджер заданий (Allocator). MEMCTX_TASK в параметре DWMEMCONTEXT; процесс и перенесен во второй процесс, используя эту память во втором процессе или даже освобождая ее. Пока возвращаемое значение функции является S_OK, PPMalloc указывает на указатель интерфейса Memory Manager библиотеки COM, и вы можете использовать его для выполнения операций памяти. Библиотека COM инкапсулирует три функции API, которые можно использовать для распределения и выпуска памяти: void * cotaskmemalc (ulong cb); void cotaskfree (void *pv); void cotaskmemrealloc (void *pv, ulong cb); Эти три функции присваиваются трем функциям элемента, соответствующим iMalloc: Alloc, Realloc и Free. Пример: как программа COM находит соответствующее значение PROGID из значения CLSID: Wchar *pwprogid; char pszprogid [128]; hresult = :: progidfromclsid (clsid_dictionary, & pwprogid); if (hresult! = s_ok) { ... } wcstombs (pszprogid, pwprogid, 128); CotaskMemfree (PWProgid); После вызова функции COM PROGIDFROMCLSID возвращается, поскольку библиотека COM выделяет пространство памяти для выходной переменной PWProgID, приложение должно вызвать функцию CotaskMemfree, чтобы освободить память после использования переменной PWProgid. Этот пример иллюстрирует ситуацию, когда память выделяется в библиотеке COM, а память освобождается в вызовной программе. Некоторые другие функции в библиотеке COM также имеют сходные характеристики, особенно некоторые функции, которые содержат выходные параметры неопределенной длины. ------------------------------------------------------ ------------------------------------------------------ ------------------------------------------------------ ---------------------------- Загрузка и удаление компонентных программ ------------------------------------------------------ ------------------------------------------------------ ------------------------------------------------------ ---------------------------- Загрузка компонентов в процессе: Клиентская программа вызывает функцию COCREATEINSTANCE или COGETCLASSOBJECT библиотеки COM, чтобы создать объект COM. the system registry, and then calls LoadLibrary( It is actually a CoLoadLibrary function, and calls the DllGetClassObject of the component program to introduce the function. DllGetClassObject 函数创建相应的类厂对象,并返回类厂对象的IClassFactory 接口。至此CoGetClassObject 函数的任务完成,然后客户程序或者CoCreateInstance 函数继续调用类厂对象的CreateInstance 成员函数,由它负责COM 对象的创建工作。 CoCreateInstance |-CoGetClassObject |-Get CLSID -> DLLfile path |-CoLoadLibrary |-DLLfile.DllGetClassObject |-return IClassFactory |-IClassFactory.CreateInstnace 进程外组件的装载: 在COM 库的CoGetClassObject 函数中,当它发现组件程序是EXE 文件(由注册表组件对象信息中的LocalServer 或LocalServer32 值指定)时,COM 库创建一个进程启动组件程序,并带上“/Embedding”命令行参数,然后等待组件程序;而组件程序在启动后,当它检查到“/Embedding”命令行参数后,就会创建类厂对象,然后调用CoRegisterClassObject 函数把类厂对象注册到COM 中。当COM 库检查到组件对象的类厂之后,CoGetClassObject 函数就把类厂对象返回。由于类厂与客户程序运行在不同的进程中,所以客户程序得到的是类厂的代理对象。一旦客户程序或COM 库得到了类厂对象,它就可以完成组件对象的创建工作。 进程内对象和进程外对象的不同创建过程仅仅影响了CoGetClassObject 函数的实现过程,对于客户程序来说是完全透明的。 CoGetClassObject |-LocalServer/LocalServer32 |-Execute EXE /Embedding |-Create class factory |-CoRegisterClassObject ( class factory ) |-return class factory (proxy) 进程内组件的卸载: 只有当组件程序满足了两个条件时,它才能被卸载,这两个条件是:组件中对象数为0,类厂的锁计数为0。满足这两个条件时,DllCanUnloadNow 引出函数返回TRUE。COM 提供了一个函数CoFreeUnusedLibraries,它会检测当前进程中的所有组件程序,当发现某个组件程序的DllCanUnloadNow 函数返回TRUE 时,就调用FreeLibrary 函数(实际上是CoFreeLibrary 函数)把该组件从程序从内存中卸出。 该由谁来调用CoFreeUnusedLibraries 函数呢?因为在组件程序执行过程中,它不可能把自己从内存中卸出,所以这个任务应该由客户来完成。客户程序随时都可以调用CoFreeUnusedLibraries 函数完成卸出工作,但通常的做法是,在程序的空闲处理过程中调用CoFreeUnusedLibraries 函数,这样做既可以避免程序中处处考虑对CoFreeUnusedLibraries 函数的调用,又可以使不再使用的组件程序得到及时清除,提高资源的利用率,COM 规范也推荐这种做法。 进程外组件的卸载: 进程外组件的卸载比较简单,因为组件程序运行在单独的进程中,一旦其退出的条件满足,它只要从进程的主控函数返回即可。在Windows 系统中,进程的主控函数为WinMain。 前面曾经说过,在组件程序启动运行时,它调用CoRegisterClassObject 函数,把类厂对象注册到COM 中,注册之后,类厂对象的引用计数始终大于0,因此单凭类厂对象的引用计数无法控制进程的生存期,这也是引入类厂对象的加锁和减锁操作的原因。进程外组件的载条件与DllCanUnloadNow 中的判断类似,也需要判断COM 对象是否还存在、以及判断是否锁计数器为0,只有当条件满足了,进程的主函数才可以退出。 从原则上讲,进程外组件程序的卸载就是这么简单,但实际上情况可能复杂一些,因为有些组件程序在运行过程中可以创建自己的对象,或者包含用户界面的程序在运行过程中,用户手工关闭了进程,那么进程对这些动作的处理要复杂一些。例如,组件程序在运行过程中,用户又打开了一个文件并进行操作,那么即使原先创建的对象被释放了,而且锁计数器也为0,进程也不能退出,它必须继续为用户服务,就像是用户打开的进程一样。对这种程序,可以增加一个“用户控制”标记flag,如果flag 为FALSE,则可以按简单的方法直接退出程序即可;如果flag 为TRUE,则表明用户参与了控制,组件进程不能马上退出,但应该调用CoRevokeClassObject 函数以便与CoRegisterClassObject 调用相响呼应,把进程留给用户继续进行。 如果组件程序在运行过程中,用户要关闭进程,而此时并不满足进程退出条件,那么进程可以采取两种办法:第一种方法,把应用隐藏起来,并把flag 标记设置为FALSE,然后组件程序继续运行直到卸载条件满足为止;另一种办法是,调用CoDisconnectObject 函数,强迫脱离对象与客户之间的关系,并强行终止进程,这种方法比较粗暴,不提倡采用,但不得已时可以也使用,以保证系统完成一些高优先级的操作。 ------------------------------------------------------ ------------------------------------------------------ ------------------------------------------------------ ---------------------------- COM 库常用函数 ------------------------------------------------------ ------------------------------------------------------ ------------------------------------------------------ ---------------------------- 初始化函数CoBuildVersion 获得COM 库的版本号 CoInitialize COM 库初始化 CoUninitialize COM 库功能服务终止 CoFreeUnusedLibraries 释放进程中所有不再使用的组件程序 GUID 相关函数IsEqualGUID 判断两个GUID 是否相等 IsEqualIID 判断两个IID 是否相等 IsEqualCLSID 判断两个CLSID 是否相等(*为什么要3个函数) CLSIDFromProgID 字符串组件标识转换为CLSID 形式 StringFromCLSID CLSID 形式标识转化为字符串形式 IIDFromString 字符串转换为IID 形式 StringFromIID IID 形式转换为字符串 StringFromGUID2 GUID 形式转换为字符串(*为什么有2) 对象创建函数CoGetClassObject 获取类厂对象 CoCreateInstance 创建COM 对象 CoCreateInstanceEx 创建COM 对象,可指定多个接口或远程对象 CoRegisterClassObject 登记一个对象,使其它应用程序可以连接到它 CoRevokeClassObject 取消对象的登记 CoDisconnectObject 断开其它应用与对象的连接 内存管理函数CoTaskMemAlloc 内存分配函数 CoTaskMemRealloc 内存重新分配函数 CoTaskMemFree 内存释放函数 CoGetMalloc 获取COM 库内存管理器接口 ------------------------------------------------------ ------------------------------------------------------ ------------------------------------------------------ ---------------------------- HRESULT 类型 ------------------------------------------------------ ------------------------------------------------------ ------------------------------------------------------ ---------------------------- 大多数COM 函数以及一些接口成员函数的返回值类型均为HRESULT 类型。HRESULT 类型的返回值反映了函数中的一些情况,其类型定义规范如下: 31 30 29 28 16 15 0 |-----|--|------------------------|-----------------------------------| 类别码(30-31) 反映函数调用结果: 00 调用成功 01 包含一些信息 10 警告 11 错误 自定义标记(29) 反映结果是否为自定义标识,1 为是,0 则不是; 操作码(16-28) 标识结果操作来源,在Windows 平台上,其定义如下: #define FACILITY_WINDOWS 8 #define FACILITY_STORAGE 3 #define FACILITY_RPC 1 #define FACILITY_SSPI 9 #define FACILITY_WIN32 7 #define FACILITY_CONTROL 10 #define FACILITY_NULL 0 #define FACILITY_INTERNET 12 #define FACILITY_ITF 4 #define FACILITY_DISPATCH 2 #define FACILITY_CERT 11 操作结果码(0-15) 反映操作的状态,WinError.h 定义了Win32 函数所有可能返回结果。 以下是一些经常用到的返回值和宏定义: S_OK 函数执行成功,其值为0 (注意,其值与TRUE 相反) S_FALSE 函数执行成功,其值为1 S_FAIL 函数执行失败,失败原因不确定 E_OUTOFMEMORY 函数执行失败,失败原因为内存分配不成功 E_NOTIMPL 函数执行失败,成员函数没有被实现 E_NOTINTERFACE 函数执行失败,组件没有实现指定的接口 不能简单地把返回值与S_OK 和S_FALSE 比较,而要用SECCEEDED 和FAILED 宏进行判断。 ============================================================================= ================= ============================================================= ========= ⊙ 第四章COM 特性 ============================================================================= ================= ============================================================= ========= 可重用性:包容和聚合 ------------------------------------------------------ ------------------------------------------------------ ------------------------------------------------------ ---------------------------- 包容模型: 组件对象在接口的实现代码中执行自身创建的另一个组件对象的接口函数(客户/服务器模型)。这个对象同时实现了两个(或更多)接口的代码。 聚合模型: 组件对象在接口的查询代码中把接口传递给自已创建的另一个对象的接口查询函数,而不实现该接口的代码。另一个对象必须实现聚合模型(也就是说,它知道自己正在被另一个组件对象聚合),以便QueryInterface 函数能够正常运作。 在组件对象被聚合的情况下,当客户请求它所不支持的接口或者请求IUnknown 接口时,它必须把控制交给外部对象,由外部对象决定客户程序的请求结果。 聚合模型体现了组件软件真正意义上的重用。 聚合模型实现的关键在CoCreateInstance 函数和IClassFactory 接口: HRESULT CoCreateInstance(const CLSID& clsid, IUnknown *pUnknownOuter, DWORD dwClsContext, const IID& iid, (void **)ppv); // class IClassFactory : public IUnknown virtual HRESULT _stdcall CreateInstance(IUnknown *pUnknownOuter, const IID& iid, void **ppv) = 0; 其中pUnknownOuter 参数用于指定组件对象是否被聚合。如果pUnknownOuter 参数为NULL,说明组件对象正常使用,否则说明被聚合使用,pUnknownOuter 是外部组件对象的接口指针。 聚合模型下的被聚合对象的引用计数成员函数也要进行特别处理。在未被聚合的情况下,可以使用一般的引用计数方法。在被聚合时,由客户调用AddRef/Release 函数时,必须转向外部组件对象的AddRef/Release 方法。这时,外部组件对象要控制被聚合的对象必须采用其它的引用计数接口。 ------------------------------------------------------ ------------------------------------------------------ ------------------------------------------------------ ---------------------------- 进程透明性(待学) 安全性(待学) 多线程特性(待学) ------------------------------------------------------ ------------------------------------------------------ ------------------------------------------------------ ---------------------------- ============================================================================= ================= ============================================================= ========= ⊙ 第五章用Visual C++ 开发COM 应用 ============================================================================= ================= ============================================================= ========= Win32 SDK 提供的一些头文件的说明 ------------------------------------------------------ ------------------------------------------------------ ------------------------------------------------------ ---------------------------- Unknwn.h 标准接口IUnknown 和IClassFacatory 的IID 及接口成员函数的定义 Wtypes.h 包含COM 使用的数据结构的说明 Objidl.h 所有标准接口的定义,即可用于C 语言风格的定义,也可用于C++ 语言 Comdef.h 所有标准接口以及COM 和OLE 内部对象的CLSID ObjBase.h 所有的COM API 函数的说明 Ole2.h 所有经过封装的OLE 辅助函数 ------------------------------------------------------ ------------------------------------------------------ ------------------------------------------------------ ---------------------------- 与COM 接口有关的一些宏 ------------------------------------------------------ ------------------------------------------------------ ------------------------------------------------------ ---------------------------- DECLARE_INTERFACE(iface) 声明接口iface,它不从其他的接口派生 DECLARE_INTERFACE_(iface, baseiface) 声明接口iface,它从接口baseiface 派生 STDMETHOD(method) 声明接口成员函数method,函数返回类型为HRESULT STDMETHOD_(type, method) 声明接口成员函数method,函数返回类型为type ============================================================================= ================= ============================================================= ========= ⊙ 结束 ============================================================================= ================= ============================================================= ========= |