Сообщений 2    Оценка 281        Оценить  
Система Orphus

Использование ATL Registrar

Использование средств ATL для регистрации своих компонент

Автор: Eugene Korobko
Опубликовано: 28.12.2003
Исправлено: 10.12.2016
Версия текста: 1.1
Введение
Использование ATL Registrar
Что представляет из себя ATL Registrar
Создание объекта ATL Registrar
RGS-скрипты
Подключение ресурса
Пример реализации регистрации и дерегистрации
Заключение

Введение

Если Вы пишете свой COM-объект, но по каким-то причинам не хотите использовать ATL, то перед Вами встанет задача написания функций регистрации и дерегистрации компонента. Использовать для этого функции доступа к реестру для ручного занесения каждого ключа очень муторно и неприятно. Но есть альтернативной решение – использование компонента ATL Registrar. В статье рассматривается использование компонента ATL Registrar в приложениях, не использующих каркас ATL. Описанные методы работают как с ATL 3.0, так и с ATL 7.0.

Использование ATL Registrar

Что представляет из себя ATL Registrar

ATL Registrar – это обычный COM объект, который умеет заносить в реестр и удалять из него информацию, оформленную специальным образом, в виде RGS-скрипта (см. ниже). Этот компонент реализован в библиотеке atl.dll, которая, как правило, находится в каталоге system (system32) Вашей операционной системы. Компонент называется “ATL.Registrar”, GUID {44EC053A-400F-11D0-9DCD-00A0C90391D3}.

Для работы с компонентом нужно подключить файл atlbase.h.

ПРИМЕЧАНИЕ

Собственно IRegistrar описан в atliface.h

Из этого файла нам нужно только описание интерфейсов и константы GUID-ов. Поскольку библиотека ATL построена на шаблонах, включение этого файла практически не отразится на объёме Ваших бинарников.

ATL Registrar реализует только один интерфейс – IRegistrar (не считая, конечно, IUnknown). Именно через этот интерфейс и ведётся вся работа.

Интерфейс IRegistrar выглядит следующим образом (описан на С++):

    Class IRegistrar : public IUnknown
    {
      public:
        virtual HRESULT STDMETHODCALLTYPE AddReplacement(LPCOLESTR key,LPCOLESTR item)=0;
        virtual HRESULT STDMETHODCALLTYPE ClearReplacements()=0;
        virtual HRESULT STDMETHODCALLTYPE ResourceRegisterSz(LPCOLESTR resFileName,LPCOLESTR szID,LPCOLESTR szType)=0;
        virtual HRESULT STDMETHODCALLTYPE ResourceUnregisterSz(LPCOLESTR resFileName,LPCOLESTR szID,LPCOLESTR szType)=0;
        virtual HRESULT STDMETHODCALLTYPE FileRegister(LPCOLESTR fileName)=0;
        virtual HRESULT STDMETHODCALLTYPE FileUnregister(LPCOLESTR fileName)=0;
        virtual HRESULT STDMETHODCALLTYPE StringRegister(LPCOLESTR data)=0;
        virtual HRESULT STDMETHODCALLTYPE StringUnregister(LPCOLESTR data)=0;
        virtual HRESULT STDMETHODCALLTYPE ResourceRegister(LPCOLESTR resFileName,UINT nID,LPCOLESTR szType)=0;
        virtual HRESULT STDMETHODCALLTYPE ResourceUnregister(LPCOLESTR resFileName,UINT nID,LPCOLESTR szType)=0;
    };

Подробно методы этого интерфейса будут описаны ниже.

Создание объекта ATL Registrar

ATL Registrar создаётся так же, как и любой другой COM-объект:

STDAPI DllRegisterServer()
{
    IRegistrar *reg=0;
    HRESULT hr = ::CoCreateInstance( CLSID_Registrar, NULL, CLSCTX_INPROC_SERVER, IID_IRegistrar, (void**)&reg);
    if ( SUCCEEDED( hr ) )
    {
        // используем
        . . .
        // отпускаем
        reg->Release();
    }    
    return S_OK;
}
ПРИМЕЧАНИЕ

В реальной программе, конечно же, нужно вызывать CoInitialize и CoUninitialize, а также проверять результат и обрабатывать ошибки.

