一个COM组件在使用前必须首先注册。所谓“注册”,也就是向系统注册表的相应位置写入一些数据。这些数据可以完成guid与Dll的绝对路径的一一对应,也就是说可以帮助程序通过guid找到Dll的位置。
GUID概念:
GUID(globally unique identifier)是一个128位的数。用于保证每一个接口和组件在时间和空间上都是全球唯一的一个标识符。为保证空间唯一性,根据机器上网卡的MAC地址再加上一定的算法生成的唯一的48位值序列;为保证时间上的唯一性,每个GUID值具有一个60位的时间戳。这个时间戳表示的是自1852年10月15号00:00:00以来以100纳秒为时间间隔的计数值。这样可以保证3400年GUID值仍然是唯一的。下面是GUID的定义和一个示例:
typedef struct _GUID {
unsigned long Data1;
unsigned short Data2;
unsigned short Data3;
unsigned char Data4[8];
} GUID;
// {364ede61-08ac-43ec-8861-15f5f9f4ced1}
微软提供了两个建立GUID的程序,一个时UUIDGEN.EXE,该程序是命令行方式的;另一个则是GUIDGEN.EXE,是一个示例性的VC++对话框应用。
DEFINE_GUID可以使用GUIDGEN.EXE来生成一个GUID。
注册表:
组件可以用CLSID作为索引在Windows的注册表中发布包含他们的DLL文件名称。CoCreateInstance将用CLSID作为关键字在注册表中查找所需要的文件名称。注
册表是一个由许多元素构成的层次结构。每一个元素均被称作是一个关键字。每一个关键字可以包含一系列子关键字、一系列命名的值及/或一个未命名的值。COM只
使用了注册表的一个分支:HKEY_CLASSES_ROOT;在此关键字之下,可以看到有一个CLSID关键字。在CLSID关键字之下列有系统中安装的所有组件的CLSID。对于
每一个CLSID关键字,我们现在关心的只是它的一个子关键字InprocServer32。此子关键字的缺省值是组件所在的Dll文件路径名称。另一个子关键字ProgID指的是程序
员给某个CLSID指定的一个程序员易记的名称。
如何注册COM组件:
由于DLL知道它所包含的组件,因此DLL可以完成这些信息的注册。在DLL中一定要处处如下两个函数:
DllRegisterServer();// 完成注册组件
DllUnRegisterServer();// 完成反注册组件
用户可以使用程序REGSVR32.EXE来注册某个组件。方法是使用命令行:regsvr32/u testDll.dll(反注册) regsvr32 testDll.dll(注册)。
一个典型的注册COM组件Dll必须导出如下五个函数:
1)DllMain:Dll的入口函数,完成一些Dll的初始化工作(DirectShow实现的是DllEntryPoint);
2)DllGetClassObject:用于获得类工厂指针;
3)DllCanUnloadNow: 系统空闲时会调用这个函数,以确定是否可以卸载Dll;
4)DllRegisterServer:将COM组件注册到注册表中;
5)DllUnregisterServer: 删除注册表中COM组件的注册信息。
所以,要想完成注册,关键就是对DllRegisterServer();DllUnRegisterServer();两个函数的实现,下面将详细介绍如何实现这两个函数:
//g_module为DLL的实例句柄
//CLSID_INNER_COM为组件的CLSID
//const char g_friend_name_inner [] = "inner_com_test";
//const char g_ver_indprog_id_inner [] = "inside.com.chapter.7.inner";
//const char g_prog_id_inner [] = "inside.com.chapter.7.inner.1";
STDAPI DllRegisterServer(void)
{
HRESULT hr = RegisterServer(g_module,CLSID_INNER_COM,
g_friend_name_inner,g_ver_indprog_id_inner,g_prog_id_inner);
return hr;
}
//Register the component in the registry
HRESULT RegisterServer(HMODULE hModule,
const CLSID& clsid,
const char* szFriendlyName,
const char* szVerIndProgID,
const char* szProgID)
{
//Get the Server location
char szModule[512];
DWORD dwResult = ::GetModuleFileName(hModule,szModule,sizeof(szModule)/sizeof(char));
assert(dwResult!=0);
//Convert the CLSID into a char
char szCLSID[CLSID_STRING_SIZE];
CLSIDtochar(clsid,szCLSID,sizeof(szCLSID));
//Build the key CLSID\\{}
char szKey[64];
strcpy(szKey,"CLSID\\");
strcat(szKey,szCLSID);
//Add the CLSID to the registry
setKeyAndValue(szKey,NULL,szFriendlyName);
//Add the Server filename subkey under the CLSID key
setKeyAndValue(szKey,"InprocServer32",szModule);
setKeyAndValue(szKey,"ProgID",szProgID);
setKeyAndValue(szKey,"VersionIndependentProgID",szVerIndProgID);
//Add the version-independent ProgID subkey under HKEY_CLASSES_ROOT
setKeyAndValue(szVerIndProgID,NULL,szFriendlyName);
setKeyAndValue(szVerIndProgID,"CLSID",szCLSID);
setKeyAndValue(szVerIndProgID,"CurVer",szProgID);
//Add the versioned ProgID subkey under HKEY_CLASSES_ROOT
setKeyAndValue(szProgID,NULL,szFriendlyName);
setKeyAndValue(szProgID,"CLSID",szCLSID);
return S_OK;
}
STDAPI DllUnregisterServer(void)
{
HRESULT hr = UnRegisterServer(CLSID_INNER_COM,g_ver_indprog_id_inner,g_prog_id_inner);
return hr;
}
//Remove the component from the register
HRESULT UnRegisterServer(const CLSID& clsid, // Class ID
const char* szVerIndProgID, // Programmatic
const char* szProgID) // IDs
{
//Convert the CLSID into a char.
char szCLSID[CLSID_STRING_SIZE];
CLSIDtochar(clsid,szCLSID,sizeof(szCLSID));
//Build the key CLSID\\{}
char szKey[64];
strcpy(szKey,"CLSID\\");
strcat(szKey,szCLSID);
//Delete the CLSID key - CLSID\{}
LONG lResult = recursiveDeleteKey(HKEY_CLASSES_ROOT,szKey);
assert((lResult == ERROR_SUCCESS) || (lResult == ERROR_FILE_NOT_FOUND));
//Delete the version-independent ProgID Key
lResult = recursiveDeleteKey(HKEY_CLASSES_ROOT,szVerIndProgID);
assert((lResult == ERROR_SUCCESS) || (lResult == ERROR_FILE_NOT_FOUND));
//Delete the ProgID key.
lResult = recursiveDeleteKey(HKEY_CLASSES_ROOT,szProgID);
assert((lResult == ERROR_SUCCESS) || (lResult == ERROR_FILE_NOT_FOUND));
return S_OK;
}