我实现了一个自我升级过程,其中我的主应用程序 exe 启动一个更新程序 exe,在命令行上将句柄传递给自身。然后,应用程序 exe 调用 ExitProcess 退出,更新程序在传入的句柄上调用 WaitForSingleObject 来等待应用程序 exe 终止。
WaitForSingleObject 确实会等待。在应用程序调用 ExitProcess 之前,更新程序会停止。
但是,有时,当更新程序尝试使用新版本覆盖应用程序 dll 时,我会收到文件锁定错误,当前版本的更新程序将其视为不可恢复的错误并终止。似乎包含任意 sleep(100) 就足以绕过这个“问题”,但我真的很讨厌这样的代码。真的很讨厌。
在我看来很奇怪的是,当主应用程序仍然处于活动状态以锁定 dll 文件时,进程句柄可以发出信号。
当应用程序代码退出时,进程会发出信号。 操作系统可能需要更多时间才能完全卸载该进程。 发信号的要点是说“我已经完成了需要做的事情”,释放其他可能有真正有用的东西的代码比让该代码在操作系统执行一些内务处理时等待更有效。
正如另一个答案指出的那样,当进程停止执行时,进程句柄会收到信号,并且操作系统可能需要更长的时间来释放 DLL。
你是对的,依赖 Sleep(100) 是一个坏主意。你应该像这样在循环中覆盖你的 DLL:
BOOL UpdateDll(LPCTSTR dll_name, WHATEVER whatever) {
int tries = 150;
while (tries--) {
if (TryUpdateDll(dll_name, whatever))
return TRUE;
Sleep(200);
}
return FALSE;
}
这会持续尝试卸载 DLL 30 秒,然后放弃。即使系统负载较重,30 秒也应该足够了,但仍然可以防止您的更新程序永远挂起。 (如果 UpdateDll 返回 FALSE,请务必向用户显示有意义的错误消息,说明有问题的 DLL 的名称。)
如果您正在使用 COM,在退出之前调用 CoFreeUnusedLibraries 也可能会有所帮助。 (http://msdn.microsoft.com/en-us/library/ms679712.aspx) 坦率地说,我不知道 COM 是否可能在进程退出后保留 DLL,但最好保持安全。
最重要的是,Win32 API 中有很多奇怪的地方。只要您能找到可接受的解决方案,您不必处理所有情况。显然 Sleep(100) 可能会中断,但 30 秒的轮询循环对我来说似乎可以接受。
可能 DLL 当时被其他进程锁定。测试此问题的一种方法是生成一份报告,说明发生这种情况时 DLL 上持有的内容。
大约六个月前,用一些防病毒软件看到了一系列的皮疹。尝试不使用 AV,至少确保 AV 是最新的。
如果您正在使用的任何 DLL 使用线程,如果您没有显式卸载它们,它们可能无法足够快地终止(或加入)——如果您使用 LoadLibrary 显式加载它们,当然会发生这种情况
看这里: http://msdn.microsoft.com/en-us/library/ms682596(VS.85).aspx
具体来说这一行:
...DLL 被卸载时 进程终止或调用 FreeLibrary功能及参考 计数变为零。如果过程 终止作为结果 终止进程或终止线程 函数,系统不会调用 DLL 入口点函数。
可能为您提供修复...虽然您无法替换正在使用的 dll,但您可以重命名它。因此,如果您有一个需要替换的 dll,但由于某种原因正在使用它,请将其重命名为 .delete 或类似的名称。进行更新,然后让您的主程序搜索任何 .delete 文件并在启动时删除它们。
-don
另一种方法是使用 A/B 更新机制,您始终保留安装的软件的先前版本,并在更新完成后交换快捷方式等以指向新版本。
(如果文件夹A是最后一个工作版本,请安装到文件夹B,然后将快捷方式指向文件夹B中的exe。同样,对于文件夹B是最后一个工作版本,擦除并覆盖文件夹A,然后安装在那里,并交换快捷方式.)
这对于大型软件可能不实用,但它可以确保您始终安装一个功能版本,并避免此类问题。