Аналогично будет выглядеть код и в функции DllUnregisterServer.

Существует и альтернативный вариант – использование статического подключения. Статическое подключение возможно благодаря тому, что ATL поставляется вместе со своим исходным кодом. Если вы загляните в файл statreg.h каталога ATL, то увидете в нём описание класса CRegObject, который реализует интерфейс IRegistrar. Это и есть нужный нам класс.

В этом случае создание объекта ATL Registrar становится тривиальным:

STDAPI DllRegisterServer()
{
    CRegObject r;
    …
    return S_OK;
}
ПРИМЕЧАНИЕ

Статическая линковка возможна только в программе на С++, в то время как подключение через COM может быть реализовано на любом языке, поддерживающим COM. Кроме того, использование статической линковки увеличивает объем бинарного кода примерно на 20К.

RGS-скрипты

Этот формат был специально разработан фирмой Microsoft для описания правил регистрации и дерегистрации компонент. В MSDN приведено формальное описание синтаксиса языка в нотации Бэкуса-Наура. Приведу его.

RGS-скрипт состоит из одного или более корневых деревьев (root_tree), каждое из которых описывается следующими правилами:

<root_tree> ::= <root key>{<registry expression>}+
<root key> ::= HKEY_CLASSES_ROOT | HKEY_CURRENT_USER |
               HKEY_LOCAL_MACHINE | HKEY_USERS |
               HKEY_PERFORMANCE_DATA | HKEY_DYN_DATA |
               HKEY_CURRENT_CONFIG | HKCR | HKCU |
               HKLM | HKU | HKPD | HKDD | HKCC
<registry expression> ::= <Add Key> | <Delete Key>
<Add Key> ::= [ForceRemove | NoRemove | val]<Key Name>
              [<Key Value>][{< Add Key>}]
<Delete Key> ::= Delete<Key Name>
<Key Name> ::= '<AlphaNumeric>+'
<AlphaNumeric> ::= any character not NULL, i.e. ASCII 0
<Key Value> ::== <Key Type><Key Name>
<Key Type> ::= s | d
<Key Value> ::= '<AlphaNumeric>'

Приведу пример RGS-скрипта для регистрации COM-объекта:

HKEY_CLASSES_ROOT
{
   MyProject.MyObject = s 'Test object for my article'
   {
      CLSID = s '{10AAF476-7DE1-4148-94B5-B48C015A828D}'
   }
   NoRemove CLSID
   {
      ForceRemove {10AAF476-7DE1-4148-94B5-B48C015A828D} = s 'Test object for my article'
      {
         ProgID = s 'MyProject.MyObject'
         InprocServer32 = s '%MODULE%'
         {
            val ThreadingModel = s 'Apartment'
         }
      }
   }
}

Во-первых, видно, что скрипт состоит из одного корневого дерева, которое ссылается на корневой ключ реестра HKEY_CLASSES_ROOT. Скрипт добавляет (а в режиме дерегистрации – удаляет) дочерний ключ с именем MyProject.MyObject со значением по умолчанию строкового типа, равным 'Test object for my article'. Далее идёт работа с ключём CLSID. Перед ним стоит специальное слово «NoRemove». Это слово означает, что подключ CLSID не удаляется при дерегистрации. Внутри ключа CLSID описан ключ с именем «{10AAF476-7DE1-4148-94B5-B48C015A828D}» и значением по умолчанию, равным 'Test object for my article'. Слово «ForceRemove» означает, что перед регистрацией содержимое этого ключа будет удалено. Далее аналогично. У ключа InprocServer32 создаётся значение с именем «ThreadingModel», равное «Apartment».

RGS-скрипты позволяют добавлять значения двух типов – числовые и строковые. Строковые выделяются буквой «s» перед значенем, а числовые – буквой «d».

Обратите внимание на строку

InprocServer32 = s '%MODULE%'

Между двумя символами «%» заключается макроподстановка. Программа должна заменить строку «%MODULE%» на реальный путь к компоненту. Для этой цели в интерфейсе IRegistrar есть метод AddReplacement. После создания компонента ATL Registrar нужно дописать:

   TCHAR szModule[_MAX_PATH];
   GetModuleFileName(g_hThisModule, szModule, _MAX_PATH);
   r->AddReplacement(_bstr_t("Module"), _bstr_t(szModule));
