MFC实现软件版本更新(二)

一、主要功能

自动更新程序Update.exe需要实现以下功能:

  1. 检测待更新的程序进程是否完全退出
  2. 检测本地是否存在配置文件'VersionInfo.xml'和已经下载下来的服务端配置文件'ServerVersionInfo.xml'
  3. 解析'ServerVersionInfo.xml',获取文件下载URL和MD5值,并将其存入Map中
  4. 遍历Map在子线程中开始进行下载,同时在dialog上显示下载进度
  5. 在下载时,还要使用MD5取值函数,判断本地与服务端的文件是否重合,以及下载过程是否存在丢包问题
  6. 下载完成后,将'ServerVersionInfo.xml'中的版本号、更新日期、作者覆盖到'VersionInfo.xml'
  7. 删除'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

三、下载链接

https://download.csdn.net/download/Completits_/89101049

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Completits_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值