|
通过编程实践可以发现,如果通过ATL向导生成的COM,自动会生成DllRegisterServer及DllUnregisterServer函数,可供regsvr32等调用进行 注册与反注册。而如果通过普通的DLL向导并选择Automation支持,则只会自动生成DllRegisterServer,而没有DllUnregisterServer接口,因 此需要手工添加一个DllUnregisterServer函数。当试图在此函数中调用COleObjectFactory::UnregisterAll()进行反注册时,跟踪源代码可知 最终只是简单地返回了TRUE,所以根本没有反注册。 为了能够仿ATL实现COM的反注册,需要研究ATL注册/反注册源代码: STDAPI DllRegisterServer(void) { // registers object, typelib and all interfaces in typelib return _Module.RegisterServer(TRUE); } ///////////////////////////////////////////////////////////////////////////// // DllUnregisterServer - Removes entries from the system registry STDAPI DllUnregisterServer(void) { return _Module.UnregisterServer(TRUE); } _Module为一个CComModule的全局对象,就类似于CXXXApp的全局对象,上面两个函数是CComModule的成员函数,如下: HRESULT RegisterServer(BOOL bRegTypeLib = FALSE, const CLSID* pCLSID = NULL) { return AtlModuleRegisterServer(this, bRegTypeLib, pCLSID); } HRESULT UnregisterServer(BOOL bUnRegTypeLib, const CLSID* pCLSID = NULL) { return AtlModuleUnregisterServerEx(this, bUnRegTypeLib, pCLSID); } 由于注册与反注册是类似的,下面以普通支持自动化的DLL中没有的反注册功能为例 ATLINLINE ATLAPI AtlModuleUnregisterServerEx(_ATL_MODULE* pM, BOOL bUnRegTypeLib, const CLSID* pCLSID) { ATLASSERT(pM != NULL); if (pM == NULL) return E_INVALIDARG; ATLASSERT(pM->m_hInst != NULL); ATLASSERT(pM->m_pObjMap != NULL); _ATL_OBJMAP_ENTRY* pEntry = pM->m_pObjMap; for (;pEntry->pclsid != NULL; pEntry = _NextObjectMapEntry(pM, pEntry)) { if (pCLSID == NULL) { if (pEntry->pfnGetObjectDescription != NULL && pEntry->pfnGetObjectDescription() != NULL) continue; } else { if (!IsEqualGUID(*pCLSID, *pEntry->pclsid)) continue; } pEntry->pfnUpdateRegistry(FALSE); //unregister if (pM->cbSize == sizeof(_ATL_MODULE) && pEntry->pfnGetCategoryMap != NULL) AtlRegisterClassCategoriesHelper( *pEntry->pclsid, pEntry->pfnGetCategoryMap(), FALSE ); } if (bUnRegTypeLib) AtlModuleUnRegisterTypeLib(pM, 0); return S_OK; } 关键处是这句pEntry->pfnUpdateRegistry(FALSE); //unregister 那么pfnUpdateRegistry是哪来的呢?它是_ATL_OBJMAP_ENTRY中的一员:
struct _ATL_OBJMAP_ENTRY { const CLSID* pclsid; HRESULT (WINAPI *pfnUpdateRegistry)(BOOL bRegister); // ------------- 这里 _ATL_CREATORFUNC* pfnGetClassObject; _ATL_CREATORFUNC* pfnCreateInstance; IUnknown* pCF; DWORD dwRegister; _ATL_DESCRIPTIONFUNC* pfnGetObjectDescription; _ATL_CATMAPFUNC* pfnGetCategoryMap; HRESULT WINAPI RevokeClassObject() { return CoRevokeClassObject(dwRegister); } HRESULT WINAPI RegisterClassObject(DWORD dwClsContext, DWORD dwFlags) { IUnknown* p = NULL; if (pfnGetClassObject == NULL) return S_OK; HRESULT hRes = pfnGetClassObject(pfnCreateInstance, IID_IUnknown, (LPVOID*) &p); if (SUCCEEDED(hRes)) hRes = CoRegisterClassObject(*pclsid, p, dwClsContext, dwFlags, &dwRegister); if (p != NULL) p->Release(); return hRes; } // Added in ATL 3.0 void (WINAPI *pfnObjectMain)(bool bStarting); }; 这个结构是怎么构造起来的呢?ATL采用了类似MFC中构建message map及serialize的使用宏构建列表的方法: BEGIN_OBJECT_MAP/OBJECT_ENTRY/END_OBJECT_MAP #define BEGIN_OBJECT_MAP(x) static _ATL_OBJMAP_ENTRY x[] = { #define END_OBJECT_MAP() {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}}; #define OBJECT_ENTRY(clsid, class) {&clsid, class::UpdateRegistry, class::_ClassFactoryCreatorClass::CreateInstance, class::_CreatorClass::CreateInstance, NULL, 0, class::GetObjectDescription, class::GetCategoryMap, class::ObjectMain }, 这三个宏可以构建一个COM类静态链表(数组),链表的元素就是上面的_ATL_OBJMAP_ENTRY结构,它的第二个成员就是注册时被调用的pfnUpda teRegistry。 下面是一例: BEGIN_OBJECT_MAP(ObjectMap) OBJECT_ENTRY(CLSID_HtmlSelParse, CHtmlSelParse) END_OBJECT_MAP() 通过上面的分析可以知道,pfnUpdateRegistry = CHtmlSelParse::UpdateRegistry,那么UpdateRegistry又是什么呢?它是通过DECLARE_REGISTRY_RESOURCEID宏定义的一个静态函数: #define DECLARE_REGISTRY_RESOURCEID(x)\ static HRESULT WINAPI UpdateRegistry(BOOL bRegister)\ {\ return _Module.UpdateRegistryFromResource(x, bRegister);\ } 下面再来看UpdateRegistryFromResource: #ifdef _ATL_STATIC_REGISTRY #define UpdateRegistryFromResource UpdateRegistryFromResourceS #else #define UpdateRegistryFromResource UpdateRegistryFromResourceD #endif UpdateRegistryFromResourceS与UpdateRegistryFromResourceD分别为ATL静态链接与动态链接版本,下面看UpdateRegistryFromResourceD动 态链接版本: HRESULT WINAPI UpdateRegistryFromResourceD(LPCTSTR lpszRes, BOOL bRegister, struct _ATL_REGMAP_ENTRY* pMapEntries = NULL) { USES_CONVERSION; return AtlModuleUpdateRegistryFromResourceD(this, T2COLE(lpszRes), bRegister, pMapEntries); } 它调用的是AtlModuleUpdateRegistryFromResourceD,其位于ATLBASE.h中,下面是其原型: ATLINLINE ATLAPI AtlModuleUpdateRegistryFromResourceD(_ATL_MODULE* pM, LPCOLESTR lpszRes, BOOL bRegister, struct _ATL_REGMAP_ENTRY* pMapEntries, IRegistrar* pReg) 查看它的源代码就会发现,它调用的是IRegistrar::ResourceRegister/ResourceUnregister等进行注册与反注册。IRegistar是什么?它应该 是一个用来注册COM的Shell接口,具体可查看MSDN的“The ATL Registry Component (Registrar)”主题。由于到了这里不能看到它的源代码 了,所以不知道它的具体实现,但通过搜索ResourceRegister/ResourceUnregister,意外地发现它们也是另一个类CRegObject的成员函数。它 们最终调用的是CRegObject::RegisterFromResource,其中调用了CRegParser::RegisterBuffer,这个可以看作是注册与反注册的终极靶标了 。通过查看这个函数,而且根据前面的函数名及需要传递的资源ID,你会恍然大悟:它实际上是通过解析ATL向导生成的rgs文件实现注册与反 注册了,其根本操作就是添加或删除注册表项。这又回到了注册与反注册的最原始的方法了。 使用这种方法要求有一个rgs文件,并把它以资源方式添加到工程中,资源类型命名必须为"REGISTRY"(这是从函数的源代码中可以看到),函数 通过这个资源类型找到rgs文件。 关于rgs文件,下面是一小段解释:
HKCR { NoRemove txtfile { NoRemove ShellEx { NoRemove ContextMenuHandlers { ForceRemove SimpleShlExt = s '{5E2121EE-0300-11D4-8D3B-444553540000}' } } } } 每一行代表一个注册表键, "HKCR"是 HKEY_CLASSES_ROOT 的缩写. NoRemove 关键字表示当该COM服务器注销时该键 不用被删除. 最后一行有 些复杂. ForceRemove 关键字表示如果该键已存在, 那么在新键添加之前该键先应被删除. 这行脚本的余下部分指定一个字符串,它将被存为 SimpleShlExt 键的默认值. 下面是两种方式来实现普通支持Automation的DLL的反注册代码: 需要写一个rgs文件,并把它作为资源添加到工程中,假设资源类型为“REGISTRY”,ID为IDR_WFDOWNLOAD,其内容是类似下面的: HKCR { WFDownload.AddURL = s 'WFDownload.AddURL' { CLSID = s '{C7CFF70F-3F33-4F34-ACEF-CFCE14F1792D}' } NoRemove CLSID { ForceRemove {C7CFF70F-3F33-4F34-ACEF-CFCE14F1792D} = s 'WFDownload.AddURL' { ProgID = s 'WFDownload.AddURL' InprocServer32 = s '%MODULE%' } } } 方法一:利用IRegistrar实现反注册(代码摘抄自ATLBASE.h中的AtlModuleUpdateRegistryFromResourceD函数) STDAPI DllUnregisterServer(void) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); USES_CONVERSION; HRESULT hRes = S_OK; CComPtr<IRegistrar> p; hRes = CoCreateInstance(CLSID_Registrar, NULL, CLSCTX_INPROC_SERVER, IID_IRegistrar, (void**)&p); if (SUCCEEDED(hRes)) { TCHAR szModule[_MAX_PATH]; GetModuleFileName(AfxGetInstanceHandle(), szModule, _MAX_PATH); LPOLESTR pszModule; pszModule = T2OLE(szModule); int nLen = ocslen(pszModule); LPOLESTR pszModuleQuote = (LPOLESTR)alloca((nLen*2+1)*sizeof(OLECHAR)); CComModule::ReplaceSingleQuote(pszModuleQuote, pszModule); p->AddReplacement(OLESTR("Module"), pszModuleQuote); LPCOLESTR szType = OLESTR("REGISTRY"); LPCOLESTR lpszRes = (LPCOLESTR)MAKEINTRESOURCE(IDR_WFDOWNLOAD); if (HIWORD(lpszRes)==0) { hRes = p->ResourceUnregister(pszModule, ((UINT)LOWORD((DWORD)lpszRes)), szType); } else { hRes = p->ResourceUnregisterSz(pszModule, lpszRes, szType); } } return hRes; } 方法二:利用CRegObject STDAPI DllUnregisterServer(void) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); USES_CONVERSION; HRESULT hRes = S_OK; TCHAR szModule[_MAX_PATH]; GetModuleFileName(AfxGetInstanceHandle(), szModule, _MAX_PATH); LPOLESTR pszModule; pszModule = T2OLE(szModule); int nLen = ocslen(pszModule); LPOLESTR pszModuleQuote = (LPOLESTR)alloca((nLen*2+1)*sizeof(OLECHAR)); CComModule::ReplaceSingleQuote(pszModuleQuote, pszModule); LPCOLESTR szType = OLESTR("REGISTRY"); CRegObject objReg; objReg.AddReplacement(OLESTR("Module"), pszModule); hRes = objReg.ResourceUnregister(pszModule, IDR_WFDOWNLOAD, szType); return hRes; }
|