Что такое "технология COM" и как с ней бороться?№9
... и что именно COM хранит
В
современном программировании системный
реестр, начиная с какого-то уровня
опытности программиста - знакомое понятие.
Хоть раз и хоть где-то программист о нём
слышал, а может быть - уже и лазал, да не
просто из любопытства, но по делу.
Поэтому для большинства не будет новостью, что и COM
хранит свою конфигурационную информацию
там же. Вопрос в другом - что он там хранит?
Информация,
которую использует система для
обслуживания COM, многообразна. Она
совершенно чётко и однозначно
структурирована и эти правила не являются
очень сложными. Другое дело, что в настоящий
момент изложения большинство из понятий,
которые можно было бы обнаружить в
системном реестре относящимися к COM, ещё не
введены нами в рассмотрение. Вследствие
этого сейчас мы ограничимся только систематизацией этой информации и
иллюстрацией, как же требуемая информация
расположена. А недостающие знания о
конкретных объектах в системном реестре мы
будем вводить по мере продвижения вперёд.
Вообще говоря, я стал получать письма с
одинаковым, по сути, вопросом - "Доколе же?".
Я понимаю ваше нетерпение, уважаемые
почитатели нашей рассылки, но наша
аудитория неоднородна и вступительных
экзаменов не имеет. Поэтому, дабы потом
можно было говорить о чём-то более
интересном понятно, поминутно не сбиваясь в
определения и уточнения, я вначале
неглубоко очерчиваю область рассмотрения в
надежде, что не знающие каких-то понятий
читатели будут иметь возможность увидеть
свои пробелы и начнут отыскивать способы их
ликвидации. Первые десять рассылок мною и
предназначены только для этого, в 11-й
рассылке вводится понятие "интерфейс",
а фрагменты кода появляются с 12 - 13-й
рассылок. Оставайтесь с нами!
Итак,
в системном реестре (иллюстрирующая наше
объяснение программа вызывается на
исполнение командой С:\WinNT\regedit.exe,
если операционная система у вас
расположена по адресу С:\WinNT) можно
обнаружить несколько сущностей вида:
HKEY_CLASSES_ROOT
HKEY_CURRENT_USER
HKEY_LOCAL_MACHINE
…
и других
аналогичных.
Они
называются "части", являются корнями
соответствующих отрастающих от них
деревьев и являются предустановленными.
Это означает - никакой "своей" части
пользователь в реестре завести не может, не
может также и переименовать или удалить
существующую часть. Назначение частей
предопределено, а их имена в написании
часто сокращают. Программист COM должен
знать, что аббревиатура HKCR обозначает часть
HKEY_CLASSES_ROOT, а аббревиатура HKLM -
HKEY_LOCAL_MACHINE. Думаю, что произвести все другие возможные
здесь аббревиатуры не составит никакого труда
и самостоятельно.
Адресация
в реестре производится аналогично указанию
имени файла в файловой системе, но всегда -
указанием полного пути. Например, строка :
ссылается
на значение "abcd", располагающееся по
приведённому пути.
Чем
в данном случае является Identifier -
разделом или параметром? В такой нотации
это не видно да и установить невозможно -
раздел, подобно параметру, может иметь
значение, с ним ассоциированное. Но
расположение этого значения - такой
нотацией указывается точно.
К
путанице такая небольшая неоднозначность не приводит,
т.к. искать в системном реестре имя
раздела или параметра, которым присвоено
заданное значение может только человек.
Программы-то, напротив, ищут значение в
хорошо им известной структуре разделов и
параметров, а они знают что должно чем
являться. Нужно также еще сказать, что
исторически устоялось, что часть
идентификаторов разделов и параметров
записывается прописными буквами, а часть -
строчными. Но для функциональности это
абсолютно несущественно, функции API, обслуживающие реестр, нечувствительны к
регистру, в котором записаны
идентификаторы.
Для
"использования технологией COM" в системном
реестре зарезервировано несколько подразделов.
Главный, с точки зрения COM, подраздел это -
HKEY_CLASSES_ROOT\CLSID. Если вы откроете его, то
увидите, что весь он состоит из GUID, т.е.
составляющие его разделы это - GUID. Каждый
раздел обозначенный GUID содержит в себе
информацию, относящуюся к одному COM-объекту,
т.к. GUID и есть "официальное имя объекта".
Посмотрим, что это за информация, которая
собрана под именем GUID.
На
моей машине первым по порядку следования (а порядок
следования разделов и параметров в окне
редактора реестра - по алфавиту) находится
такой раздел:
Это
- объект системы DAO доступа к данным машины
Microsoft Jet Database Engine, версии 3.5. Его "идентификатор
навсегда" на всех машинах установлен
разработчиком этого объекта - 00000010-0000-0010-8000-00AA006D2EA4.
А вот то, что это объект DAO я просто как-то
догадался… :)
Посмотрим
также, что скрывается в разделе
обозначенном этим GUID. Там располагаются еще
два раздела:
Раздел
ProgID пока лежит в стороне от нашего
изложения, а вот раздел InprocServer32 очень
любопытен, потому, что его значением (параметр
Default) является строка:
которая
есть не что иное, как ссылка на исполняемый
модуль, в котором обитает означенный COM-объект.
Иными словами, если мы знаем GUID именующий
требуемый нам объект, то нам достаточно
посмотреть в системном реестре раздел:
HKCR\CLSID\<наш
GUID>\InprocServer32,
прочитать присвоенное ему значение и выяснить, какой исполняемый
модуль необходимо загрузить. Если такого GUID
не обнаружится, то это означает, что такой COM-объект данной системе неизвестен. В самом
начале нашего философского рассуждения о
механизме разыскания объектов мы об этом
говорили - система должна помнить имена
статических типов COM-объектов и ссылки на
исполняемые модули в которых объекты
обитают. Теперь вы видите, каким образом
система это делает "на самом деле".
Терминологически такой модуль, реализующий
COM-объекты называется "COM-сервер".
Рассмотрите
еще несколько любых разделов - везде
значением параметра InprocServer32 является
строка с указанием имени DLL. Но ведь ранее
говорилось, что COM-объект может жить и
внутри EXE-модуля? И это тоже наблюдается -
внутри некоторых GUID-разделов можно не
обнаружить раздела InprocServer32, но
обнаруживается раздел с именем LocalServer32.
Например, на моей машине таковой первым
нашелся у GUID 00020800-0000-0000-C000-000000000046:
который, очевидно, должна запустить система когда от
нее клиент потребует предоставить ссылку
на объект 00020800-0000-0000-C000-000000000046
Нет
ничего странного в том, что разделы,
описывающие DLL и EXE-серверы называются по
разному. Ведь и модули этих типов на
исполнение загружаются тоже совершенно
различно, а система должна возможность
различать что есть что.
Кроме
того, в разделе LocalServer32
обнаруживается еще и параметр ThreadingModel,
значением которого является слово Apartment.
Этот параметр описывает потоковую модель в
которой может работать компонент (и
подробное рассмотрение его случится в
нашей же рассылке, но - ещё нескоро). Смысл
параметра - если компонент вызывается из
программы, владеющей несколькими потоками,
то как следует его вызывать, а точнее - что
"умеет компонент" по управлению
конкурентным исполнением внутри себя, а что
должна делать вызывающая сторона.
Ну
и в заключение сегодняшней рассылки - что
такое раздел ProgID
связанный со статическим типом? Это - "человеческое
имя" данного статического типа. Хотя GUID
совершенно однозначно идентифицирует
статический тип в некоторых случаях бывает
удобнее пользоваться всё-таки мнемонически
значимым именем. Раздел ProgID
и связывает это мнемоническое имя с GUID.
Внешнее (в реестре), а не внутреннее (в COM-сервере)
связывание в данном случае совершенно
оправданно - если на данной конкретной
машине и возникнет конфликт мнемонических
имён (см. рассылку
№7), то ведь мнемоническое имя можно легко
и сменить, в то время, как GUID никогда и
нигде сменить невозможно.
В
следующей рассылке мы закончим обзорное
рассмотрение ресурсов, сформулированных в
рассылках №№ 5
и 6 и начнём
"серьёзное изучение COM".