优化以前的代码,让使用者更方便
一丶 优化思路
1.我们可以将我们写的GUID(类工厂的ID)保存到注册表中,并且保存一下DLL的文件路径,遍历注册表去DLL路径即可.
2.每个类工厂我们就要使用一个GUID,而我们就要写到注册表中GUID
注册表在系统的文件夹下:
C:\ WINDWOS \ System32\config下面,当然这个使我们不能删除的,也不能查看的.我们需要调用API来操作
如果要查看,可以运行CMD命令:
regedit命令来查看注册表
二丶插件注册
问题一:
当我们要把类工厂的ID写到注册表中,但是DLL的路径我们不知道,这些都是动态写入的,所以我们需要利用插件注册模式
问题二:
我们要怎么注册,要写到哪里合适,要怎么注册
1.我们要写到的是注册表的
计算机HKEY_CLASSES_ROOTCLSID 里面,其中项的名称就是类工厂的GUID
2.注册要调用系统的注册工具,我们需要实现3个API供他调用
HRESULT __stdcall DllCanUnloadNow() /*能否卸载*/
HRESULT __stdcall DllRegisterServer() /*是否注册*/
HRESULT __stdcall DllUnregisterServer() /*卸载插件*/
HRESULT __stdcall DllGetClassObject(const GUID& clsid, const GUID& riid, void **ppObject)(其中这个就是COM前边讲的根据类ID返回类工厂)
三丶设计注册表存储
上面已经准备好了,我们就要设计注册表了,然后依次写入到注册表
const char* g_szRegTable[][3] ={
{"CLSID\{450A883B-F00A-46b3-AF3C-EC559997396A}", 0, "SuperMath" /*你的类工厂的GUID*/
},
{ "CLSID\{450A883B-F00A-46b3-AF3C-EC559997396A}\InprocServer32",
0 ,
(const char*)-1(代表你可以动态的写入DLL路径)
}, { "CLSID\{450A883B-F00A-46b3-AF3C-EC559997396A}\ 这个是可选的,因为你的GUID要存储进去,当我们取出来的时候要根据GUID查找,很麻烦,所以定义怎么一个文件夹,里面存放的就是GUID,可以直接通过他来获得GUID", 0, "SuperMathsrv.SuperMath.1"
(
}, { "CLSID\{450A883B-F00A-46b3-AF3C-EC559997396A}\TypeLib", 0, "{450A883B-F00A-46b3-AF3C-EC559997396A}"
你要包含的头文件的路径,放在Typelib里面
}, { "SuperMathsrv.SuperMath", 0, "SuperMath"
先定义一个目录,下面取消注册,删除插件的时候需要删除这个文件夹(也就是注册表的一项)
}, { "SuperMathsrv.SuperMath\CLSID", 0, "{450A883B-F00A-46b3-AF3C-EC559997396A}"
写入类工厂的GUID
}, { "SuperMathsrv.SuperMath\CurVer", 0, "SuperMathsrv.SuperMath.1"
写入类工厂的版本号 },
{ "SuperMathsrv.SuperMath.1", 0, "" }, { "SuperMathsrv.SuperMath.1\CLSID", 0, "{450A883B-F00A-46b3-AF3C-EC559997396A}" }, };
上面是一个二维数组里面保存了要创建注册表的信息
详解二维数组里面的文件夹:
1.SuperMath 你的类工厂的名字,要创建怎么一个文件夹
2.InprocServer32 这个是注册表标准的文件夹,这里面的默认项填写的是当前COM(也就是DLL)的路径
3.ProID 可选写入 版本
4.TypeLib 这个是使用者需要用#include,所以我们把他的路径也封装一下
5. 这个是可选的,因为你的GUID要存储进去,当我们取出来的时候要根据GUID查找,很麻烦,所以定义怎么一个文件夹,里面存放的就是GUID,可以直接通过他来获得GUID
这个是可选的,因为你的GUID要存储进去,当我们取出来的时候要根据GUID查找,很麻烦,所以定义怎么一个文件夹,里面存放的就是GUID,可以直接通过他来获得GUID,需要先定义怎么一个文件夹,我们删除的时候使用
最后一个GUID,写入的GUID
四丶在插件注册标准的函数中写注册表
1 for (int i = 0; i < sizeof(g_szRegTable) / sizeof(g_szRegTable[0]); i++) 2 { 3 const char* pszKey = g_szRegTable[i][0]; /*找到数组的第第0项里面以为数组的第0项的值*/ 4 const char* pszValueName = g_szRegTable[i][1];/*一次类推*/ 5 const char* pszValue = g_szRegTable[i][2]; 6 char szModuleName[MAX_PATH]; 7 8 HKEY hKey; 9 if (RegCreateKeyEx(HKEY_CLASSES_ROOT, /*写入注册表*/ 10 pszKey, NULL, NULL, 11 REG_OPTION_VOLATILE, 12 KEY_ALL_ACCESS, 13 NULL, 14 &hKey, 15 NULL) != ERROR_SUCCESS) 16 return S_FALSE; 17 18 if (pszValue == (const char*)-1)/*判断里面的内容是否是这个(主要是第二次循环要动态写入DLL路径)*/ 19 { 20 GetModuleFileName(g_hInstan, szModuleName, sizeof(szModuleName));
/*获得DLL路径写入,g_Hinstan是从DLLmain里面保存的,是一个全局变量,因为要获取当前DLL的模块*/ 21 pszValue = szModuleName; 22 } 23 24 if (RegSetValueEx(hKey, pszValueName, NULL, REG_SZ, /*写入值*/ 25 (BYTE*)pszValue, strlen(pszValue)) != ERROR_SUCCESS) 26 return S_FALSE; 27 28 RegCloseKey(hKey); /*关闭Key*/ 29 } 30 31 return S_OK;
卸载的时候需要反向卸载
1 HRESULT __stdcall DllUnregisterServer() 2 { 3 int nCount = sizeof(g_szRegTable) / sizeof(g_szRegTable[0]);/*获得大小*/ 4 for (int i = nCount - 1; i >= 0; i--) 5 { 6 const char* pszKey = g_szRegTable[i][0];/*获得第x项的第0个下标(一维数组的第一项)的内容*/ 7 if (RegDeleteKey(HKEY_CLASSES_ROOT, pszKey) != ERROR_SUCCESS) 8 return S_FALSE; /*删除*/ 9 } 10 11 12 return S_OK; 13 }
解决全新的字符串格式
我们的COM组件要跨平台使用,所以这个时候不得不考虑字符串了,C语言中的字符串是 结尾,而Pascal是前边是字符串的长度,后面是字符串
全新的字符串格式 BSTR 他是他们两个的组合 前边是长度,中间是字符串,后边是 结尾
而COM是C/c++程序员设计的,所以使用BSTR的时候,直接使用,他会默认帮我们移动到数据位,直到遇到 结尾
而Pascal想要使用,就要自己减去一个,得到长度,然后获得字符串,不考虑 结尾.