developing.ru - клуб программистов Здесь может быть и ваша реклама.
developing.ru >технология COM >
Михаил Безверхов
aspid@developing.ru

У кого и как добиться получения COM-объекта

Для завершения подробной иллюстрации как COM-объекты находят друг-друга при помощи операционной системы нам осталось описать всего два пункта - системную функцию, осуществляющую разыскание, запуск и предоставление адреса объекта запросившему его клиенту и то, как сам COM-сервер обрабатывает запросы от этой функции.

Возможно, что на данном этапе это пока и не слишком интересно, но я хочу повторить свои слова - точное знание философии взаимодействия объектов, знание средств и инструментов, используемых на разных этапах этого процесса неизбежно. В COM нет возможности "перекомпилировать всё" или запустить отладчик и наблюдать любую часть вашей программы - часть программного комплекса будут составлять "чужие" объекты, с которыми вы будете обращаться так же, как и со своими. А выяснить "что у них внутри" и что-то "подправить" вы не сможете, вам останется полагаться только на точное соблюдение протокола взаимодействия...

Мы начнём с последнего оставшегося пункта - как взаимодействуют операционная система и COM-сервер, когда клиент запросил адрес объекта. Поскольку наше изложение - только иллюстрация важнейших аспектов, мы будем рассматривать лишь самый простой случай - взаимодействие системы и DLL-сервера.

Для этого нам понадобится знание некоторых подробностей "DLLестроения". Ранее говорилось, что операционная система неким стандартным образом обращается к модулю, именуемому COM-сервер, и побуждает его выдать ссылку на COM-объект. Вопрос, на который хочется получить ответ - как?

Если обратиться к техническим подробностям, то вы, вероятно, слышали, что DLL - не просто кусок кода. Внутри DLL существуют специальные таблицы, которые описывают функции, "внешние" с точки зрения границы этой DLL. Первая таблица называется IMPORTS - это функции, которые требуются DLL, но которые в самой DLL не реализованы. Вторая таблица называется EXPORTS, она описывает функции, которые реализованы в DLL и предлагаются всем желающим.

Эти таблицы - важная часть DLL, поскольку системный загрузчик, производящий "позднее связывание", использует их для правильной настройки адресов вызывающей и вызываемой процедур. Другими словами - все функции, которые можно вызвать находясь снаружи DLL, перечислены в её таблице EXPORTS. Поэтому, если система как-то обращается к DLL, то она может это сделать только "по правилам" - иного способа вызвать какую-то функцию из DLL нет. Следовательно, присутствие этой функции должно обнаруживаться инструментально.

В поставке Visual Studio существует очень полезный инструмент исследования двоичных файлов - программа dumpbin.exe. Она располагается в каталоге "...\Program Files\Microsoft Visual Studio\VC98\Bin" и управляется интерфейсом командной строки. Назначение этой программы - выдавать содержимое таблиц, содержащихся в двоичных файлах в человекочитаемом виде. Используем её в применении к заведомому COM-серверу, обнаруженному нами в прошлой статье - к DAO350.DLL, который на моей машине располагается в каталоге "C:\Program Files\Common Files\Microsoft Shared\DAO".

Запустим dumpbin следующей командой из командной строки:

dumpbin.exe /EXPORTS C:\Program Files\Common Files\Microsoft Shared\DAO\DAO350.DLL > tttt.txt

Переназначение вывода dumpbin в файл tttt.txt сделано удобства ради. По умолчанию программа выводит на консоль, а мне хочется получить вывод так, чтобы его можно было исследовать и после её завершения. Посмотрим, что мы в этот файл получили. А получили мы вот (с небольшими сокращениями) что:

Microsoft (R) COFF Binary File Dumper Version 6.00.8447

Copyright (C) Microsoft Corp 1992-1998. All rights reserved.

Dump of file DAO350.DLL

File Type: DLL

Section contains the following exports for DAO350.dll

ordinalhintRVAname
2000008D28DllCanUnloadNow
1100008A31DllGetClassObject
3200009199DllRegisterServer
53000090DBDllRegisterServerEx
4400008E51DllUnregisterServer

Это в точности то, что мы хотели - таблица EXPORTS. У этой DLL всего пять экспортируемых функций! Забегая вперёд скажу - все они используются исключительно для функционирования COM, т.е. перед нами чистый COM-сервер, не предназначенный делать ничего больше.

И из этих пяти функций в настоящий момент речь идет только об одной - DllGetClassObject. Она - та самая функция, вызывая которую система сообщает серверу ссылку на какой объект он должен изготовить и предоставить системе. Обращение к MSDN дает следующий прототип этой функции:

 STDAPI DllGetClassObject(        REFCLSID rclsid,    //CLSID объекта класса        REFIID riid,        //Ссылка на идентификатор  интерфейса,                            //который взаимодействует с объектом        LPVOID * ppv        //Адрес выходной переменной, которая принимает                            //указатель на  интерфейс, указанный  riid        );

т.е. система при вызове данной функции готовит список аргументов <CLSID>> -<ссылка на интерфейс>-<адрес переменной, куда вернуть результат>. Функция DllGetClassObject принадлежит DLL, т.е., видимо, как-то умеет производить объекты запрошенного при помощи CLSID имени. Адрес произведенного объекта система ожидает получить посредством указателя ppv. Напомним, что CLSID - не что иное, как имя COM-объекта, это - CLaSs IDentifier.

Откуда приходят все эти параметры? Они приходят от COM-клиента, который захотел получить экземпляр объекта, именуемого данным CLSID. Для того, чтобы система выяснила какой сервер необходимо активизировать (просмотрев системный реестр), загрузила его, и вызвала у этого сервера функцию DllGetClassObject клиент на своей стороне вызывает "широко известную в узких кругах" функцию Win32 API CoCreateInstance - создать экземпляр объекта. MSDN дает о ней следующую справку:

 STDAPI CoCreateInstance(        REFCLSID rclsid, //CLSID объекта класса        LPUNKNOWN pUnkOuter,        DWORD dwClsContext,        REFIID riid,     //Ссылка на идентификатор  интерфейса,                         //который взаимодействует с объектом        LPVOID * ppv     //Адрес выходной переменной, которая принимает                         //указатель на  интерфейс, указанный  riid        );

Аргументы pUnkOuter и dwClsContext в данный момент нас не интересуют, они указывают системе среди DLL или EXE-серверов искать интересующий нас объект и агрегировать ли его. А вот три других аргумента - в точности те, которые передаются системой функции сервера DllGetClassObject. И - функцию CoCreateInstance клиент вызывает из своего кода. Иными словами - вызов клиентом функции CoCreateInstance "транслируется" системой в вызов функции DllGetClassObject на стороне сервера. DllGetClassObject возвращает адрес объекта, система передает его CoCreateInstance, которая возвращает его клиенту. Всё, наша экскурсия в философию закончена - пункты с первого по четвёртый проиллюстрированы сущностями операционной системы.

Но вот откуда берется аргумент riid и что он означает? И сам этот вопрос и ответ на него старательно обходились с начала нашего изложения, а между тем компонентное программирование вообще-то начинается с ответа на этот вопрос. riid есть идентификатор интерфейса. В пункте пятом нашего философского изложения стояло - "как взаимодействовать между собой объекты и сами знают...". На самом деле, в COM объекты взаимодействуют друг с другом посредством интерфейсов и - никак иначе.

предыдущий выпускархив и оглавлениеследующий выпуск
Авторские права © 2001 - 2004, Михаил Безверхов
Публикация требует разрешения автора

разделы сайта

форум

технология COM

оптимизация сайтов


© 2000-2004 Клуб программистов developing.ru