Где COM хранит имена статических типов...

 

Итак, мы рассматривали генерацию уникального идентификатора статического типа COM-объекта. Но это только половина всей проблемы - эти идентификаторы нужно где-то помнить. Для этой цели в MS Windows используется специальный системный сервис, называемый системный реестр.

Надобность возникновения системного реестра в эволюции операционной системы возникла очень давно. Даже программы в такой примитивной системе как DOS нуждались в каком-то средстве, которое позволяло сохранять им хотя бы настроечную информацию в промежутках между сеансами. Традиционно это реализовывалось как особый файл, который программа прочитывала при старте и записывала при завершении. Те, кто работал с Windows 3.x, может быть, еще помнят, что этот файл имел расширение *.ini и хранился в каталоге Windows. Каждая программа вела этот файл самостоятельно и кто во что горазд. Всё возрастающая сложность программ и большая избыточность таких индивидуальных файлов сподвигла разработчиков операционной системы призадуматься над какой-то централизацией и упорядочением этого разрозненного хозяйства. Так появилась концепция особой системной базы данных, которая бы, если можно так выразиться, содержала в себе информацию о "самоосознании данной копии операционной системы" работающей на данном компьютере.

Эта концепция и была реализована в виде системного реестра, который представляет собой базу данных такого назначения и средства доступа к ней. Средства эти глубоко интегрированы в операционную систему, поэтому всякая программа может воспользоваться этим сервисом для межсеансного (постоянного, энергонезависимого) хранения своей информации о состоянии. Разумеется, что первым пользователем этой базы является сама операционная система - без нее система оказывается хуже малого ребёнка и не может даже загрузиться, поскольку не помнит какими ресурсами располагает. Точнее говоря, все свои знания о том, что она - система, операционная система хранит именно там.

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

Иерархическая организация означает, что единицы хранения в этой БД находятся между собой в отношении "родитель-потомок" и однозначный доступ к потомку обеспечивается перечислением всех его предков.

Системный реестр организован так же. Единицами хранения в нем являются "разделы" (keys) и "параметры" (values), которые соотносятся с "каталогами" и "файлами" привычной файловой системы - разделы могут содержать в себе другие разделы и параметры, а параметры - нет. И разделы, и параметры могут иметь значения - числа или строки, так что эти числа или строки адресуемы при помощи указания цепочки разделов и параметра.

Существуют установленные правила о структурировании информации реестра, т.е. какого сорта информация должна присутствовать в определенном разделе реестра, существует набор функций Win32 API, позволяющий читать, искать, записывать и удалять информацию и существует специальная программа regedit.exe, которая представляет собой графический редактор реестра. Запущенная программа показывает окно, разделенное по вертикали на две неравные части. В левой части окна показывается пиктограмма "Мой компьютер" и значок "плюс", означающий, что на самом деле мы имеем внутри скрытое дерево. Развернув дерево мы увидим строки:

HKEY_CLASSES_ROOT

HKEY_CURRENT_USER

HKEY_LOCAL_MACHINE

которые тоже содержат в себе деревья. Эти строки именуют части (parts) системного реестра, каждая часть содержит в себе информацию определенного назначения. Например, часть HKEY_LOCAL_MACHINE содержит в себе информацию, относящуюся к конфигурации данного локального компьютера, а раздел HKEY_CLASSES_ROOT является корневым для информации о классах (статических типах) объектов, известных операционной системе.

Раскройте раздел HKEY_LOCAL_MACHINE, внутри вы увидите:

HARDWARE

SOFTWARE

которые так же являются деревьями. Далее раскройте раздел HARDWARE, а внутри него - раздел DESCRIPTION, а в нём - раздел System. Правое окно изменится - оно покажет строки типа Identifier, VideoBiosDate и другие. Строкам сопоставлены какие-то значения, сейчас для нас совершенно неважно - какие.

Разделы HKEY_… HARDWARE и т.д. являются разделами реестра, а строки Identifier и VideoBiosDate - параметрами. Внимательный читатель так же мог заметить, что когда показывается раздел, не имеющий в себе ни одного параметра, правое окно все-таки показывает параметр с именем "(По умолчанию/Default)", что означает, что некое значение можно связать не только с параметром, но и с самим разделом.

Естественно, что редактор реестра умеет и операции выполнять, которые перечислены в пунктах его меню - создавать, удалять, искать… И вот здесь самое время сделать очень важное замечание! Системный реестр - очень значимая и ответственная в операционной системе сущность. Повреждение системного реестра практически всегда означает, что какая-то часть функциональности операционной системы "отсыхает" - система перестает о ней что либо знать и "держать во внимании". С точки зрения пользователя, cистемный реестр - примитивная база данных. У неё нет ни откатов, ни транзакций. То, что вы "наворотили" прямым ходом записывается на диск и немедленно на нём сохраняется. Поэтому, если вы что-то "не то" удалите или измените в системном реестре - в лучшем случае отделаетесь потерей функциональности какой-либо программы, а в худшем - операционную систему придётся переустанавливать заново.

Итак, в системном реестре (иллюстрирующая наше объяснение программа вызывается на исполнение командой С:\WinNT\regedit.exe, если операционная система у вас расположена по адресу С:\WinNT) можно обнаружить несколько сущностей вида:

HKEY_CLASSES_ROOT

HKEY_CURRENT_USER

HKEY_LOCAL_MACHINE

и других аналогичных.

