|
VC++MFC自动更新程序中实现文件替换,可采用以下三种主流方案
************方案1:批处理脚本替换(推荐)************通过生成临时批处理脚本实现异步替换,避免文件占用冲突
- void CAutoUpdater::CreateReplaceScript(const CString& strTargetPath) {
- CString strBatPath = GetTempPath() + _T("replace.bat");
- HANDLE hFile = CreateFile(strBatPath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
-
- CString strCmd;
- strCmd.Format(_T("@echo off\r\n")
- _T("timeout /t 3 /nobreak >nul\r\n") // 等待主程序退出
- _T("move /y "%s" "%s"\r\n") // 替换文件
- _T("start "" "%s"\r\n") // 启动新程序
- _T("del "%%~f0"\r\n"), // 自删除脚本
- m_strTempPath, strTargetPath, strTargetPath);
-
- DWORD dwWritten;
- WriteFile(hFile, strCmd, strCmd.GetLength() * sizeof(TCHAR), &dwWritten, NULL);
- CloseHandle(hFile);
-
- ShellExecute(NULL, _T("open"), strBatPath, NULL, NULL, SW_HIDE);
- }
复制代码 ************方案2:独立更新程序************分离更新逻辑到独立进程,通过进程间通信控制替换流程: - 主程序检测到更新后启动Updater.exe
- 主程序退出后,Updater.exe完成文件替换
- Updater.exe启动新版本主程序后自退出
以下是独立更新程序Updater.exe的完整实现方案,包含进程通信和文件替换逻辑
Updater.h
- #pragma once
- #include <vector>
- class CUpdater {
- public:
- BOOL Init(int argc, TCHAR* argv[]);
- BOOL PerformUpdate();
- private:
- BOOL WaitForProcessExit(DWORD dwPID);
- BOOL ReplaceFiles();
- BOOL StartNewVersion();
-
- CString m_strMainExePath;
- CString m_strUpdateDir;
- DWORD m_dwMainPID;
- std::vector<CString> m_vecReplaceFiles;
- };
复制代码 Updater.cpp
- "stdafx.h"
- #include "Updater.h"
- #include <tlhelp32.h>
- BOOL CUpdater::Init(int argc, TCHAR* argv[]) {
- if(argc < 4) return FALSE;
-
- m_dwMainPID = _ttoi(argv[1]);
- m_strMainExePath = argv[2];
- m_strUpdateDir = argv[3];
-
- WIN32_FIND_DATA fd;
- HANDLE hFind = FindFirstFile(m_strUpdateDir + _T("\\*.*"), &fd);
- while(hFind != INVALID_HANDLE_VALUE) {
- if(!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
- m_vecReplaceFiles.push_back(fd.cFileName);
- }
- if(!FindNextFile(hFind, &fd)) break;
- }
- FindClose(hFind);
-
- return !m_vecReplaceFiles.empty();
- }
- BOOL CUpdater::WaitForProcessExit(DWORD dwPID) {
- HANDLE hProcess = OpenProcess(SYNCHRONIZE, FALSE, dwPID);
- if(hProcess) {
- WaitForSingleObject(hProcess, 10000); // 最多等待10秒
- CloseHandle(hProcess);
- }
- return TRUE;
- }
- BOOL CUpdater::ReplaceFiles() {
- for(auto& file : m_vecReplaceFiles) {
- CString src = m_strUpdateDir + _T("\") + file;
- CString dest = m_strMainExePath.Left(m_strMainExePath.ReverseFind('\\') + 1) + file;
-
- if(!MoveFileEx(src, dest, MOVEFILE_REPLACE_EXISTING|MOVEFILE_WRITE_THROUGH)) {
- return FALSE;
- }
- }
- return TRUE;
- }
- BOOL CUpdater::StartNewVersion() {
- SHELLEXECUTEINFO sei = {0};
- sei.cbSize = sizeof(sei);
- sei.lpFile = m_strMainExePath;
- sei.nShow = SW_SHOW;
- return ShellExecuteEx(&sei);
- }
- BOOL CUpdater::PerformUpdate() {
- return WaitForProcessExit(m_dwMainPID) &&
- ReplaceFiles() &&
- StartNewVersion();
- }
复制代码 UpdaterApp.cpp
- "stdafx.h"
- #include "Updater.h"
- int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) {
- CUpdater updater;
- if(updater.Init(__argc, __targv)) {
- if(updater.PerformUpdate()) {
- return 0;
- }
- }
- return 1;
- }
复制代码代码说明: - 主程序调用方式:Updater.exe [主进程ID] [主程序路径] [更新文件目录]
- 使用WaitForSingleObject确保主程序退出
- 采用MOVEFILE_WRITE_THROUGH确保文件完全替换
- 更新完成后自动启动新版本
使用流程: - 主程序下载更新包到临时目录
- 启动Updater.exe并传入自身PID和路径
- 主程序立即退出
- Updater.exe完成文件替换后启动新版本
注意事项: - 需要管理员权限才能替换系统目录文件
- 建议添加文件校验逻辑确保完整性
- 生产环境应使用数字签名验证更新包
************方案3:Windows卷影复制************利用VSS服务实现文件热替换 - BOOL ReplaceWithShadowCopy(LPCTSTR lpszSrc, LPCTSTR lpszDest) {
- // 需要链接VSSAPI.LIB
- IVssBackupComponents* pVss = NULL;
- CoCreateInstance(CLSID_VssBackupComponents, NULL, CLSCTX_ALL,
- IID_IVssBackupComponents, (void**)&pVss);
- pVss->InitializeForBackup();
- pVss->SetContext(VSS_CTX_APP_ROLLBACK);
-
- // 创建卷影副本并执行文件替换...
- }
复制代码
关键注意事项- 权限处理:需请求管理员权限才能替换Program Files目录文件
- 版本回滚:应保留旧版本备份以便恢复
- 文件校验:替换前需验证MD5/SHA校验值
- 日志记录:记录替换操作结果便于故障排查
实际项目中推荐方案1+方案2组合使用,通过批处理完成简单替换,复杂场景使用独立更新程序
|
|