一、主要功能
自动更新程序Update.exe需要实现以下功能:
- 检测待更新的程序进程是否完全退出
- 检测本地是否存在配置文件'VersionInfo.xml'和已经下载下来的服务端配置文件'ServerVersionInfo.xml'
- 解析'ServerVersionInfo.xml',获取文件下载URL和MD5值,并将其存入Map中
- 遍历Map在子线程中开始进行下载,同时在dialog上显示下载进度
- 在下载时,还要使用MD5取值函数,判断本地与服务端的文件是否重合,以及下载过程是否存在丢包问题
- 下载完成后,将'ServerVersionInfo.xml'中的版本号、更新日期、作者覆盖到'VersionInfo.xml'
- 删除'ServerVersionInfo.xml',退出自动更新程序,同时启动主程序
项目结构:
其中,tinyXML是第三方解析xml的库,需要自己下载。
二、实现代码
MD5计算:
Hash.h
#pragma once
#include <afx.h>
#include <afxtempl.h>
#include <afxmt.h>
#include <atlenc.h>
#include <atlstr.h>
#include <iostream>
#include <iomanip>
class CHash
{
public:
CHash();
virtual ~CHash();
CString CalculateMD5(const CString& filePath);
private:
CString ByteArrayToString(const BYTE* byteArray, DWORD size);
};
Hash.cpp
#include "pch.h"
#include "Hash.h"
#include <Windows.h>
#include <wincrypt.h>
#pragma comment(lib, "crypt32.lib")
CHash::CHash()
{
}
CHash::~CHash()
{
}
CString CHash::CalculateMD5(const CString& filePath)
{
HCRYPTPROV hProv;
HCRYPTHASH hHash;
BYTE rgbFile[1024];
DWORD cbRead = 0;
DWORD cbHash = 0;
BYTE rgbHash[16];
CString md5Result;
if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
{
AfxMessageBox(_T("CryptAcquireContext failed."));
return _T("");
}
if (!CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash))
{
AfxMessageBox(_T("CryptCreateHash failed."));
CryptReleaseContext(hProv, 0);
return _T("");
}
HANDLE hFile = CreateFile(filePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
AfxMessageBox(_T("Failed to open file."));
CryptDestroyHash(hHash);
CryptReleaseContext(hProv, 0);
return _T("");
}
while (ReadFile(hFile, rgbFile, sizeof(rgbFile), &cbRead, NULL) && cbRead != 0)
{
if (!CryptHashData(hHash, rgbFile, cbRead, 0))
{
AfxMessageBox(_T("CryptHashData failed."));
CryptDestroyHash(hHash);
CryptReleaseContext(hProv, 0);
CloseHandle(hFile);
return _T("");
}
}
cbHash = 16;
if (CryptGetHashParam(hHash, HP_HASHVAL, rgbHash, &cbHash, 0))
{
md5Result = ByteArrayToString(rgbHash, cbHash);
}
else
{
AfxMessageBox(_T("CryptGetHashParam failed."));
}
CryptDestroyHash(hHash);
CryptReleaseContext(hProv, 0);
CloseHandle(hFile);
return md5Result;
}
CString CHash::ByteArrayToString(const BYTE* byteArray, DWORD size)
{
CString strResult;
for (DWORD i = 0; i < size; i++)
{
CString strTemp;
strTemp.Format(_T("%02x"), byteArray[i]);
strResult += strTemp;
}
return strResult;
}
dialog绑定的类
UpdateDlg.h
#pragma once
#include "afxcmn.h"
#include <vector>
#include <map>
#define WM_THREAD_UPDATE_END (WM_USER + 101)
#define WM_UPDATE_END (WM_USER + 100)
class CUpdateDlgAutoProxy;
// CUpdateDlg 对话框
class CUpdateDlg : public CDialog
{
DECLARE_DYNAMIC(CUpdateDlg);
friend class CUpdateDlgAutoProxy;
// 构造
public:
CUpdateDlg(CWnd* pParent = nullptr); // 标准构造函数
virtual ~CUpdateDlg();
// 对话框数据
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_UPDATE_DIALOG };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
// 实现
protected:
CUpdateDlgAutoProxy* m_pAutoProxy;
HICON m_hIcon;
BOOL CanExit();
// 生成的消息映射函数
virtual BOOL OnInitDialog();
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
afx_msg void OnClose();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
virtual void OnOK();
virtual void OnCancel();
DECLARE_MESSAGE_MAP()
public:
struct FileData {
CString url;
CString hash;
};
struct UpdateInfo {
CString xmlUrl;
CString mainver;
CString verdate;
CString author;
}m_Info;
std::map<CString, FileData> m_mapFile;//保存需要更新的文件信息
CListCtrl m_listFile;
afx_msg LRESULT OnUpDateEndMsg(WPARAM wParam, LPARAM lParam);
//自动更新线程
static UINT ThreadUpdate(LPVOID pParam);
//自动更新逻辑
void UpdateThread();
//解析配置文件
void getUpdateFile();
BOOL CheckFileMd5(CString &strSavePath, CString &filemd);
LRESULT DownloadFile(UINT nNumber, CString &strSavePath, CString &strUrl);
bool IsProcessRunning(const wchar_t* processName);
void CloseProcess(const wchar_t* processName);
};
UpdateDlg.cpp
// UpdateDlg.cpp: 实现文件
//
#include "pch.h"
#include "framework.h"
#include "Update.h"
#include "UpdateDlg.h"
#include "DlgProxy.h"
#include "afxdialogex.h"
#include "tinyxml.h"
#include <WinBase.h>
#include <afxinet.h>
#include <Windows.h>
#include <tlhelp32.h>
#include <iostream>
#include "io.h"
#include "Hash.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
#define SERVERXMLNAME "ServerVersionInfo.xml"
#define CLIENTXMLNAME "VersionInfo.xml"
#define MAIN_PROGRAM "Inspect3D.exe"
// CUpdateDlg 对话框
IMPLEMENT_DYNAMIC(CUpdateDlg, CDialog);
CUpdateDlg::CUpdateDlg(CWnd* pParent /*=nullptr*/)
: CDialog(IDD_UPDATE_DIALOG, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
m_pAutoProxy = nullptr;
}
CUpdateDlg::~CUpdateDlg()
{
// 如果该对话框有自动化代理,则
// 此对话框的返回指针为 null,所以它知道
// 此代理知道该对话框已被删除。
if (m_pAutoProxy != nullptr)
m_pAutoProxy->m_pDialog = nullptr;
}
void CUpdateDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Control(pDX, IDC_LIST_FILE, m_listFile);
}
BEGIN_MESSAGE_MAP(CUpdateDlg, CDialog)
ON_WM_CLOSE()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_WM_SYSCOMMAND()
ON_MESSAGE(WM_THREAD_UPDATE_END, OnUpDateEndMsg)
END_MESSAGE_MAP()
// CUpdateDlg 消息处理程序
BOOL CUpdateDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标
// TODO: 在此添加额外的初始化代码
m_listFile.InsertColumn(0, _T("文件名"), LVCFMT_LEFT, 100, 0);
m_listFile.InsertColumn(1, _T("大小"), LVCFMT_LEFT, 90, 0);
m_listFile.InsertColumn(2, _T("进度"), LVCFMT_LEFT, 100, 0);
m_listFile.SetExtendedStyle(LVS_EX_FLATSB | LVS_EX_FULLROWSELECT
| LVS_EX_HEADERDRAGDROP | LVS_EX_ONECLICKACTIVATE | LVS_EX_GRIDLINES);
//此处先检测Inspect3D.exe是否完全关闭
// 最大尝试次数
const int MAX_TRIES = 5;
// 每次尝试之间的等待时间(单位:毫秒)
const DWORD RETRY_INTERVAL_MS = 1000;
// 检测并关闭进程
if (IsProcessRunning(_T(MAIN_PROGRAM)))
{
// 循环等待并检测进程关闭
int tries = 0;
while (tries < MAX_TRIES && IsProcessRunning(_T(MAIN_PROGRAM)))
{
// 等待一段时间再进行下一次检测
Sleep(RETRY_INTERVAL_MS);
tries++;
}
// 再次确认进程是否仍在运行
if (IsProcessRunning(_T(MAIN_PROGRAM)))
{
AfxMessageBox(_T("无法关闭" MAIN_PROGRAM ",请手动关闭"));
return FALSE;
}
}
AfxBeginThread(ThreadUpdate, this);
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
// 如果向对话框添加最小化按钮,则需要下面的代码
// 来绘制该图标。 对于使用文档/视图模型的 MFC 应用程序,
// 这将由框架自动完成。
void CUpdateDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // 用于绘制的设备上下文
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// 使图标在工作区矩形中居中
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// 绘制图标
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CUpdateDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
// 当用户关闭 UI 时,如果控制器仍保持着它的某个
// 对象,则自动化服务器不应退出。 这些
// 消息处理程序确保如下情形: 如果代理仍在使用,
// 则将隐藏 UI;但是在关闭对话框时,
// 对话框仍然会保留在那里。
void CUpdateDlg::OnClose()
{
if (CanExit())
CDialog::OnClose();
}
void CUpdateDlg::OnOK()
{
if (CanExit())
CDialog::OnOK();
}
void CUpdateDlg::OnCancel()
{
if (CanExit())
CDialog::OnCancel();
}
BOOL CUpdateDlg::CanExit()
{
// 如果代理对象仍保留在那里,则自动化
// 控制器仍会保持此应用程序。
// 使对话框保留在那里,但将其 UI 隐藏起来。
if (m_pAutoProxy != nullptr)
{
ShowWindow(SW_HIDE);
return FALSE;
}
return TRUE;
}
void CUpdateDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
CDialog::OnSysCommand(nID, lParam);
}
LRESULT CUpdateDlg::OnUpDateEndMsg(WPARAM wParam, LPARAM lParam)
{
wchar_t pBuf[MAX_PATH]; // 使用宽字符数组存储路径名
GetCurrentDirectoryW(MAX_PATH, pBuf); // 获取当前目录
wcscat_s(pBuf, _T("\\" CLIENTXMLNAME)); // 拼接出本地配置文件完整路径
TRACE(pBuf);
CString strFileName_xml = pBuf;
if (_waccess(strFileName_xml, 0) == -1) // 检查配置文件是否存在
{
AfxMessageBox(_T(CLIENTXMLNAME "文件不存在!"));
return -1;
}
TRACE("\n %s\n", strFileName_xml);
std::shared_ptr<TiXmlDocument> doc(new TiXmlDocument);
if (!doc->LoadFile(CStringA(strFileName_xml))) {
AfxMessageBox(_T("加载" CLIENTXMLNAME "文件失败!"));
return -2;
}
TiXmlElement* root = doc->RootElement();
if (!root) {
AfxMessageBox(_T(CLIENTXMLNAME "文件格式错误!"));
return -3;
}
auto setText = [](TiXmlElement* element, const char* name, const CString& value) {
TiXmlElement* childElement = element->FirstChildElement(name);
if (childElement) {
TiXmlNode* textNode = childElement->FirstChild();
TRACE("%s\n", childElement->GetText());
if (textNode) {
TiXmlText* text = textNode->ToText();
if (text) {
text->SetValue(CStringA(value));
}
}
}
};
TiXmlElement* clientElement = root->FirstChildElement("Client");
if (!clientElement) {
AfxMessageBox(_T(CLIENTXMLNAME "文件缺少Client节点!"));
return -4;
}
setText(clientElement, "XMLURL", m_Info.xmlUrl);
setText(clientElement, "MAINVER", m_Info.mainver);
setText(clientElement, "VERDATE", m_Info.verdate);
setText(clientElement, "AUTHOR", m_Info.author);
//将修改的数据写入xml文件
if (!doc->SaveFile(CLIENTXMLNAME)) {
AfxMessageBox(_T("保存版本信息失败!"));
return -5;
}
// 删除下载的配置文件 ServerVersionInfo.xml
strFileName_xml.Replace(_T(CLIENTXMLNAME), _T(SERVERXMLNAME));
if (!DeleteFile(strFileName_xml))
{
AfxMessageBox(_T("删除" SERVERXMLNAME "配置文件失败!"));
return -6;
}
int result = AfxMessageBox(_T("即将退出更新程序,是否运行" MAIN_PROGRAM), MB_YESNO);
if (result == IDNO) {
// 用户点击了“否”按钮
ExitProcess(0);
}
else {
// 启动主程序 Inspect3D.exe
CString strExeName = strFileName_xml;
strExeName.Replace(_T(SERVERXMLNAME), _T(MAIN_PROGRAM));
HINSTANCE hID = ShellExecute(NULL, _T("open"), strExeName, NULL, NULL, SW_SHOWNORMAL);
if (hID == NULL || hID == INVALID_HANDLE_VALUE)
{
AfxMessageBox(_T("未找到主程序!请确保主程序命名为" MAIN_PROGRAM "并放置在正确的路径下。"));
return -7;
}
// 关闭本程序
ExitProcess(0);
return 0;
}
}
// 实现静态成员函数 ThreadUpdate
UINT CUpdateDlg::ThreadUpdate(LPVOID pParam)
{
//wchar_t buf[MAX_PATH];
//GetCurrentDirectoryW(MAX_PATH, buf); // 获取当前目录
//wcscat_s(buf, _T("\\" MAIN_PROGRAM)); // 拼接出Inspect3D.exe完整路径
//TRACE(buf);
CUpdateDlg* pDlg = (CUpdateDlg*)pParam;
ASSERT(pDlg);
pDlg->UpdateThread();
// 通知主线程更新完成
pDlg->PostMessage(WM_THREAD_UPDATE_END);
return 0;
}
void CUpdateDlg::UpdateThread() {
BOOL bIsSuccess = TRUE;
// 首先获取需要更新的文件信息列表
getUpdateFile();
// 刷新列表框
m_listFile.DeleteAllItems();
CString strUrl, strName, strTemp, strMd;
FileData f;
int nNumber = 0;
for (auto it = m_mapFile.begin(); it != m_mapFile.end(); ++it) {
strName = it->first;
f = it->second;
strUrl = f.url;
strMd = f.hash;
// 在列表框中添加文件信息
m_listFile.InsertItem(nNumber, _T(""));
m_listFile.SetItemText(nNumber, 0, strName);
m_listFile.SetItemText(nNumber, 1, _T("0 kb"));
m_listFile.SetItemText(nNumber, 2, _T("已下载 0 %"));
++nNumber;
}
// 下载文件并检查MD5值
nNumber = 0;
for (auto it = m_mapFile.begin(); it != m_mapFile.end(); ++it) {
strName = it->first;
f = it->second;
strUrl = f.url;
strMd = f.hash;
TRACE(strName + L"\n" + strUrl + L"\n" + strMd + L"\n");
// 对比文件的MD5 true为重复文件
if (!CheckFileMd5(strName, strMd)) {
if (DownloadFile(nNumber, strName, strUrl) != 0) {
m_listFile.SetItemText(nNumber, 2, _T("下载失败"));
bIsSuccess = FALSE;
}
}
else {
m_listFile.SetItemText(nNumber, 2, _T("已经最新"));
}
++nNumber;
}
// 所有文件下载成功
if (bIsSuccess) {
::PostMessage(m_hWnd, WM_UPDATE_END, 0, 0); // 发送消息进行自动退出并打开主程序
}
}
BOOL CUpdateDlg::CheckFileMd5(CString &strSavePath, CString &filemd) {
// 获取当前目录路径
TCHAR szBuffer[MAX_PATH];
GetCurrentDirectory(MAX_PATH, szBuffer);
// 拼接完整的文件路径
CString strFileName;
strFileName.Format(_T("%s\\%s"), szBuffer, strSavePath);
CHash md5;
// 检查文件是否存在
if (PathFileExists(strFileName)) {
// 计算文件的 MD5 值
CString fmd5 = md5.CalculateMD5(strFileName);
// 比较文件的 MD5 值与提供的 MD5 值是否相等
if (filemd == fmd5) {
return TRUE; // MD5 值相等,文件未被篡改
}
}
return FALSE; // 文件不存在或者 MD5 值不匹配
}
void CUpdateDlg::getUpdateFile()
{
CString strServerXmlName;
wchar_t pBuf[MAX_PATH]; // 存储路径名
GetCurrentDirectoryW(MAX_PATH, pBuf); // 获取当前目录
wcscat_s(pBuf, _T("\\" SERVERXMLNAME)); // 拼接出配置文件完整路径
TRACE("%s\n", pBuf);
strServerXmlName = pBuf;
if (_waccess(strServerXmlName, 0) == -1) // 使用 _waccess 函数检查配置文件是否存在
{
AfxMessageBox(_T(SERVERXMLNAME "文件不存在!"));
exit(0);
//return ;
}
TRACE(strServerXmlName);
std::shared_ptr<TiXmlDocument> doc(new TiXmlDocument);
if (!doc->LoadFile(CStringA(strServerXmlName))) {
AfxMessageBox(_T("加载XML文件失败!"));
exit(0);
//return ;
}
auto pRootElement = doc->RootElement();
auto pServerElement = pRootElement->FirstChildElement("Server");
auto getText = [](TiXmlElement* element, const char* name) -> const char* {
auto childElement = element->FirstChildElement(name);
if (childElement) {
const char* text = childElement->GetText();
TRACE("Text content of <%s>: %s\n", name, text ? text : "NULL");
return text ? text : "";
}
else {
TRACE("Element <%s> not found!\n", name);
return "";
}
};
auto xmlUrl = getText(pServerElement, "XMLURL");
auto mainver = getText(pServerElement, "MAINVER");
auto verdate = getText(pServerElement, "VERDATE");
auto author = getText(pServerElement, "AUTHOR");
auto fileCount = getText(pServerElement, "FILECOUNT");
TRACE("%s\n", xmlUrl);
TRACE("%s\n", mainver);
TRACE("%s\n", verdate);
TRACE("%s\n", author);
TRACE("%s\n", fileCount);
m_Info = { CString(xmlUrl),CString(mainver) ,CString(verdate) ,CString(author) };
auto pFilesElement = pServerElement->FirstChildElement("FILES");
if (pFilesElement) {
for (auto pFileElement = pFilesElement->FirstChildElement("FILE"); pFileElement; pFileElement = pFileElement->NextSiblingElement("FILE")) {
auto filename = getText(pFileElement, "FILENAME");
auto fileurl = getText(pFileElement, "FILEURL");
auto filehash = getText(pFileElement, "FILEHASH");
TRACE("%s\n", filename);
TRACE("%s\n", filehash);
TRACE("%s\n", fileurl);
//CString str = CString(fileurl) + CString(",") + CString(filehash);
//TRACE(str);
FileData f = { CString(fileurl) ,CString(filehash) };
m_mapFile[CString(filename)] = f;
}
}
}
LRESULT CUpdateDlg::DownloadFile(UINT nNumber, CString & strSavePath, CString & strUrl)
{
// 检查传入的两个参数
if (strUrl.IsEmpty())
return -5;
if (strSavePath.IsEmpty())
return -6;
// 判断文件路径是否存在,不存在则创建
CString fileDir;
TCHAR Buffer[MAX_PATH];
GetCurrentDirectory(MAX_PATH, Buffer);
fileDir.Format(_T("%s\\%s"), Buffer, strSavePath.Left(strSavePath.ReverseFind(_T('/'))));
if (!PathIsDirectory(fileDir)) {
CreateDirectory(fileDir, NULL);
}
// 解析URL,获取信息
unsigned short nPort;
CString strServer, strObject;
DWORD dwServiceType;
if (!AfxParseURL(strUrl, dwServiceType, strServer, strObject, nPort)) {
return -1; // 解析失败,该Url不正确
}
// 创建网络连接对象,HTTP连接对象指针和用于该连接的HttpFile文件对象指针
CInternetSession intsess;
CHttpFile* pHtFile = NULL;
CHttpConnection* pHtCon = NULL;
intsess.SetOption(INTERNET_OPTION_CONNECT_TIMEOUT, 1000 * 20); // 连接超时
intsess.SetOption(INTERNET_OPTION_CONNECT_BACKOFF, 1000); // 两次重试之间的间隔时间
intsess.SetOption(INTERNET_OPTION_CONNECT_RETRIES, 1); // 2次重试
intsess.SetOption(INTERNET_OPTION_SEND_TIMEOUT, 6000); // 发送请求的超时时间
intsess.SetOption(INTERNET_OPTION_RECEIVE_TIMEOUT, 6000); // 接受数据的超时时间
try {
// 建立网络连接
pHtCon = intsess.GetHttpConnection(strServer, nPort);
if (pHtCon == NULL) {
// 建立网络连接失败
intsess.Close();
return -2;
}
// 发起GET请求
pHtFile = pHtCon->OpenRequest(CHttpConnection::HTTP_VERB_GET, strObject);
if (pHtFile == NULL) {
// 发起GET请求失败
intsess.Close();
delete pHtCon;
pHtCon = NULL;
return -3;
}
// 提交请求
pHtFile->SendRequest();
// 获取服务器返回的状态号
DWORD dwRet;
pHtFile->QueryInfoStatusCode(dwRet);
if (dwRet != HTTP_STATUS_OK) {
// 服务器不接受请求
intsess.Close();
delete pHtCon;
pHtCon = NULL;
delete pHtFile;
pHtFile = NULL;
return -4;
}
}
catch (CInternetException* e) {
e->Delete();
intsess.Close();
delete pHtCon;
pHtCon = NULL;
delete pHtFile;
pHtFile = NULL;
return -2;
}
// 获取文件大小并刷新列表框显示
CString szSize;
pHtFile->QueryInfo(HTTP_QUERY_CONTENT_LENGTH, szSize);
UINT nFileSize = _ttoi(szSize);
CString strFileSize;
if (nFileSize < 1024) {
strFileSize.Format(_T("%d b"), nFileSize);
m_listFile.SetItemText(nNumber, 1, strFileSize);
}
else if (nFileSize > 1024 && nFileSize < 1024 * 1024) {
strFileSize.Format(_T("%d kb"), nFileSize / 1024);
m_listFile.SetItemText(nNumber, 1, strFileSize);
}
else {
double dbFileSize = (double)nFileSize;
strFileSize.Format(_T("%.2lf MB"), dbFileSize / (1024 * 1024));
m_listFile.SetItemText(nNumber, 1, strFileSize);
}
// 创建缓冲区
CHAR* szBuffer = new CHAR[nFileSize + 1];
TRY{
UINT nCompletedSize = 0;
int nCircle = 0;
DWORD dwRead = 0;
CFile PicFile(strSavePath, CFile::modeCreate | CFile::modeWrite );
PicFile.SetLength(0); // 清空文件内容
while (nCompletedSize < nFileSize) {
// 清空缓冲区
memset(szBuffer, 0, nFileSize + 1);
// 读取到缓冲区
dwRead = pHtFile->Read(szBuffer, nFileSize);
// 写入到文件
PicFile.Write(szBuffer, dwRead);
nCompletedSize += dwRead;
if (nCircle % 10 == 0) {
int n = (int)(nCompletedSize * 100 / nFileSize);
CString strCompletedSize;
strCompletedSize.Format(_T("已下载 %d %%"), n);
m_listFile.SetItemText(nNumber, 2, strCompletedSize);
}
nCircle++;
}
// 关闭文件
PicFile.Close();
// 释放内存
delete[] szBuffer;
delete pHtFile;
delete pHtCon;
// 关闭网络连接
intsess.Close();
m_listFile.SetItemText(nNumber, 2, _T("已完成下载"));
}
CATCH(CFileException, e) {
// 释放内存
delete[] szBuffer;
delete pHtFile;
delete pHtCon;
// 关闭网络连接
intsess.Close();
return -7; // 读写文件异常
}
END_CATCH
return 0;
}
bool CUpdateDlg::IsProcessRunning(const wchar_t* processName) {
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapshot == INVALID_HANDLE_VALUE) {
return false;
}
PROCESSENTRY32 pe;
pe.dwSize = sizeof(PROCESSENTRY32);
if (!Process32First(hSnapshot, &pe)) {
CloseHandle(hSnapshot);
return false;
}
do {
if (_wcsicmp(pe.szExeFile, processName) == 0) {
CloseHandle(hSnapshot);
return true;
}
} while (Process32Next(hSnapshot, &pe));
CloseHandle(hSnapshot);
return false;
}
void CUpdateDlg::CloseProcess(const wchar_t* processName) {
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapshot == INVALID_HANDLE_VALUE) {
return;
}
PROCESSENTRY32 pe;
pe.dwSize = sizeof(PROCESSENTRY32);
if (!Process32First(hSnapshot, &pe)) {
CloseHandle(hSnapshot);
return;
}
do {
if (_wcsicmp(pe.szExeFile, processName) == 0) {
HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pe.th32ProcessID);
if (hProcess != NULL) {
TerminateProcess(hProcess, 0);
CloseHandle(hProcess);
}
}
} while (Process32Next(hSnapshot, &pe));
CloseHandle(hSnapshot);
}
dialog页面:
listControl的view属性需要设置成report