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

"...DllRegisterServer как зеркало статических типов, реализуемых сервером..."

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

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

Первым известным способом это сделать, довольно простым и сравнительно малозатратным, является использование специального .reg-файла. Возможность его использования появляется потому, что системный реестр - простая иерархическая строковая база данных. Снять с нее дамп - задача элементарная. И восстановить базу данных из дампа - задача не сложнее. Во всяком случае, редактор реестра - программа regedit.exe (см. статью Где COM хранит имена статических типов...) делать это умеет - и для полного дампа системного реестра, и для дампа любой его ветви, и для дампа выборочного. Ведь все параметры в реестре адресуются абсолютно, место расположения всякого параметра строго определено, поэтому если мы просто составим массив символьных строк с указанием чего и где, оформим его в виде файла "правильного формата" и предложим импортировать редактору реестра, то редактор всю работу по перенесению информации в реестр выполнит за нас.

Вот фрагмент такого файла:

Windows Registry Editor Version 5.00 [HKEY_CLASSES_ROOT\CLSID\{0002E510-0000-0000-C000-000000000046}] @="Microsoft Office Spreadsheet 9.0" [HKEY_CLASSES_ROOT\CLSID\{0002E510-0000-0000-C000-000000000046}\InprocServer32] @="C:\\PROGRA~1\\MICROS~1\\Office\\MSOWC.DLL" [HKEY_CLASSES_ROOT\CLSID\{0002E510-0000-0000-C000-000000000046}\ProgID] @="OWC.Spreadsheet.9" [HKEY_CLASSES_ROOT\CLSID\{0002E510-0000-0000-C000-000000000046}\VersionIndependentProgID] @="OWC.Spreadsheet"

Вот командная строка, которая заставляет редактор реестра произвести импорт в реестр:

regedit.exe <имя файла>.reg

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

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

Следующий способ предполагает использование "профессионального инсталлятора". Например, Install Shield или MS Installer. Эта категория программных средств позволяет составить скрипт дистрибутива, потом его как-то преобразовать в "человеконеисправляемую форму" и предложить специальной программе setup.exe которую сам же "профессиональный инсталлятор" и составляет - именно для того, чтобы исполнить этот самый скрипт на пользовательской машине. Каждый программист знает, что это такое, а то и сам пользовался этим способом создания дистрибутивов.

Этот способ сложнее в использовании, но обладает значительно бОльшими достоинствами - и целостность дистрибутива можно проверить, и деинсталляция возможна "правильная" и при регистрации компонентов можно не просто вписать значения параметров, а ещё кое-что проверить. Однако и здесь не обходится без ложки дёгтя - полную структуру инсталляционной информации, необходимой для функционирования вашего продукта вам приходится раскрывать (системный реестр ведь используется не только для функционирования COM, в нём можно держать ещё массу другой полезной информации, которая не вся может быть предназначена для посторонних глаз - ни по содержанию, ни по структуре). Когда автором и правообладателем всего "конечного дистрибутива" являетесь вы сами это не составляет проблемы. Но ситуация меняется, если вы являетесь автором только т.н. redistributable components - модулей, которые просто входят в состав дистрибутива чужого.

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

Здесь можно сказать, что функция DllRegisterServer вызывается за время эксплуатации данной копии модуля, по сути, только один раз. Но присутствует в составе модуля как неотъемлемая его часть постоянно. А, значит, она не должна занимать очень много места, хотя вполне допустимо, если она будет работать не очень быстро.

Канонически (по современным меркам - давным-давно) для этого использовалось семейство системных функций Reg????Key???. Как выглядит один из его представителей - RegCreateKeyEx любопытства ради приведено ниже:

LONG RegCreateKeyEx(   HKEY hKey,              //дескриптор открытого параметра реестра   LPCTSTR lpSubKey,       //имя подпараметра   DWORD Reserved,         //зарезервировано   LPTSTR lpClass,         //строка класса   DWORD dwOptions,        //особые опции   REGSAM samDesired,      //желаемый уровень безопасности доступа   LPSECURITY_ATTRIBUTES lpSecurityAttributes,//наследуемые значения   PHKEY phkResult,        //дескриптор созданного ключа   LPDWORD lpdwDisposition //буфер для диспозиции );

Проблем с его использованием нет никаких - надо подставить параметры и функция впишет в реестр один параметр реестра. Это грустное обстоятельство, а также то, что информация о статическом типе в реестре регулярна, стимулировала программистов делать "внутренние скрипты", которые бы в виде двоичного ресурса присутствовали в модуле, и для которых бы вызывалась относительно универсальная процедура, просто вызывающая серию RegCreateKeyEx. Такое решение, например, было использовано в ATL 1.1 (ныне применяющаяся версия ATL - 3.0).

Ну и наконец, существует самое изящное решение - воспользоваться "стандартным системным сервисом". Можно только гадать, почему Microsoft ввела этот сервис в состав операционной системы так поздно. Ведь в спецификациях CORBA "сервис репозитариев" был определён изначально и программисты CORBA имели с регистрацией куда меньше мучений (что, впрочем, с лихвой компенсировалось и компенсируется мучениями другими). Однако, начиная с версии Windows NT 4.0 SP3 и выше, такой сервис в системе присутствует, причём, как и положено, сервис этот - сам является COM-объектом.

Называется он Component Categories Manager, его CLSID - {0002E005-0000-0000-C000-000000000046} и он реализован в составе модуля OLE32.DLL, т.е. если в системе есть COM, есть и данный сервис. В собственной программе на него можно сослаться по идентификатору CLSID_StdComponentCategoriesMgr. Компонент экспонирует два интерфейса ICatInformation и ICatRegister. Как использовать это чудо компонентной технологии - сейчас желающие отсылаются к MSDN. В нашем повествовании сейчас пока достаточно только знать, что такой сервис есть и что им можно и нужно пользоваться.

Но это никоим образом не умаляет заслуг старого семейства функций Reg????Key??? и не освобождает программиста от необходимости знать и о них и том, как они работают. Всё дело-то в том, что Component Categories Manager регистрирует в реестре только "стандартную системную" информацию! А иногда нужно регистрировать и проверять и информацию "внесистемную" - например, при реализации интерфейса IClassFactory2 вам может понадобиться "куда-то в системе запрятать ключ лицензии". Это вряд ли стоит делать какими-то уж очень стандартными средствами, верно?

Поэтому, исключительно демонстрации того ради, "как это делается на самом деле" мы в очередном примере нашей рассылки пока используем только Reg????Key??? и зарегистрируем только те параметры реестра, которые упоминались как "минимально достаточные".

Естественно, что вторая экспортируемая функция сервера - DllUnregisterServer - реализуется совершенно аналогично, только направление её работы будет прямо противоположным - всё, что сервер вписал в реестр при регистрации она должна вычистить. Хотя... хотя для удаления сервера из системы это - вовсе не обязательная операция. Стереть файл можно и без предварительной разрегистрации сервера. Многие, особенно небрежно составленные программы и инсталляционные скрипты, делают так. Но сам я не думаю, что они - достойный пример для подражания. Поэтому и функцию разрегистрации сервера реализовывать тоже необходимо.

В следующей статье о DllCanUnloadNow и выгрузке DLL-сервера...

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

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

форум

технология COM

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


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