C++实现com组件每天单次检查更新(Win32 Atom表)
使用场景:window下com组件(ocx/atldll)会被多个应用程序调用,但是只能确保每天只会执行一次检查更新
实现思路:
1.因为com组件涉及多进程调用,所以会有多个进程都需要检查是否当天已经执行更新,所以选择com组件的入口函数中执行,确保系统中不管哪个进程调用哪个方法都会检查更新逻辑
2.实现单次更新可以使用配置文件来控制,但是多次读取配置文件操作必然会影响性能,所以我选择Windows系统的Atom来实现,并且是在系统中作唯一标识,所以使用全局原子
3.原理就是当第一个进程在调用com的时候会先使用全局Atom进行标记,其他进程在调用之后会检查系统中是否存在这个标记,如果存在该标记就不执行更新逻辑,否则就执行
代码实现:
- 在com返回类工厂的函数中添加Atom标记逻辑
std::string GetCurrentDateString() {
// 获取当前时间 注意只能到d
std::time_t t = std::time(nullptr);
std::tm now;
localtime_s(&now, &t);
char buffer[11];
std::strftime(buffer, sizeof(buffer), "%Y-%m-%d", &now);
return std::string(buffer);
}
STDAPI DllGetClassObject(_In_ REFCLSID rclsid, _In_ REFIID riid, _Outptr_ LPVOID* ppv)
{
std::string dateString = GetCurrentDateString();
ATOM existingAtom = GlobalFindAtomA(dateString.c_str());
// 添加全局原子
GlobalFindAtom(lpString);
// existrigAtom 为0时就是不存在
if (existingAtom == 0) {
ATOM atom = GlobalAddAtomA(lpString);
std::cout << << "标记添加成功"<< std::endl;
// UpdateTest()
// 代表第一次添加Atom标识,所以执行一次检查更新逻辑
} else {
std::cerr << "已经存在标记" << std::endl;
}
return _AtlModule.DllGetClassObject(rclsid, riid, ppv);
}
- 更新逻辑的实现
- 一般软件的更新都会停机更新,因为文件占用的时候更新会导致数据丢失或异常 ,但是本次实现com组件的不停机更新,原理是通过修改ocx或者atldll的名字来实现。利用的是文件在占用的时候可以修改名字的特点来实现
- 具体就是在com组件被加载时,给需要替换的文件添加一个新的后缀,再把新的文件(名字需要保持和原来注册的com组件一致)复制到当前目录,这样当进程第二次加载时就是加载的新的com组件,并且不会影响当前正在使用的com组件,因为Windows每个进程都会拷贝一份注册的com组件在各自的进程中运行
更新逻辑实现:
void UpdateTest(){
// 比如之前com名字是 test1.ocx
// 使用rename更改文件名
int ret = rename("test1.ocx","test1.ocx.old"); //此时更改名字不会影响已经加载的正常进程
if(ret!=0){
// 失败
return ;
}
// 如果成功就将新的com组件移动到com组件的注册路径
// D:/old/test1.ocx 是新的文件路径
if (rename("D:/old/test1.ocx", "C:/pag/test1.ocx") == 0) {
// 更新成功
//也可以选择删除 test1.ocx.old
}
}