Они называются "части", являются корнями соответствующих отрастающих от них деревьев и являются предустановленными. Это означает - никакой "своей" части пользователь в реестре завести не может, не может также и переименовать или удалить существующую часть. Назначение частей предопределено, а их имена в написании часто сокращают. Программист COM должен знать, что аббревиатура HKCR обозначает часть HKEY_CLASSES_ROOT, а аббревиатура HKLM - HKEY_LOCAL_MACHINE. Думаю, что произвести все другие возможные здесь аббревиатуры не составит никакого труда и самостоятельно.

Адресация в реестре производится аналогично указанию имени файла в файловой системе, но всегда - указанием полного пути. Например, строка :

HKEY_LOCAL_MACHINE\HARDWARE\DESCRIPTION\System\Identifier= "abcd"

ссылается на значение "abcd", располагающееся по приведённому пути.

Чем в данном случае является Identifier - разделом или параметром? В такой нотации это не видно да и установить невозможно - раздел, подобно параметру, может иметь значение, с ним ассоциированное. Но расположение этого значения - такой нотацией указывается точно.

К путанице такая небольшая неоднозначность не приводит, т.к. искать в системном реестре имя раздела или параметра, которым присвоено заданное значение может только человек. Программы-то, напротив, ищут значение в хорошо им известной структуре разделов и параметров, а они знают что должно чем являться. Нужно также еще сказать, что исторически устоялось, что часть идентификаторов разделов и параметров записывается прописными буквами, а часть - строчными. Но для функциональности это абсолютно несущественно, функции API, обслуживающие реестр, нечувствительны к регистру, в котором записаны идентификаторы.

Для "использования технологией COM" в системном реестре зарезервировано несколько подразделов. Главный, с точки зрения COM, подраздел это - HKEY_CLASSES_ROOT\CLSID. Если вы откроете его, то увидите, что весь он состоит из GUID, т.е. составляющие его разделы это - GUID. Каждый раздел обозначенный GUID содержит в себе информацию, относящуюся к одному COM-объекту, т.к. GUID и есть "официальное имя объекта". Посмотрим, что это за информация, которая собрана под именем GUID.

На моей машине первым по порядку следования (а порядок следования разделов и параметров в окне редактора реестра - по алфавиту) находится такой раздел:

HKEY_CLASSES_ROOT\CLSID\{00000010-0000-0010-8000-00AA006D2EA4},

которому присвоено значение DAO.DBEngine.35, т.е.

HKCR\CLSID\{00000010-0000-0010-8000-00AA006D2EA4} = DAO.DBEngine.35

Это - объект системы DAO доступа к данным машины Microsoft Jet Database Engine, версии 3.5. Его "идентификатор навсегда" на всех машинах установлен разработчиком этого объекта - 00000010-0000-0010-8000-00AA006D2EA4. А вот то, что это объект DAO я просто как-то догадался… :)

Посмотрим также, что скрывается в разделе обозначенном этим GUID. Там располагаются еще два раздела:

HKCR\CLSID\{00000010-0000-0010-8000-00AA006D2EA4}\ProgID

HKCR\CLSID\{00000010-0000-0010-8000-00AA006D2EA4}\InprocServer32

Раздел ProgID пока лежит в стороне от нашего изложения, а вот раздел InprocServer32 очень любопытен, потому, что его значением (параметр Default) является строка:

C:\Program Files\Common Files\Microsoft Shared\DAO\DAO350.DLL

которая есть не что иное, как ссылка на исполняемый модуль, в котором обитает означенный COM-объект. Иными словами, если мы знаем GUID именующий требуемый нам объект, то нам достаточно посмотреть в системном реестре раздел:

HKCR\CLSID\<наш GUID>\InprocServer32,

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

Рассмотрите еще несколько любых разделов - везде значением параметра InprocServer32 является строка с указанием имени DLL. Но ведь ранее говорилось, что COM-объект может жить и внутри EXE-модуля? И это тоже наблюдается - внутри некоторых GUID-разделов можно не обнаружить раздела InprocServer32, но обнаруживается раздел с именем LocalServer32. Например, на моей машине таковой первым нашелся у GUID 00020800-0000-0000-C000-000000000046:

HKCR\CLSID\{00020800-0000-0000-C000-000000000046}\LocalServer32

А его значением оказалась ссылка на EXE-модуль:

D:\Office\GRAPH9.EXE /automation

который, очевидно, должна запустить система когда от нее клиент потребует предоставить ссылку на объект 00020800-0000-0000-C000-000000000046

Нет ничего странного в том, что разделы, описывающие DLL и EXE-серверы называются по разному. Ведь и модули этих типов на исполнение загружаются тоже совершенно различно, а система должна возможность различать что есть что.

Кроме того, в разделе LocalServer32 обнаруживается еще и параметр ThreadingModel, значением которого является слово Apartment. Этот параметр описывает потоковую модель в которой может работать компонент (и подробное рассмотрение его случится в нашей же рассылке, но - ещё нескоро). Смысл параметра - если компонент вызывается из программы, владеющей несколькими потоками, то как следует его вызывать, а точнее - что "умеет компонент" по управлению конкурентным исполнением внутри себя, а что должна делать вызывающая сторона.

Ну и в заключение - что такое раздел ProgID связанный со статическим типом? Это - "человеческое имя" данного статического типа. Хотя GUID совершенно однозначно идентифицирует статический тип в некоторых случаях бывает удобнее пользоваться всё-таки мнемонически значимым именем. Раздел ProgID и связывает это мнемоническое имя с GUID. Внешнее (в реестре), а не внутреннее (в COM-сервере) связывание в данном случае совершенно оправданно - если на данной конкретной машине и возникнет конфликт мнемонических имён, то ведь мнемоническое имя можно легко и сменить, в то время, как GUID никогда и нигде сменить невозможно.

Предлагаю ознакомиться с аналогичными статьями: