1. ATL注册组件
1.1 创建注册脚本
注册脚本通过操作 系统注册表完成Com服务的注册,通过数字形式而非代码API的形式完成,这种形式显得更加简单有效,因为它只需要几行数字代码就可以将一个Key添加到注册表中。
使用ATL向导时,会自动的生成一个后缀为.rgs的注册脚本文件,ATL在服务安装时,会自动的调用脚本文件,实现对注册表的修改,完成Com服务的注册。
1.1.1 基本术语
符号释义
符号 |
解释 |
::= |
相等 |
| |
或 |
X+ |
一个或多个Xs |
[X] |
X是可选的 |
字符串字面值:
字符串字面值 |
行为 |
ForceRemove |
如果存在则删除,再重新建立 |
NoRemove |
反注册时,不删除 |
val |
用于指定一个键的值 |
Delete |
在注册时,删除Key |
s |
类型为REG_SZ |
d |
类型为REG_DWORD |
m |
类型为REG_MULTI_SZ |
b |
类型为REG_BINARY |
示例:
? val‘testhex‘
= d ‘&H55‘
键testhex的值类型为REG_DWORD,
值为&H55
? val‘testmulti‘
= m ‘String 1\0String2\0‘
键testmulti的值类型为REG_MULTI_SZ, 值为String 1 String2
? registryexpression ::= AddKey | DeleteKey
注册表达式等于Add Key 或Delete Key
? KeyName ::= ‘AlphaNumeric+‘
子键Name等于一个或多个任意的非空字符
? registryexpression ::=
[ForceRemove|NoRemove|val]
Add Key等于KeyName,ForceRemove、NoRemove、val都是可选项
? AlphaNumeric ::=any
character not NULL, that is, ASCII 0
AlphaNumeric为任意的非空字符
1.1.2 分析树
在注册脚本文件中,你可以使用一个或多个分析树对注册表进行操作,其树型结构类似文件夹的目录与文件结构,单个结构如下:
root key{registryexpression}+
详细介绍如下:
rootkey ::= 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
registryexpression ::= AddKey | DeleteKey
AddKey ::=[ForceRemove | NoRemove | val]Key Name
[KeyValue][{<
AddKey>}]
DeleteKey ::= Delete KeyName
KeyName ::=‘AlphaNumeric+‘
AlphaNumeric ::=any character not NULL, i.e. ASCII 0
KeyValue ::== KeyType KeyName
KeyType ::=s | d
KeyValue ::=‘AlphaNumeric‘
具体含义,基本术语已经详细介绍了,其中HKEY_CLASSES_ROOT 等于HKCR,HKEY_CURRENT_USER等于HKCU等。
一个分析树可以一次添加一个或多个子键,这种方式通过避免一次只能添加一个子键的限制,显得很有效。
1.1.3 示例
【1】 【1】添加多个子键及键值
C++ Code
1 2 3 4 5 6 7 8 9 10 11 |
HKCU { ‘MyVeryOwnKey‘ = s ‘HowGoesIt?‘ { ‘HasASubkey‘ { ‘PrettyCool?‘ = d ‘55‘ val ‘ANameValue‘ = s ‘WithANamedValue‘ } } } |
在主键HKCU下添加了默认值为‘HowGoesIt?‘的子键‘MyVeryOwnKey‘,在该子键下添加‘HasASubkey‘子键,在‘HasASubkey‘子键下,添加了默认值为‘55‘的‘PrettyCool?‘子键和键值对为‘WithANamedValue‘的‘ANameValue‘,类似文件夹与文件的层次关系,很简单
【2】 注册Com服务
C++ Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
HKCR { ATL.Registrar = s ‘ATL Registrar Class‘ { CLSID = s ‘{44EC053A-400F-11D0-9DCD-00A0C90391D3}‘ } NoRemove CLSID { ForceRemove {44EC053A - 400F - 11D0 - 9DCD - 00A0C90391D3} = s ‘ATL Registrar Class‘ { ProgID = s ‘ATL.Registrar‘ InprocServer32 = s ‘%MODULE%‘ { val ThreadingModel = s ‘Apartment‘ } } } } |
这段理解很简单,在HKCR主键下添加了默认值为‘ATL RegistrarClass‘的ATL.Registrar子键,在ATL.Registrar子键下添加了默认值为{44EC053A-400F-11D0-9DCD-00A0C90391D3}的CLSID子键。
在HKCR主键下添加了CLSID子键,设定属性为NoRemove,表示反注册时,不删除该子键,在该子键下添加默认值为‘ATLRegistrarClass的{44EC053A-400F-11D0-9DCD-00A0C90391D3}子键,设定属性为ForceRemove,表示注册时,如果存在该子键,则首先将该子键下的所有的子键与键值都删除,再重新建立。在{44EC053A-400F-11D0-9DCD-00A0C90391D3}子键下添加了默认值‘ATL.Registrar‘的子键ProgID与默认值为‘%MODULE%‘的InprocServer32子键(%MODULE%会自动释义为模块的全路径),在InprocServer32子键下添加键值对‘Apartment‘的ThreadingModel。
注意一个注册脚本的大小为4K!!!
1.1.4 可替换参数
%MODULE% ——Com服务的实际路径(dll或exe的路径)
如:
‘MySampleKey‘ =s ‘%MODULE%,1‘
在调用注册脚本前,需要建立可替换参数MODULE的映射,调用AddReplacement添加映射,如下:
C++ Code
1 2 3 |
TCHAR szModule[_MAX_PATH]; ::GetModuleFileName(_AtlBaseModule.GetModuleInstance(), szModule, _MAX_PATH); p->AddReplacement(OLESTR("Module"), T2OLE(szModule)); |
这样,在运行时,MODULE会自动释义为Com服务的全路径。
注意:
为了替换可替换参数的值,需要脚本中移除DECLARE_REGISTRY_RESOURCE 和DECLARE_REGISTRY_RESOURCEID宏。使用自定义的UpdateRegistry方法调用CAtlModule::UpdateRegistryFromResourceD或CAtlModule::UpdateRegistryFromResourceS,传入类型为_ATL_REGMAP_ENTRY结构体的数组(至少有一个元素设为{NULL,NULL},且为最后一个元素)否则调用UpdateRegistryFromResource将产生错误。
当创建的是可执行的exe服务时,使用可替换参数%MODULE%,运行时会自动的在映射的路径上添加引用,如果你不想要,可以使用%MODULE_RAW%替换%MODULE%;如果创建的是DLL,则不会添加引用。
1.2 调用注册脚本
使用API调用注册脚本很简单,这里简单介绍:
方法 |
详细解释 |
ResourceRegister |
HRESULT ResourceRegister(LPCOLESTR resFileName ,UINT nID ,LPCOLESTR szType ); 注册脚本包含在资源文件中.resFileName表示模块路径,nID和szType表示资源的ID与类型。 |
ResourceUnregister |
HRESULT ResourceUnregister(LPCOLESTR resFileName ,UINT nID ,LPCOLESTR szType ); |
ResourceRegisterSz |
HRESULT ResourceRegisterSz(LPCOLESTR resFileName ,LPCOLESTR szID ,LPCOLESTR szType ); szID包含资源字符串的标识 |
ResourceUnregisterSz |
HRESULT ResourceUnregisterSz(LPCOLESTR resFileName ,LPCOLESTR szID ,LPCOLESTR szType ); |
FileRegister |
HRESULT FileRegister(LPCOLESTR fileName );注册脚本包含在文件中 |
FileUnregister |
HRESULT FileUnregister(LPCOLESTR fileName ); |
StringRegister |
HRESULT StringRegister(LPCOLESTR data ); 注册脚本包含在字符串中 |
StringUnregister |
HRESULT StringUnregister(LPCOLESTR data ); |
1.3 完整测试用例
前面介绍的都是基于ATL向导生成的工程,来操作注册表,完成Com组件的注册,很简单,没有什么技术含量,实用性不强。那如果非ATL向导生成的工程也想使用rgs注册脚本文件进行注册表的操作可以吗?答案是肯定的!
本测试用例就是基于win32控制台应用程序,通过rgs注册脚本文件,操作注册表。
有以下几个步骤:
【1】项目属性要设置使用ATL
【2】添加rgs注册脚本文件,设置注册的内容
C++ Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
HKEY_CURRENT_USER { NoRemove ‘MySOFTWARE‘ = s ‘My Software Test‘ { NoRemove ‘VisualStudio‘ { NoRemove ‘9.0‘ { ForceRemove ‘CppClean.Connect‘ { val LoadBehavior = d 0 val CommandLineSafe = d 0 val FriendlyName = s ‘CppClean AddIn‘ } } } val DllPath = s ‘%Module%‘ } } |
【3】编写操作rgs文件的代码
C++ Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
#ifndef _AFXSTD_091159A6_4CF3_4FD0_87B6_8411D7A2F123_INCLUDE_H_ #define _AFXSTD_091159A6_4CF3_4FD0_87B6_8411D7A2F123_INCLUDE_H_ #if _MSC_VER > 1000 #pragma once #endif #include "afxwin.h" #include "atlbase.h" #include "statreg.h" #define ATL_NO_ASSERT_ON_DESTROY_NONEXISTENT_WINDOW #define BREAK_ON_FAIL(value) if (FAILED(value)){break;} #define BREAK_ON_NULL(value, newHr) if (NULL == value){hr = newHr; break;} class CRegOper { public: CRegOper(HRESULT *pHr) { ::CoInitialize(NULL); HRESULT hr = CoCreateInstance(CLSID_Registrar, NULL, CLSCTX_INPROC_SERVER, IID_IRegistrar, (void**)&m_pRegistrar); if (NULL != pHr) { *pHr = hr; } } virtual ~CRegOper() { m_pRegistrar = NULL; ::CoUninitialize(); } public: //添加可替换参数映射 HRESULT AddReplacement(LPCOLESTR lpszKey, LPCOLESTR lpszItem) { HRESULT hr = S_OK; do { BREAK_ON_NULL(m_pRegistrar, E_POINTER); hr = m_pRegistrar->AddReplacement(lpszKey, lpszItem); } while (false); return(hr); } //rgs文件注册,传入rgs文件的全路径 HRESULT FileRegister(PCWSTR pRegFilePath) { HRESULT hr = S_OK; do { BREAK_ON_NULL(m_pRegistrar, E_POINTER); hr = m_pRegistrar->FileRegister(pRegFilePath); } while (false); return(hr); } //rgs文件反注册,传入rgs文件的全路径 HRESULT FileUnregister(PCWSTR pRegFilePath) { HRESULT hr = S_OK; do { BREAK_ON_NULL(m_pRegistrar, E_POINTER); hr = m_pRegistrar->FileUnregister(pRegFilePath); } while (false); return(hr); } private: CComPtr m_pRegistrar; }; #endif // !_AFXSTD_091159A6_4CF3_4FD0_87B6_8411D7A2F123_INCLUDE_H_ |
【4】执行代码
C++ Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
#include "stdafx.h" #include "RegOper.h" int _tmain(int argc, _TCHAR* argv[]) { HRESULT hr = S_OK; CRegOper regOper(&hr); if (FAILED(hr)) { return(1); } TCHAR szModule[_MAX_PATH] = {0}; USES_CONVERSION; GetModuleFileName(NULL, szModule, _MAX_PATH); hr = regOper.AddReplacement(OLESTR("Module"), T2OLE(szModule)); if (SUCCEEDED(hr)) { hr = regOper.FileRegister(L"RegScript.rgs"); } return 0; } |
【5】执行结果
【6】源码下载地址
ATL使用.rgs注册脚本文件操作注册表注册Com组件