COM学习(二)——COM的注册和卸载

COM组件是跨语言的,组件被注册到注册表中,在加载时由加载函数在注册表中查找到对应模块的路径并进行相关加载。它的存储规则如下:
1. 在注册表的HKEY_CLASSES_ROOT中以模块名的方式保存着COM模块的GUID,比如HKEY_CLASSES_ROOT\ADODB.Error\CLSID键中保存着模块ADODB.Error的GUID为{00000541-0000-0010-8000-00AA006D2EA4}
2. 在HKEY_CLASSES_ROOT\CLSID中以GUID为项名保存着对应组件的详细信息,比如之前的{00000541-0000-0010-8000-00AA006D2EA4}这个GUID在注册表中的位置为HKEY_CLASSES_ROOT\CLSID\{00000541-0000-0010-8000-00AA006D2EA4}\InprocServer32\项的默认键中保存着模块所在路径为%CommonProgramFiles%\System\ado\msado15.dll
一般的COM模块都是使用regsvr32程序注册到注册表中,该程序在注册时会在模块中查找DllRegisterServer函数,卸载时调用模块中提供的DllUnregisterServer,所以要实现注册的功能主要需要实现这两个函数
这两个函数的原型如下:

STDAPI DllRegisterServer();
STDAPI DllUnregisterServer();

通过VS的F12功能查找STDAPI 的定义如下:

#define STDAPI  EXTERN_C HRESULT STDAPICALLTYPE

在查看STDAPICALLTYPE得到如下结果:

#define STDAPICALLTYPE          __stdcall

所以这个宏展开也就是

extern "C" HRESULT __stdcall DllRegisterServer();

为了实现注册功能,首先定义一个全局的变量,用来表示需要写入到注册表中的项

const TCHAR *g_regTable[][3] = {
        {_T("SOFTWARE\\ComDemo"), 0, _T("ComDemo")},
        {_T("SOFTWARE\\ComDemo\\InporcServer32"), 0, (const TCHAR*)-1}

这三项分别为注册表项,注册表项中的键名和键值,当键名为0时会创建一个默认的注册表键,最后一个-1我们会在程序中判断,如果键值为-1,那么值取为模块的路径
下面是注册的函数

STDAPI DllRegisterServer()
{
    HKEY hKey = NULL;
    TCHAR szFileName[MAX_PATH] = _T("");
    GetModuleFileName(g_hDllIns, szFileName, MAX_PATH);

    int nCount = sizeof(g_regTable) / sizeof(*g_regTable);
    for (int i = 0; i < nCount; i++)
    {
        LPCTSTR pszKeyName = g_regTable[i][0];
        LPCTSTR pszValueName = g_regTable[i][1];
        LPCTSTR pszValue = g_regTable[i][2];
        if (pszValue == (const TCHAR*)-1)
        {
            pszValue = szFileName;
        }

        long err = RegCreateKey(HKEY_LOCAL_MACHINE, pszKeyName, &hKey);
        if (err != ERROR_SUCCESS)
        {
            return SELFREG_E_LAST;
        }

        err = RegSetValueEx(hKey, pszValueName, 0, REG_SZ, (const BYTE*)pszValue, _tcslen(pszValue) * sizeof(TCHAR));
        if (err != ERROR)
        {
            return SELFREG_E_LAST;
        }

        RegCloseKey(hKey);
    }

    return S_OK;
}

在程序中会循环读取上述全局变量中的值,将值保存到注册表中,在上面的代码中有一句sizeof(g_regTable) / sizeof(*g_regTable);
这个是算需要循环多少次,第一个sizeof得到的是这个二维数组的总大小。在C语言中我们说二维数组可以看做是由一维数组组成的,这个二维数组可以看成是由两个一维数组——一个由3个const TCHAR *成员组成的一维数组组成。所以*g_regTab自然就是这个一维数组的首地址,第二个sizeof就是这个一维数组的大小,两个相除得到的就是一维数组的个数。
卸载函数如下:

STDAPI DllUnregisterServer()
{
    int nCount = sizeof(g_regTable) / sizeof(*g_regTable);
    for (int i = nCount - 1; i >= 0; i--)
    {
        LPCTSTR pszKeyName = g_regTable[i][0];
        long err = RegDeleteKey(HKEY_LOCAL_MACHINE, pszKeyName);
        if (err != ERROR_SUCCESS)
        {
            return SELFREG_E_LAST;
        }
    }

    return S_OK;
}

至此已经实现注册和卸载函数。后面就可以直接使用regsvr32这个程序进行注册和卸载了.

posted @ 2017-10-24 20:55  masimaro  阅读(943)  评论(0编辑  收藏  举报