ПРИМЕЧАНИЕ

Я использую класс _bstr_t, но можно преобразовываться строки к BSTR любым другим способом.

В интерфейсе IRegistrar есть также метод ClearReplacements, который удаляет все макроподстановки.

ПРИМЕЧАНИЕ

С помощью AddReplacement можно создавать макроподстановки с произвольным именем, а затем использовать их в скрипте.

Ну а теперь – самое главное.

Для регистрации (дерегистрации) можно использовать один из четырёх методов:

Единственное различие между этими методами заключается в том, что они работают с разными источниками скриптов. FileRegister считывает RGS-скрипт из указанного файла, StringRegister принимает в качестве аргумента сам скрипт, ResourceRegister и ResourceRegisterSz считывают RGS-скрипт из регистра и различаются между собой тем, что ResourceRegister принимает в качестве параметра целочисленное ID ресурса, а ResourceRegisterSz – строковое имя.

Каждая из этих функций имеет соответствующую функцию дерегистрации.

Подключение ресурса

Наиболее естественным способом хранения RGS-скрипта представляется использование ресурсов. Однако RGS-скрипты не являются «родным» типом ресурса. Чтобы подключить RGS-файл в ресурс нужно сделать следующее:

  1. Записать RGS-скрипт в файл
  2. В интегрированной среде выбрать пункт меню Insert->Resource
  3. Нажать кнопку Import
  4. Выбрать файл. В качестве типа ресурса ведите «REGISTRY»
  5. Далее – как с обычным ресурсом

Пример реализации регистрации и дерегистрации

Теперь, когда вы всё знаете, привожу полный листинг процедур регистрации и дерегистрации:

STDAPI DllRegisterServer()
{
   IRegistrar *r;
   CoCreateInstance(CLSID_Registrar,0,CLSCTX_INPROC_SERVER,IID_IRegistrar,(void**)&r);
   TCHAR szModule[_MAX_PATH];
   GetModuleFileName(g_hThisModule, szModule, _MAX_PATH);
   r->AddReplacement(_bstr_t("Module"), _bstr_t(szModule));
   r->ResourceRegister(_bstr_t(szModule),IDR_MAINREGISTRY,OLESTR("REGISTRY"));
   return S_OK;
}

STDAPI DllUnRegisterServer()
{
   IRegistrar *r;
   CoCreateInstance(CLSID_Registrar,0,CLSCTX_INPROC_SERVER,IID_IRegistrar,(void**)&r);
   TCHAR szModule[_MAX_PATH];
   GetModuleFileName(g_hThisModule, szModule, _MAX_PATH);
   r->AddReplacement(_bstr_t("Module"), _bstr_t(szModule));
   r->ResourceUnregister(_bstr_t(szModule),IDR_MAINREGISTRY,OLESTR("REGISTRY"));
   return S_OK;
}

Заключение

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

      #include
      "comutil.h"
      #include
      "atlbase.h"
      #pragma comment(lib, "comsupp.lib")

int _tmain(int argc, _TCHAR* argv[])
{
    ::CoInitialize(NULL);

    IRegistrar *reg=0;
    HRESULT hr = ::CoCreateInstance( CLSID_Registrar, NULL, CLSCTX_INPROC_SERVER, IID_IRegistrar, (void**)&reg);
    if ( SUCCEEDED( hr ) )
    {
        // используем
        hr = reg->AddReplacement(_bstr_t("PARAMETER"), _bstr_t("Такой вот строковый параметр"));

        hr = reg->StringRegister( _bstr_t( "HKEY_CURRENT_USER "" {""   NoRemove Software""   {""      ForceRemove Test = s 'Тестовый ключ'""      {""          val Parameter = s '%PARAMETER%'""      }""   }"" }"));         
// отпускаем
         reg->Release();
    }

    ::CoUninitialize();
    return 0;
}

создает ключ HKEY_CURRENT_USER\Software\Test со значением по умолчанию “Тестовый ключ” и внутри него параметр Parameter со значением "Такой вот строковый параметр".


Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав.
    Сообщений 2    Оценка 281        Оценить