Сообщений 2 Оценка 281 Оценить |
Если Вы пишете свой COM-объект, но по каким-то причинам не хотите использовать ATL, то перед Вами встанет задача написания функций регистрации и дерегистрации компонента. Использовать для этого функции доступа к реестру для ручного занесения каждого ключа очень муторно и неприятно. Но есть альтернативной решение – использование компонента ATL Registrar. В статье рассматривается использование компонента ATL Registrar в приложениях, не использующих каркас ATL. Описанные методы работают как с ATL 3.0, так и с ATL 7.0.
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 создаётся так же, как и любой другой COM-объект:
STDAPI DllRegisterServer() { IRegistrar *reg=0; HRESULT hr = ::CoCreateInstance( CLSID_Registrar, NULL, CLSCTX_INPROC_SERVER, IID_IRegistrar, (void**)®); 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К. |
Этот формат был специально разработан фирмой 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-файл в ресурс нужно сделать следующее:
Теперь, когда вы всё знаете, привожу полный листинг процедур регистрации и дерегистрации:
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**)®); 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 Оценить |