VC下一个通过HTTP协议下载的类(CHttpDownload)
作者:dozb
这个类是基于MFC的,是对 wininet 库的封装。
下面是类头文件:
HttpDownload.h
package com.ly.util; // HttpDownload.h: interface for the CHttpDownload class. // // #if !defined(AFX_HTTPDOWNLOAD_H__E4698E96_7AD2_4DF0_AB2D_B184E46B8784__INCLUDED_) #define AFX_HTTPDOWNLOAD_H__E4698E96_7AD2_4DF0_AB2D_B184E46B8784__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 const UINT WM_HTTPDOWNLOAD_THREAD_FINISHED = WM_APP + 81; const UINT WM_HTTPDOWNLOAD_FILESTATUS = WM_APP + 82; const UINT WM_HTTPDOWNLOAD_PERCENTAGE = WM_APP + 83; const UINT WM_HTTPDOWNLOAD_TIMELEFT = WM_APP + 84; const UINT WM_HTTPDOWNLOAD_STATUS = WM_APP + 85; const UINT WM_HTTPDOWNLOAD_TRANSFERRATE = WM_APP + 86; #define IDS_HTTPDOWNLOAD_FILESTATUS "%s from %s" #define IDS_HTTPDOWNLOAD_CONNECTED "Connected to %s" #define IDS_HTTPDOWNLOAD_RESOLVING_NAME "Resolving name: %s" #define IDS_HTTPDOWNLOAD_RESOLVED_NAME "Resolved name to %s" #define IDS_HTTPDOWNLOAD_CONNECTING "Connecting to %s" #define IDS_HTTPDOWNLOAD_REDIRECTING "Redirecting to %s" #define IDS_HTTPDOWNLOAD_GETTING_FILE_INFORMATION "Getting file information" #define IDS_HTTPDOWNLOAD_FAIL_PARSE_ERROR "An error occurred parsing the url: %s" #define IDS_HTTPDOWNLOAD_GENERIC_ERROR \ "An error occurred while attempting to download the file, Error:%s" #define IDS_HTTPDOWNLOAD_FAIL_CONNECT_SERVER \ "An error occurred connecting to the server, Error:%s" #define IDS_HTTPDOWNLOAD_BYTESPERSECOND "%s Bytes/Sec" #define IDS_HTTPDOWNLOAD_KILOBYTESPERSECOND "%s KB/Sec" #define IDS_HTTPDOWNLOAD_OK_TO_OVERWRITE \ "The file '%s' already exists.\nDo you want to replace it?" #define IDS_HTTPDOWNLOAD_FAIL_FILE_OPEN \ "An error occured while opening the file to be downloaded, Error:%s" #define IDS_HTTPDOWNLOAD_ABORTING_TRANSFER "Aborting transfer" #define IDS_HTTPDOWNLOAD_FAIL_FILE_SEEK \ "An error occurred while seeking to the end of the file to be downloaded, Error:%s" #define IDS_HTTPDOWNLOAD_INVALID_SERVER_RESPONSE \ "Failed to receive a valid response from the server, Error:%s" #define IDS_HTTPDOWNLOAD_INVALID_HTTP_RESPONSE \ "Failed to receive a valid HTTP response from the server, Response Code:%s" #define IDS_HTTPDOWNLOAD_ERROR_READFILE \ "An error occurred while downloading the file, Error:%s" #define IDS_HTTPDOWNLOAD_PERCENTAGE "%s% of %s Completed" #define IDS_HTTPDOWNLOAD_RETREIVEING_FILE "Retrieving the file" #define IDS_HTTPDOWNLOAD_OF "%s of %s" #define IDS_HTTPDOWNLOAD_SECONDS "%s sec" #define IDS_HTTPDOWNLOAD_MINUTES "%s min" #define IDS_HTTPDOWNLOAD_MINUTES_AND_SECONDS "%s min %s sec" #define IDS_HTTPDOWNLOAD_BYTES "%s Bytes" #define IDS_HTTPDOWNLOAD_KILOBYTES "%s KB" #define IDS_HTTPDOWNLOAD_MEGABYTES "%s MB" #define IDS_HTTPDOWNLOAD_TIMELEFT "%s (%s copied)" class CHttpDownload : public CObject { public: CHttpDownload(); virtual ~CHttpDownload(); public: //Enums enum ConnectionType { UsePreConfig, DirectToInternet, UseProxy, }; HWND m_hNotifyWnd; BOOL Init( HWND hNotifyWnd, CString sURLToDownload, CString sFileToDownloadInto, CString sProxyServer="", CString sProxyUserName="", CString sProxyPassword="", CString sHTTPUserName="", CString sHTTPPassword="", CHttpDownload::ConnectionType nConnectionType = CHttpDownload::UsePreConfig, BOOL bPromptFileOverwrite = FALSE, BOOL PromptForProxyDetails = FALSE, BOOL bPromptForHTTPDetails = FALSE, double dbLimit = 0, //For BANDWIDTH Throptling, The value in Bytes / Second to limit the connection to DWORD dwStartPos= 0 //Offset to resume the download at ); BOOL Start(); void Cancel(); void DisInit(); //Public Member variables CString m_sURLToDownload; CString m_sFileToDownloadInto; CString m_sProxyServer; CString m_sProxyUserName; CString m_sProxyPassword; CString m_sHTTPUserName; CString m_sHTTPPassword; CString m_sUserAgent; ConnectionType m_ConnectionType; BOOL m_bPromptFileOverwrite; BOOL m_bPromptForProxyDetails; BOOL m_bPromptForHTTPDetails; double m_dbLimit; //For BANDWIDTH Throptling, The value in Bytes / Second to limit the connection to DWORD m_dwStartPos; //Offset to resume the download at protected: LRESULT OnThreadFinished(WPARAM wParam, LPARAM lParam); //Methods static void CALLBACK _OnStatusCallBack(HINTERNET hInternet, DWORD dwContext, DWORD dwInternetStatus, LPVOID lpvStatusInformation, DWORD dwStatusInformationLength); static BOOL QueryStatusNumber(HINTERNET hInternet, DWORD dwFlag, DWORD& dwCode); static BOOL QueryStatusCode(HINTERNET hInternet, DWORD& dwCode); static BOOL QueryContentLength(HINTERNET hInternet, DWORD& dwCode); void OnStatusCallBack(HINTERNET hInternet, DWORD dwInternetStatus, LPVOID lpvStatusInformation, DWORD dwStatusInformationLength); static UINT _DownloadThread(LPVOID pParam); virtual BOOL OnSetOptions(); virtual void HandleThreadErrorWithLastError(char* nIDError, DWORD dwLastError=0); virtual void HandleThreadError(char* nIDError); virtual void DownloadThread(); virtual void SetPercentage(int nPercentage); virtual void SetTimeLeft(DWORD dwSecondsLeft, DWORD dwBytesRead, DWORD dwFileSize); virtual void SetStatus(const CString& sCaption); virtual void SetStatus(char * nID); virtual void SetStatus(char* nID, const CString& lpsz1); virtual void SetTransferRate(double KbPerSecond); virtual void UpdateControlsDuringTransfer(DWORD dwStartTicks, DWORD& dwCurrentTicks, DWORD dwTotalBytesRead, DWORD& dwLastTotalBytes, DWORD& dwLastPercentage, BOOL bGotFileSize, DWORD dwFileSize); protected: //Member variables CString m_sError; CString m_sServer; DWORD m_dwServiceType; CString m_sObject; CString m_sFilename; INTERNET_PORT m_nPort; HINTERNET m_hInternetSession; HINTERNET m_hHttpConnection; HINTERNET m_hHttpFile; BOOL m_bAbort; BOOL m_bSafeToClose; CFile m_FileToWrite; CWinThread* m_pThread; }; #endif // !defined(AFX_HTTPDOWNLOAD_H__E4698E96_7AD2_4DF0_AB2D_B184E46B8784__INCLUDED_)
下面是类的实现文件:
HttpDownload.cpp
// HttpDownload.cpp: implementation of the CHttpDownload class. // // #include "stdafx.h" #include "HttpDownload.h" #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif // // Construction/Destruction // CHttpDownload::CHttpDownload() { } CHttpDownload::~CHttpDownload() { } BOOL CHttpDownload::Init( HWND hNotifyWnd, CString sURLToDownload, CString sFileToDownloadInto, CString sProxyServer, CString sProxyUserName, CString sProxyPassword, CString sHTTPUserName, CString sHTTPPassword, CHttpDownload::ConnectionType nConnectionType, BOOL bPromptFileOverwrite, BOOL bPromptForProxyDetails, BOOL bPromptForHTTPDetails, double dbLimit, //For BANDWIDTH Throptling, The value in Bytes / Second to limit the connection to DWORD dwStartPos //Offset to resume the download at ) { m_hNotifyWnd = hNotifyWnd; m_sURLToDownload = sURLToDownload; m_sFileToDownloadInto = sFileToDownloadInto; m_sProxyServer = sProxyServer; m_sProxyUserName = sProxyUserName; m_sProxyPassword = sProxyPassword; m_sHTTPUserName = sHTTPUserName; m_sHTTPPassword = sHTTPPassword; m_ConnectionType = nConnectionType; m_bPromptFileOverwrite = bPromptFileOverwrite; m_bPromptForProxyDetails = bPromptForProxyDetails; m_bPromptForHTTPDetails = bPromptForHTTPDetails; m_dbLimit = dbLimit; m_dwStartPos = dwStartPos; m_hInternetSession = NULL; m_hHttpConnection = NULL; m_hHttpFile = NULL; m_bAbort = FALSE; m_bSafeToClose = FALSE; m_pThread = NULL; m_dwServiceType = 0; return TRUE; } LRESULT CHttpDownload::OnThreadFinished(WPARAM wParam, LPARAM lParam) { //It's now safe to close since the thread has signaled us m_bSafeToClose = TRUE; //Stop the animation // m_ctrlAnimate.Stop(); //If an error occured display the message box if (m_bAbort) { // EndDialog(IDCANCEL); } else if (wParam) { AfxMessageBox(m_sError); // EndDialog(IDCANCEL); } else { // EndDialog(IDOK); } PostMessage(m_hNotifyWnd,WM_HTTPDOWNLOAD_THREAD_FINISHED, wParam,lParam); return 0L; } BOOL CHttpDownload::Start() { //Validate the URL ASSERT(m_sURLToDownload.GetLength()); //Did you forget to specify the file to download if (!AfxParseURL(m_sURLToDownload, m_dwServiceType, m_sServer, m_sObject, m_nPort)) { //Try sticking "http://" before it m_sURLToDownload = _T("http://") + m_sURLToDownload; if (!AfxParseURL(m_sURLToDownload, m_dwServiceType, m_sServer, m_sObject, m_nPort)) { CString sMsg; sMsg.Format(_T("Failed to parse the URL: %s\n"), m_sURLToDownload); AfxMessageBox(sMsg); return FALSE; } } //Check to see if the file we are downloading to exists and if //it does, then ask the user if they were it overwritten CFileStatus fs; ASSERT(m_sFileToDownloadInto.GetLength()); BOOL bDownloadFileExists = CFile::GetStatus(m_sFileToDownloadInto, fs); if (bDownloadFileExists && m_dwStartPos == 0 && m_bPromptFileOverwrite) { CString sMsg; sMsg.Format(IDS_HTTPDOWNLOAD_OK_TO_OVERWRITE, m_sFileToDownloadInto); if (AfxMessageBox(sMsg, MB_YESNO) != IDYES) { TRACE(_T("Failed to confirm file overwrite, download aborted\n")); // EndDialog(IDCANCEL); return FALSE; } } //Try and open the file we will download into DWORD dwFileFlags = 0; if (bDownloadFileExists && (m_dwStartPos > 0)) dwFileFlags = CFile::modeCreate | CFile::modeNoTruncate | CFile::modeWrite | CFile::shareDenyWrite; else dwFileFlags = CFile::modeCreate | CFile::modeWrite | CFile::shareDenyWrite; if (!m_FileToWrite.Open(m_sFileToDownloadInto, dwFileFlags)) { CString sError; sError.Format(_T("%d"), ::GetLastError()); CString sMsg; sMsg.Format(IDS_HTTPDOWNLOAD_FAIL_FILE_OPEN, GetLastError()); AfxMessageBox(sMsg); //EndDialog(IDCANCEL); return FALSE; } else { //Seek to the end of the file try { m_FileToWrite.Seek(m_dwStartPos, CFile::begin); m_FileToWrite.SetLength(m_dwStartPos); } catch(CFileException* pEx) { CString sError; sError.Format(_T("%d"), pEx->m_lOsError); CString sMsg; sMsg.Format(IDS_HTTPDOWNLOAD_FAIL_FILE_SEEK, pEx->m_lOsError); AfxMessageBox(sMsg); // EndDialog(IDCANCEL); return FALSE; } } //Pull out just the filename component int nSlash = m_sObject.ReverseFind(_T('/')); if (nSlash == -1) nSlash = m_sObject.ReverseFind(_T('\\')); if (nSlash != -1 && m_sObject.GetLength() > 1) m_sFilename = m_sObject.Right(m_sObject.GetLength() - nSlash - 1); else m_sFilename = m_sObject; //Set the file status text CString sFileStatus; ASSERT(m_sObject.GetLength()); ASSERT(m_sServer.GetLength()); sFileStatus.Format(IDS_HTTPDOWNLOAD_FILESTATUS, m_sFilename, m_sServer); // m_ctrlFileStatus.SetWindowText(sFileStatus); SendMessage(m_hNotifyWnd,WM_HTTPDOWNLOAD_FILESTATUS,(WPARAM)&sFileStatus,0); //Spin off the background thread which will do the actual downloading m_pThread = AfxBeginThread(_DownloadThread, this, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED); if (m_pThread == NULL) { CString sMsg; sMsg.Format(_T("Failed to create download thread, dialog is aborting\n")); AfxMessageBox(sMsg); // EndDialog(IDCANCEL); return FALSE; } m_pThread->m_bAutoDelete = FALSE; m_pThread->ResumeThread(); return TRUE; } UINT CHttpDownload::_DownloadThread(LPVOID pParam) { //Convert from the SDK world to the C++ world CHttpDownload* pHttpDownload = (CHttpDownload*) pParam; ASSERT(pHttpDownload); pHttpDownload->DownloadThread(); return 0; } void CHttpDownload::SetPercentage(int nPercentage) { //Change the progress control // m_ctrlProgress.SetPos(nPercentage); //Change the caption text CString sPercentage; sPercentage.Format(_T("%d"), nPercentage); CString sCaption; sCaption.Format(IDS_HTTPDOWNLOAD_PERCENTAGE, sPercentage, m_sFilename); // SetWindowText(sCaption); SendMessage(m_hNotifyWnd,WM_HTTPDOWNLOAD_PERCENTAGE,(WPARAM)&sCaption,nPercentage); } void CHttpDownload::SetTimeLeft(DWORD dwSecondsLeft, DWORD dwBytesRead, DWORD dwFileSize) { CString sCopied; if (dwBytesRead < 1024) { CString sBytes; sBytes.Format(_T("%d"), dwBytesRead); sCopied.Format(IDS_HTTPDOWNLOAD_BYTES, sBytes); } else if (dwBytesRead < 1048576) { CString sKiloBytes; sKiloBytes.Format(_T("%0.1f"), dwBytesRead/1024.0); sCopied.Format(IDS_HTTPDOWNLOAD_KILOBYTES, sKiloBytes); } else { CString sMegaBytes; sMegaBytes.Format(_T("%0.2f"), dwBytesRead/1048576.0); sCopied.Format(IDS_HTTPDOWNLOAD_MEGABYTES, sMegaBytes); } CString sTotal; if (dwFileSize < 1024) { CString sBytes; sBytes.Format(_T("%d"), dwFileSize); sTotal.Format(IDS_HTTPDOWNLOAD_BYTES, sBytes); } else if (dwFileSize < 1048576) { CString sKiloBytes; sKiloBytes.Format(_T("%0.1f"), dwFileSize/1024.0); sTotal.Format(IDS_HTTPDOWNLOAD_KILOBYTES, sKiloBytes); } else { CString sMegaBytes; sMegaBytes.Format(_T("%0.2f"), dwFileSize/1048576.0); sTotal.Format(IDS_HTTPDOWNLOAD_MEGABYTES, sMegaBytes); } CString sOf; sOf.Format(IDS_HTTPDOWNLOAD_OF, sCopied, sTotal); CString sTime; if (dwSecondsLeft < 60) { CString sSeconds; sSeconds.Format(_T("%d"), dwSecondsLeft); sTime.Format(IDS_HTTPDOWNLOAD_SECONDS, sSeconds); } else { DWORD dwMinutes = dwSecondsLeft / 60; DWORD dwSeconds = dwSecondsLeft % 60; CString sSeconds; sSeconds.Format(_T("%d"), dwSeconds); CString sMinutes; sMinutes.Format(_T("%d"), dwMinutes); if (dwSeconds == 0) sTime.Format(IDS_HTTPDOWNLOAD_MINUTES, sMinutes); else sTime.Format(IDS_HTTPDOWNLOAD_MINUTES_AND_SECONDS, sMinutes, sSeconds); } CString sTimeLeft; sTimeLeft.Format(IDS_HTTPDOWNLOAD_TIMELEFT, sTime, sOf); // m_ctrlTimeLeft.SetWindowText(sTimeLeft); SendMessage(m_hNotifyWnd,WM_HTTPDOWNLOAD_TIMELEFT,(WPARAM)&sTimeLeft,0); } void CHttpDownload::SetStatus(const CString& sCaption) { // m_ctrlStatus.SetWindowText(sCaption); SendMessage(m_hNotifyWnd,WM_HTTPDOWNLOAD_STATUS,(WPARAM)&sCaption,0); } void CHttpDownload::SetStatus(char* nID) { CString sCaption; sCaption = nID; SetStatus(sCaption); } void CHttpDownload::SetStatus(char* nID, const CString& lpsz1) { CString sStatus; sStatus.Format(nID, lpsz1); SetStatus(sStatus); } void CHttpDownload::SetTransferRate(double KbPerSecond) { CString sRate; if (KbPerSecond < 1) { CString sBytesPerSecond; sBytesPerSecond.Format(_T("%0.0f"), KbPerSecond*1024); sRate.Format(IDS_HTTPDOWNLOAD_BYTESPERSECOND, sBytesPerSecond); } else if (KbPerSecond < 10) { CString sKiloBytesPerSecond; sKiloBytesPerSecond.Format(_T("%0.2f"), KbPerSecond); sRate.Format(IDS_HTTPDOWNLOAD_KILOBYTESPERSECOND, sKiloBytesPerSecond); } else { CString sKiloBytesPerSecond; sKiloBytesPerSecond.Format(_T("%0.0f"), KbPerSecond); sRate.Format(IDS_HTTPDOWNLOAD_KILOBYTESPERSECOND, sKiloBytesPerSecond); } // m_ctrlTransferRate.SetWindowText(sRate); SendMessage(m_hNotifyWnd,WM_HTTPDOWNLOAD_TRANSFERRATE,(WPARAM)&sRate,0); } void CHttpDownload::HandleThreadErrorWithLastError(char* nIDError, DWORD dwLastError) { //Form the error string to report CString sError; if (dwLastError) sError.Format(_T("%d"), dwLastError); else sError.Format(_T("%d"), ::GetLastError()); m_sError.Format(nIDError, sError); //Delete the file being downloaded to if it is present m_FileToWrite.Close(); ::DeleteFile(m_sFileToDownloadInto); OnThreadFinished( 1,0); } void CHttpDownload::HandleThreadError(char* nIDError) { m_sError = nIDError; OnThreadFinished( 1,0); } BOOL CHttpDownload::OnSetOptions() { //Do nothing but your derviced class could override this function and //do things ssuch as InternetSetOptions(m_hInternetSession, INTERNET_OPTION_CONNECT_RETRIES, ..) etc. return TRUE; //To continue processing } BOOL CHttpDownload::QueryStatusNumber(HINTERNET hInternet, DWORD dwFlag, DWORD& dwCode) { dwCode = 0; DWORD dwSize = sizeof(DWORD); return HttpQueryInfo(hInternet, dwFlag | HTTP_QUERY_FLAG_NUMBER, &dwCode, &dwSize, NULL); } BOOL CHttpDownload::QueryStatusCode(HINTERNET hInternet, DWORD& dwCode) { return QueryStatusNumber(hInternet, HTTP_QUERY_STATUS_CODE, dwCode); } BOOL CHttpDownload::QueryContentLength(HINTERNET hInternet, DWORD& dwCode) { return QueryStatusNumber(hInternet, HTTP_QUERY_CONTENT_LENGTH, dwCode); } void CHttpDownload::DownloadThread() { //Create the Internet session handle ASSERT(m_hInternetSession == NULL); switch (m_ConnectionType) { case UsePreConfig: { m_hInternetSession = ::InternetOpen(m_sUserAgent.GetLength() ? m_sUserAgent : AfxGetAppName(), INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); break; } case DirectToInternet: { m_hInternetSession = ::InternetOpen(m_sUserAgent.GetLength() ? m_sUserAgent : AfxGetAppName(), INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0); break; } case UseProxy: { ASSERT(m_sProxyServer.GetLength()); //You need to give me a proxy Server m_hInternetSession = ::InternetOpen(m_sUserAgent.GetLength() ? m_sUserAgent : AfxGetAppName(), INTERNET_OPEN_TYPE_PROXY, m_sProxyServer, NULL, 0); break; } default: { ASSERT(FALSE); break; } } if (m_hInternetSession == NULL) { TRACE(_T("Failed in call to InternetOpen, Error:%d\n"), ::GetLastError()); HandleThreadErrorWithLastError(IDS_HTTPDOWNLOAD_GENERIC_ERROR); return; } //Should we exit the thread if (m_bAbort) { OnThreadFinished(0,0); return; } //Setup the status callback function if (::InternetSetStatusCallback(m_hInternetSession, _OnStatusCallBack) == INTERNET_INVALID_STATUS_CALLBACK) { TRACE(_T("Failed in call to InternetSetStatusCallback, Error:%d\n"), ::GetLastError()); HandleThreadErrorWithLastError(IDS_HTTPDOWNLOAD_GENERIC_ERROR); return; } //Should we exit the thread if (m_bAbort) { OnThreadFinished(0,0); return; } //Make the connection to the HTTP server ASSERT(m_hHttpConnection == NULL); if (m_sHTTPUserName.GetLength()) m_hHttpConnection = ::InternetConnect(m_hInternetSession, m_sServer, m_nPort, m_sHTTPUserName, m_sHTTPPassword, INTERNET_SERVICE_HTTP, 0, (DWORD) this); else m_hHttpConnection = ::InternetConnect(m_hInternetSession, m_sServer, m_nPort, NULL, NULL, INTERNET_SERVICE_HTTP, 0, (DWORD) this); if (m_hHttpConnection == NULL) { TRACE(_T("Failed in call to InternetConnect, Error:%d\n"), ::GetLastError()); HandleThreadErrorWithLastError(IDS_HTTPDOWNLOAD_FAIL_CONNECT_SERVER); return; } //Should we exit the thread if (m_bAbort) { OnThreadFinished(0,0); return; } //Call the virtual function to allow session customisation if (!OnSetOptions()) { TRACE(_T("Failed in call to OnSetOptions\n")); return; } //Start the animation to signify that the download is taking place // PlayAnimation(); //Issue the request to read the file LPCTSTR ppszAcceptTypes[2]; ppszAcceptTypes[0] = _T("*/*"); //We support accepting any mime file type since this is a simple download of a file ppszAcceptTypes[1] = NULL; ASSERT(m_hHttpFile == NULL); DWORD dwFlags = INTERNET_FLAG_RELOAD | INTERNET_FLAG_DONT_CACHE | INTERNET_FLAG_PRAGMA_NOCACHE | INTERNET_FLAG_KEEP_CONNECTION; if (m_dwServiceType == AFX_INET_SERVICE_HTTPS) dwFlags |= (INTERNET_FLAG_SECURE | INTERNET_FLAG_IGNORE_CERT_CN_INVALID | INTERNET_FLAG_IGNORE_CERT_DATE_INVALID); m_hHttpFile = HttpOpenRequest(m_hHttpConnection, NULL, m_sObject, _T("HTTP/1.1"), NULL, ppszAcceptTypes, dwFlags, (DWORD) this); if (m_hHttpFile == NULL) { TRACE(_T("Failed in call to HttpOpenRequest, Error:%d\n"), ::GetLastError()); HandleThreadErrorWithLastError(IDS_HTTPDOWNLOAD_FAIL_CONNECT_SERVER); return; } //Should we exit the thread. //The purpose is to check if user has pressed the cancel button if (m_bAbort) { OnThreadFinished(0,0); return; } //label used to jump to if we need to resend the request resend: //Issue the request CString sRange; if (m_dwStartPos != 0) //we will build the range request. sRange.Format(_T("Range: bytes=%d-\r\n"), m_dwStartPos); BOOL bSend = FALSE; if (sRange.IsEmpty()) bSend = ::HttpSendRequest(m_hHttpFile, NULL, 0, NULL, 0); else bSend = ::HttpSendRequest(m_hHttpFile, sRange, sRange.GetLength(), NULL, 0); if (!bSend) { TRACE(_T("Failed in call to HttpSendRequest, Error:%d\n"), ::GetLastError()); HandleThreadErrorWithLastError(IDS_HTTPDOWNLOAD_FAIL_CONNECT_SERVER); return; } //Check the HTTP status code DWORD dwStatusCode = 0; if (!QueryStatusCode(m_hHttpFile, dwStatusCode)) { TRACE(_T("Failed in call to HttpQueryInfo for HTTP query status code, Error:%d\n"), ::GetLastError()); HandleThreadError(IDS_HTTPDOWNLOAD_INVALID_SERVER_RESPONSE); return; } else { //Handle any authentication errors if ((dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ) || (dwStatusCode == HTTP_STATUS_DENIED)) { // We have to read all outstanding data on the Internet handle // before we can resubmit request. Just discard the data. char szData[51]; DWORD dwSize = 0; do { ::InternetReadFile(m_hHttpFile, (LPVOID)szData, 50, &dwSize); } while (dwSize != 0); BOOL bPrompt = FALSE; if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ) { //Set the proxy details if we have them int nProxyUserLength = m_sProxyUserName.GetLength(); if (nProxyUserLength) { if (!InternetSetOption(m_hHttpFile, INTERNET_OPTION_PROXY_USERNAME, (LPVOID) m_sProxyUserName.operator LPCTSTR(), (nProxyUserLength+1) * sizeof(TCHAR))) TRACE(_T("Failed in call to InternetSetOption for Proxy Username, Error:%d\n"), ::GetLastError()); if (!InternetSetOption(m_hHttpFile, INTERNET_OPTION_PROXY_PASSWORD, (LPVOID) m_sProxyPassword.operator LPCTSTR(), (m_sProxyPassword.GetLength()+1) * sizeof(TCHAR))) TRACE(_T("Failed in call to InternetSetOption for Proxy Password, Error:%d\n"), ::GetLastError()); } else if (m_bPromptForProxyDetails) bPrompt = TRUE; } else if (dwStatusCode == HTTP_STATUS_DENIED) { //Set the proxy details if we have them int nHTTPUserLength = m_sHTTPUserName.GetLength(); if (nHTTPUserLength) { if (!InternetSetOption(m_hHttpFile, INTERNET_OPTION_USERNAME, (LPVOID) m_sHTTPUserName.operator LPCTSTR(), (nHTTPUserLength+1) * sizeof(TCHAR))) TRACE(_T("Failed in call to InternetSetOption for HTTP Username, Error:%d\n"), ::GetLastError()); if (!InternetSetOption(m_hHttpFile, INTERNET_OPTION_PASSWORD, (LPVOID) m_sHTTPPassword.operator LPCTSTR(), (m_sHTTPPassword.GetLength()+1) * sizeof(TCHAR))) TRACE(_T("Failed in call to InternetSetOption for HTTP Password, Error:%d\n"), ::GetLastError()); } else if (m_bPromptForHTTPDetails) bPrompt = TRUE; } //Bring up the standard authentication dialog if required if (bPrompt && ::InternetErrorDlg(m_hNotifyWnd, m_hHttpFile, ERROR_INTERNET_INCORRECT_PASSWORD, FLAGS_ERROR_UI_FILTER_FOR_ERRORS | FLAGS_ERROR_UI_FLAGS_GENERATE_DATA | FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS, NULL) == ERROR_INTERNET_FORCE_RETRY) goto resend; } else if (dwStatusCode != HTTP_STATUS_OK && dwStatusCode != HTTP_STATUS_PARTIAL_CONTENT) { TRACE(_T("Failed to retrieve a HTTP OK or partial content status, Status Code:%d\n"), dwStatusCode); HandleThreadErrorWithLastError(IDS_HTTPDOWNLOAD_INVALID_HTTP_RESPONSE, dwStatusCode); return; } } //Update the status control to reflect that we are getting the file information SetStatus(IDS_HTTPDOWNLOAD_GETTING_FILE_INFORMATION); // Get the length of the file. DWORD dwFileSize = 0; BOOL bGotFileSize = FALSE; if (QueryContentLength(m_hHttpFile, dwFileSize)) { //Set the progress control range bGotFileSize = TRUE; //m_ctrlProgress.SetRange(0, 100); } //Update the status to say that we are now downloading the file SetStatus(IDS_HTTPDOWNLOAD_RETREIVEING_FILE); //Now do the actual read of the file DWORD dwStartTicks = ::GetTickCount(); DWORD dwCurrentTicks = dwStartTicks; DWORD dwBytesRead = 0; char szReadBuf[1024]; DWORD dwBytesToRead = 1024; DWORD dwTotalBytesRead = 0; DWORD dwLastTotalBytes = 0; DWORD dwLastPercentage = 0; do { if (!::InternetReadFile(m_hHttpFile, szReadBuf, 1024, &dwBytesRead)) { TRACE(_T("Failed in call to InternetReadFile, Error:%d\n"), ::GetLastError()); HandleThreadErrorWithLastError(IDS_HTTPDOWNLOAD_ERROR_READFILE); return; } else { if (dwBytesRead && !m_bAbort) { //Write the data to file try { m_FileToWrite.Write(szReadBuf, dwBytesRead); } catch(CFileException* pEx) { TRACE(_T("An exception occured while writing to the download file\n")); HandleThreadErrorWithLastError(IDS_HTTPDOWNLOAD_ERROR_READFILE, pEx->m_lOsError); pEx->Delete(); return; } // For bandwidth throttling if (m_dbLimit > 0.0f) { double t = (double)(GetTickCount() - dwStartTicks); double q = (double)((double)dwTotalBytesRead / t); if (q > m_dbLimit) Sleep((DWORD)((((q*t)/m_dbLimit)-t))); } } //Increment the total number of bytes read dwTotalBytesRead += dwBytesRead; //Update the UI UpdateControlsDuringTransfer(dwStartTicks, dwCurrentTicks, dwTotalBytesRead, dwLastTotalBytes, dwLastPercentage, bGotFileSize, dwFileSize); } } while (dwBytesRead && !m_bAbort); //Just close the file before we return m_FileToWrite.Close(); //We're finished OnThreadFinished(0,0); } void CHttpDownload::UpdateControlsDuringTransfer(DWORD dwStartTicks, DWORD& dwCurrentTicks, DWORD dwTotalBytesRead, DWORD& dwLastTotalBytes, DWORD& dwLastPercentage, BOOL bGotFileSize, DWORD dwFileSize) { if (bGotFileSize) { //Update the percentage downloaded in the caption DWORD dwPercentage = (DWORD) ((dwTotalBytesRead + m_dwStartPos) * 100.0 / (dwFileSize + m_dwStartPos)); if (dwPercentage != dwLastPercentage) { //Update the progress control bar SetPercentage(dwPercentage); dwLastPercentage = dwPercentage; } } //Update the transfer rate amd estimated time left every second DWORD dwNowTicks = GetTickCount(); DWORD dwTimeTaken = dwNowTicks - dwCurrentTicks; if (dwTimeTaken > 1000) { double KbPerSecond = ((double)(dwTotalBytesRead) - (double)(dwLastTotalBytes)) / ((double)(dwTimeTaken)); SetTransferRate(KbPerSecond); //Setup for the next time around the loop dwCurrentTicks = dwNowTicks; dwLastTotalBytes = dwTotalBytesRead; if (bGotFileSize) { //Update the estimated time left if (dwTotalBytesRead) { DWORD dwSecondsLeft = (DWORD) (((double)dwNowTicks - dwStartTicks) / dwTotalBytesRead * (dwFileSize - dwTotalBytesRead) / 1000); SetTimeLeft(dwSecondsLeft, dwTotalBytesRead + m_dwStartPos, dwFileSize + m_dwStartPos); } } } } void CALLBACK CHttpDownload::_OnStatusCallBack(HINTERNET hInternet, DWORD dwContext, DWORD dwInternetStatus, LPVOID lpvStatusInformation, DWORD dwStatusInformationLength) { //Convert from the SDK C world to the C++ world CHttpDownload* pHttpDownload = (CHttpDownload*) dwContext; ASSERT(pHttpDownload); pHttpDownload->OnStatusCallBack(hInternet, dwInternetStatus, lpvStatusInformation, dwStatusInformationLength); } void CHttpDownload::OnStatusCallBack(HINTERNET /*hInternet*/, DWORD dwInternetStatus, LPVOID lpvStatusInformation, DWORD /*dwStatusInformationLength*/) { USES_CONVERSION; switch (dwInternetStatus) { case INTERNET_STATUS_RESOLVING_NAME: { SetStatus(IDS_HTTPDOWNLOAD_RESOLVING_NAME, A2T((LPSTR) lpvStatusInformation)); break; } case INTERNET_STATUS_NAME_RESOLVED: { SetStatus(IDS_HTTPDOWNLOAD_RESOLVED_NAME, A2T((LPSTR) lpvStatusInformation)); break; } case INTERNET_STATUS_CONNECTING_TO_SERVER: { SetStatus(IDS_HTTPDOWNLOAD_CONNECTING, A2T((LPSTR) lpvStatusInformation)); break; } case INTERNET_STATUS_CONNECTED_TO_SERVER: { SetStatus(IDS_HTTPDOWNLOAD_CONNECTED, A2T((LPSTR) lpvStatusInformation)); break; } case INTERNET_STATUS_REDIRECT: { SetStatus(IDS_HTTPDOWNLOAD_REDIRECTING, A2T((LPSTR) lpvStatusInformation)); break; } default: { break; } } } void CHttpDownload::DisInit() { Cancel(); //Wait for the worker thread to exit if (m_pThread) { WaitForSingleObject(m_pThread->m_hThread, INFINITE); delete m_pThread; m_pThread = NULL; } //Free up the internet handles we may be using if (m_hHttpFile) { ::InternetCloseHandle(m_hHttpFile); m_hHttpFile = NULL; } if (m_hHttpConnection) { ::InternetCloseHandle(m_hHttpConnection); m_hHttpConnection = NULL; } if (m_hInternetSession) { ::InternetCloseHandle(m_hInternetSession); m_hInternetSession = NULL; } } void CHttpDownload::Cancel() { if (!m_bSafeToClose) { //Just set the abort flag to TRUE and //disable the cancel button m_bAbort = TRUE; // GetDlgItem(IDCANCEL)->EnableWindow(FALSE); SetStatus(IDS_HTTPDOWNLOAD_ABORTING_TRANSFER); } }
初始化时需要传递一个窗口句柄,用来通知下载状态等各种消息。需要实现的消息如下,已经在HttpDownload.h类中定义了。
const UINT WM_HTTPDOWNLOAD_THREAD_FINISHED = WM_APP + 81; const UINT WM_HTTPDOWNLOAD_FILESTATUS = WM_APP + 82; const UINT WM_HTTPDOWNLOAD_PERCENTAGE = WM_APP + 83; const UINT WM_HTTPDOWNLOAD_TIMELEFT = WM_APP + 84; const UINT WM_HTTPDOWNLOAD_STATUS = WM_APP + 85; const UINT WM_HTTPDOWNLOAD_TRANSFERRATE = WM_APP + 86;
下面是其他应用中使用的例子代码,可以参考 :)
类头文件
class CBrowsePage : public CPropertyPage { DECLARE_DYNCREATE(CBrowsePage) // Construction public: CBrowsePage(); ~CBrowsePage(); // Dialog Data //{{AFX_DATA(CBrowsePage) enum { IDD = IDD_BROWSEPAGE }; CProgressCtrl m_ctrlProgress; CString m_sStatus; CString m_sUpdateInfo; //}}AFX_DATA // Overrides // ClassWizard generate virtual function overrides //{{AFX_VIRTUAL(CBrowsePage) public: virtual BOOL OnSetActive(); virtual void OnCancel(); protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support //}}AFX_VIRTUAL // Implementation protected: // Generated message map functions //{{AFX_MSG(CBrowsePage) virtual BOOL OnInitDialog(); //}}AFX_MSG afx_msg LRESULT OnHttpDownloadFinished(WPARAM wParam, LPARAM lParam); afx_msg LRESULT OnHttpDownloadPercentage(WPARAM wParam, LPARAM lParam); afx_msg LRESULT OnHttpDownloadTimeLeft(WPARAM wParam, LPARAM lParam); afx_msg LRESULT OnHttpDownloadFileStatus(WPARAM wParam, LPARAM lParam); afx_msg LRESULT OnDownloadFinished(WPARAM wParam, LPARAM lParam); DECLARE_MESSAGE_MAP() private: CHttpDownload m_HttpDownload; };
类的实现文件
CBrowsePage::CBrowsePage() : CPropertyPage(CBrowsePage::IDD) { //{{AFX_DATA_INIT(CBrowsePage) m_sStatus = _T(""); m_sUpdateInfo = _T(""); //}}AFX_DATA_INIT } CBrowsePage::~CBrowsePage() { } void CBrowsePage::DoDataExchange(CDataExchange* pDX) { CPropertyPage::DoDataExchange(pDX); //{{AFX_DATA_MAP(CBrowsePage) DDX_Control(pDX, IDC_PROGRESS1, m_ctrlProgress); DDX_Text(pDX, IDC_STATUS, m_sStatus); DDX_Text(pDX, IDC_EDITUPDATEINFO, m_sUpdateInfo); //}}AFX_DATA_MAP } BEGIN_MESSAGE_MAP(CBrowsePage, CPropertyPage) //{{AFX_MSG_MAP(CBrowsePage) //}}AFX_MSG_MAP ON_MESSAGE(WM_HTTPDOWNLOAD_FILESTATUS, OnHttpDownloadFileStatus) ON_MESSAGE(WM_HTTPDOWNLOAD_TIMELEFT, OnHttpDownloadTimeLeft) ON_MESSAGE(WM_HTTPDOWNLOAD_PERCENTAGE, OnHttpDownloadPercentage) ON_MESSAGE(WM_HTTPDOWNLOAD_THREAD_FINISHED, OnHttpDownloadFinished) ON_MESSAGE(WM_BROWSEPAGE_DOWNLOADFINISHED, OnDownloadFinished) END_MESSAGE_MAP()
BOOL CBrowsePage::OnSetActive() { _UpdatePackageList.RemoveAll(); CPropertySheet* pParent = (CPropertySheet*)GetParent(); ASSERT_KINDOF(CPropertySheet, pParent); _WriteLog("开始从[%s]获取[%s]...",_sURL,_sUpdateListFile); if(_bIsFromLocalFile) { PostMessage(WM_BROWSEPAGE_DOWNLOADFINISHED,!_IsFile(_sUpdateListFile),0); } else { CString sLiveUpdateUrl = _sURL + _UPDATELISTFILENAME; m_HttpDownload.Init( m_hWnd, sLiveUpdateUrl, _sUpdateListFile, _sProxyServer, _sProxyUserName, _sProxyPassword, _sHTTPUserName, _sHTTPPassword, (CHttpDownload::ConnectionType)_nConnection); if(m_HttpDownload.Start()) { pParent->SetWizardButtons(0); }else { pParent->SetWizardButtons(0); } } return CPropertyPage::OnSetActive(); } BOOL CBrowsePage::OnInitDialog() { CPropertyPage::OnInitDialog(); m_ctrlProgress.SetRange(0, 100); return TRUE; // return TRUE unless you set the focus to a control // EXCEPTION: OCX Property Pages should return FALSE } LRESULT CBrowsePage::OnHttpDownloadFileStatus(WPARAM wParam, LPARAM lParam) { CString sMsg(*(CString*)wParam); m_sStatus = sMsg; _WriteLog("%s",m_sStatus); UpdateData(FALSE); return 0; } LRESULT CBrowsePage::OnHttpDownloadTimeLeft(WPARAM wParam, LPARAM lParam) { CString sMsg(*(CString*)wParam); m_sStatus = sMsg; UpdateData(FALSE); return 0; } LRESULT CBrowsePage::OnHttpDownloadPercentage(WPARAM wParam, LPARAM lParam) { CString sMsg(*(CString*)wParam); m_ctrlProgress.SetPos(lParam); return 0; } LRESULT CBrowsePage::OnHttpDownloadFinished(WPARAM wParam, LPARAM lParam) { m_HttpDownload.DisInit(); OnDownloadFinished(wParam,lParam); return 0; } void CBrowsePage::OnCancel() { m_HttpDownload.Cancel(); CPropertyPage::OnCancel(); } LRESULT CBrowsePage::OnDownloadFinished(WPARAM wParam, LPARAM lParam) { CPropertySheet* pParent = (CPropertySheet*)GetParent(); ASSERT_KINDOF(CPropertySheet, pParent); CString sServerLastupdateTime; if(wParam == 0) { _WriteLog("获取[%s]成功。",_sUpdateListFile); pParent->SetWizardButtons(PSWIZB_NEXT); char szTemp[1001]; ini_GetPrivateProfileString("update","lastupdatetime","0",szTemp,500,_sUpdateListFile); sServerLastupdateTime = szTemp; _WriteLog("更新服务器%s:[%s]","lastupdatetime",sServerLastupdateTime); int nPackageCount = ini_GetPrivateProfileInt("update","packagecount",0,_sUpdateListFile); _WriteLog("更新服务器%s:[%s]","packagecount",nPackageCount); for(int i=0;i<nPackageCount;i++) { CString sPackId; sPackId.Format("updatepackage%d",i+1); ini_GetPrivateProfileString(sPackId,"filename","",szTemp,500,_sUpdateListFile); if(strlen(szTemp)<=0) continue; CString sFileName = szTemp; ini_GetPrivateProfileString(sPackId,"description","",szTemp,500,_sUpdateListFile); CString sDescription = szTemp; int nSize = ini_GetPrivateProfileInt(sPackId,"size",0,_sUpdateListFile); _UpdatePackageList.Add(_UpdatePackageStruct(sFileName,nSize,sDescription)); } nPackageCount = _UpdatePackageList.GetSize(); if(sServerLastupdateTime > _sLastUpdateTime && nPackageCount>0) { _sLastUpdateTime = sServerLastupdateTime; //pParent->SetActivePage(2); m_sStatus.Format("获取更新列表成功,单击“下一步”获取最新软件!"); m_sUpdateInfo = "当前安装路径信息:\r\n"; if(_sCallCenterPath !="") m_sUpdateInfo += "Isc 核心软件安装在 "+_sCallCenterPath+"\r\n"; if(_sIscConfigPath !="") m_sUpdateInfo += "IscWeb配置工具安装在 "+_sIscConfigPath+"\r\n"; m_sUpdateInfo += "---------------------------------------------------\r\n"; m_sUpdateInfo += "本次Isc软件更新包包括:\r\n"; for(int i=0;i<nPackageCount;i++) { CString sTmp; sTmp.Format("%s (file:%s size:%d)\r\n", _UpdatePackageList[i].sDescription , _UpdatePackageList[i].sFileName, _UpdatePackageList[i].nSize); m_sUpdateInfo +=sTmp; } m_sUpdateInfo += "---------------------------------------------------\r\n"; ini_GetPrivateProfileString("update","description","",szTemp,1000,_sUpdateListFile); m_sUpdateInfo += szTemp; _WriteLog("%s",m_sUpdateInfo); }else { _WriteLog("所有软件都是最新版本,不需要更新!"); pParent->SetActivePage(4); } }else { pParent->SetWizardButtons(0); m_sStatus.Format("获取更新列表失败,请检查后重试!"); _WriteLog("%s",m_sStatus); } UpdateData(FALSE); return TRUE; }
有问题给我留言。:)