⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 downloadthread.cpp

📁 FTP客户端
💻 CPP
字号:
/****************************************************************/
/*																*/
/*  DownloadThread.cpp											*/
/*																*/
/*  Implementation of the CDownloadThread class.				*/
/*	This class downloads a file from a FTP server in a seperate	*/
/*  thread. It sends a notification when finished/aborted.		*/
/*																*/
/*  Programmed by Pablo van der Meer							*/
/*  Copyright Pablo Software Solutions 2002						*/
/*	http://www.pablovandermeer.nl								*/
/*																*/
/*  Last updated: 15 may 2002									*/
/*																*/
/****************************************************************/


#include "stdafx.h"
#include "DownloadThread.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

IMPLEMENT_DYNCREATE(CDownloadThread, CWinThread)

CDownloadThread::CDownloadThread()
{
	m_bAutoDelete = FALSE;
	m_dwFileLength = 0;
	m_bTransferFailed = FALSE;
	m_bDirectoryCreated  = FALSE;

	m_nConnectionTimeout = 3000;
	m_pFtpConnection = NULL;
	m_nRetries = 1;
	m_nRetryDelay = 10;
	m_nPort = 21;
	m_bUsePASVMode = FALSE;
	
    // kill event starts out in the signaled state
    m_hEventKill = CreateEvent(NULL, TRUE, FALSE, NULL);
    m_hEventDead = CreateEvent(NULL, TRUE, FALSE, NULL);

	// default to binary transfer
	m_dwTransferType = FTP_TRANSFER_TYPE_BINARY;
}


CDownloadThread::~CDownloadThread()
{
	CloseHandle(m_hEventKill);
	CloseHandle(m_hEventDead);
}


/********************************************************************/
/*																	*/
/* Function name : InitInstance										*/
/* Description   : Initialize this instance of the thread.			*/
/*				   In this case it's actually the place where		*/
/*				   everything takes place.							*/
/*																	*/
/********************************************************************/
BOOL CDownloadThread::InitInstance()
{
	if (WaitForSingleObject(m_hEventKill, 0) != WAIT_TIMEOUT)
	{
		// aborted by user
		m_strResult = "\"" + m_strRemoteName + "\" download cancelled.";

		// Let the main thread know we are finished
		::PostMessage(m_pTransferManager->m_hWnd, WM_DOWNLOAD_FINISHED, (WPARAM)this, 0);

		// event is signaled
		return FALSE;
	}
	
	PostDownloadStatus("Initializing");

	// set animation to 'Downloading'
	m_ProgressDlg.m_nAnimationID = IDR_AVI1;
	
	// create dummy window as parent for the progress dialog
	if (!m_wndDummy.CreateEx(0, AfxRegisterWndClass(0), "CDownload Dummy Window",
			WS_OVERLAPPED, 0, 0, 0, 0, NULL, NULL))
		return FALSE;

	// Create the progress dialog box.
	if (!m_ProgressDlg.Create(&m_wndDummy, m_hEventKill))
		return FALSE;
	
	// wait until dialog is created
	WaitForProgressDialog();

	// when reducing the timeout connection, the internet connection speed is faster.
	if (m_nConnectionTimeout) 
	{
		m_InternetSession.SetOption(INTERNET_OPTION_CONNECT_TIMEOUT, m_nConnectionTimeout);
		m_InternetSession.SetOption(INTERNET_OPTION_RECEIVE_TIMEOUT, m_nConnectionTimeout);
		m_InternetSession.SetOption(INTERNET_OPTION_SEND_TIMEOUT, m_nConnectionTimeout);
	}

	// catch all exceptions
	try	
	{	
		// start receiving file 
		DownloadFile(m_strRemoteName, m_strLocalName);
	}
	catch(char *pszError)
	{ 		
		m_strResult = pszError; 		
	}
	catch (CFileException * pEx)
	{ 	
		// file can't be opened
		m_strResult.Format("File Error %d %s", pEx->m_cause, pEx->m_strFileName); 		
	}
	catch (CInternetException* pEx)
	{
		// internet exception
		m_strResult.Format("Internet Exception Error %d", pEx->m_dwError);
		if (pEx->m_dwError == ERROR_INTERNET_EXTENDED_ERROR)
		{
			char szBuffer[1024];
			DWORD dwBufferLength = 1024;
			DWORD dwError = 0;
			::InternetGetLastResponseInfo(&dwError, szBuffer, &dwBufferLength);
			m_strResult += szBuffer;
		}
	}
	// fix error message
	m_strResult.Replace('\n', ' ');
	m_strResult.Remove('\r');

	if (!m_strResult.IsEmpty())
	{
		// exception was thrown
		m_bTransferFailed = TRUE;
	}
	else
	if (WaitForSingleObject(m_hEventKill, 0) != WAIT_TIMEOUT)
	{
		// aborted by user
		m_strResult = "\"" + m_strRemoteName + "\" download cancelled.";
	}
	else 
	{
		if (m_bDirectoryCreated)
		{
			// successfull created folder
			m_strResult = "\"" + m_strLocalName + "\" folder successfully created.";
		}
		else
		{
			// successfull downloaded file
			m_strResult = "\"" + m_strRemoteName + "\" successfully downloaded.";
		}
	}

	// Let the main thread know we are done
	::PostMessage(m_pTransferManager->m_hWnd, WM_DOWNLOAD_FINISHED, (WPARAM)this, 0);

	// avoid entering standard message loop by returning FALSE
    return FALSE;
}


/********************************************************************/
/*																	*/
/* Function name : ExitInstance										*/
/* Description   : Perform any per-thread cleanup here.				*/
/*																	*/
/********************************************************************/
int CDownloadThread::ExitInstance()
{
	if (m_pFtpConnection)
	{
		m_pFtpConnection->Close();

		// delete ftp connection
		delete m_pFtpConnection;
		m_pFtpConnection = NULL;
	}

	// Close this session
	m_InternetSession.Close();

    m_ProgressDlg.DestroyWindow();

	m_wndDummy.DestroyWindow();
	
	return CWinThread::ExitInstance();
}

BEGIN_MESSAGE_MAP(CDownloadThread, CWinThread)
	//{{AFX_MSG_MAP(CDownloadThread)
		// NOTE - the ClassWizard will add and remove mapping macros here.
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()


/********************************************************************/
/*																	*/
/* Function name : KillThread										*/
/* Description   : With this public member you can kill this thread	*/
/*				   for the outside.									*/
/*																	*/
/********************************************************************/
void CDownloadThread::KillThread()
{
    // reset the m_hEventKill which signals the thread to shutdown
    VERIFY(SetEvent(m_hEventKill));

	DWORD dwResult;
	// make sure it is running
	while (dwResult = ResumeThread() > 1)
	{
		if (dwResult == 0xFFFFFFFF)
			break;
	}

    // allow thread to run at higher priority during kill process
    SetThreadPriority(THREAD_PRIORITY_ABOVE_NORMAL);
    WaitForSingleObject(m_hEventDead, INFINITE);
    WaitForSingleObject(m_hThread, INFINITE);

    // now delete CWinThread object since it's no longer necessary
    delete this;
}


/********************************************************************/
/*																	*/
/* Function name : Delete											*/
/* Description   : Called when thread is destructed.				*/
/*																	*/
/********************************************************************/
void CDownloadThread::Delete()
{
    // calling the base here won't do anything but it is a good habit
	CWinThread::Delete();

	// acknowledge receipt of kill notification
	VERIFY(SetEvent(m_hEventDead));
}


/********************************************************************/
/*																	*/
/* Function name : DownloadFile										*/
/* Description   : This is where the real downloading takes place.	*/
/*																	*/
/********************************************************************/
void CDownloadThread::DownloadFile(CString &source, CString &dest)
{
	m_ProgressDlg.SetWindowTitle("Receiving " + source);
	m_ProgressDlg.SetPos(0);
	m_ProgressDlg.SetSecondStatus("");

	// only a directory name was specified
	if (dest.Right(1) == "\\")
	{
		// try to create directory structure
		CreateLocalDirectory(dest);

		m_bDirectoryCreated = TRUE;
		return;
	}

	PostDownloadStatus("Connecting");

	// try x times to connect
	int nRetries = m_nRetries;
	while (nRetries > 0)
	{
		nRetries--;
		try
		{
			// Create new ftp connection to retrieve file
			wsprintf(m_szStatus, "Connecting to %s (Attempt %d of %d)", m_strServerName, m_nRetries - nRetries, m_nRetries); 
			m_ProgressDlg.SetStatus(m_szStatus);

			DoEvents();
			
			// Connect to FTP Server
			m_pFtpConnection = m_InternetSession.GetFtpConnection(m_strServerName, m_strUserName, m_strPassword, m_nPort, m_bUsePASVMode);
			nRetries = 0;
		}
		catch (CInternetException* pEx)
		{
			m_pFtpConnection = NULL;

			// catch errors from WinINet
			pEx->GetErrorMessage(m_szStatus, sizeof(m_szStatus));
			pEx->Delete();

			if (nRetries > 0)
			{
				wsprintf(m_szStatus, "Failed to connect to %s (Attempt %d of %d)", m_strServerName, m_nRetries - nRetries, m_nRetries); 
				m_ProgressDlg.SetStatus(m_szStatus);
				
				wsprintf(m_szStatus, "Retrying after %d seconds...", m_nRetryDelay); 
				m_ProgressDlg.SetSecondStatus(m_szStatus);

				// wait for m_nRetryDelay seconds, while m_hEventKill is not signaled
				if (WaitWithMessageLoop(m_hEventKill, m_nRetryDelay * 1000) == TRUE)
				{
					// user canceled download
					throw m_szStatus; 
				}
				PostDownloadStatus("Retrying");
				m_ProgressDlg.SetSecondStatus("");
			}
			else
			{
				throw m_szStatus; 
			}
		}
	}

	// split folder and file (if complete path is given)
	int nPos = source.ReverseFind('/');
	if (nPos != -1)
	{
		m_strCurrentDirectory = source.Left(nPos+1);
		source = source.Mid(nPos+1);
	}

	// set current directory
	if (!m_pFtpConnection->SetCurrentDirectory(m_strCurrentDirectory))
	{
		DWORD dwLength = 255, dwError;
		CString strInfo;
		InternetGetLastResponseInfo(&dwError, strInfo.GetBuffer(dwLength), &dwLength);
		strInfo.ReleaseBuffer();
		strInfo.Remove('\n');
		strInfo.Remove('\r');

		wsprintf(m_szStatus, "%s", strInfo);
		throw m_szStatus; 
	}


	lstrcpy(m_szStatus, source); 
	// Show source file name
	m_ProgressDlg.SetStatus(m_szStatus);
	// Show destination file name
	m_ProgressDlg.SetSecondStatus("to: " + dest);

	// open destination file
	if (m_File.Open(dest, CFile::modeCreate | CFile::modeWrite, NULL) == FALSE)
	{
		wsprintf(m_szStatus, "Unable to create file %s", dest); 
		throw m_szStatus; 
	}
	
	wsprintf(m_szStatus, "Opening %s", source); 
	m_ProgressDlg.SetStatus(m_szStatus);

	CInternetFile* pInternetFile = m_pFtpConnection->OpenFile(source, GENERIC_READ, m_dwTransferType);
	if (!pInternetFile)
	{
		wsprintf(m_szStatus, "Unable to open remote file %s", source); 
		throw m_szStatus; 
	}
	
	PostDownloadStatus("Downloading");

	// set max to 100%
	m_ProgressDlg.SetUpper(100);

	char buffer[BUF_SIZE];
	unsigned int nRead = BUF_SIZE;
	unsigned int nTotalRead = 0;
	while (nRead == BUF_SIZE && (WaitForSingleObject(m_hEventKill, 0) == WAIT_TIMEOUT))
	{
		// read remote data into buffer
		nRead = pInternetFile->Read(buffer, BUF_SIZE);
		// write buffer to data file
		m_File.Write(buffer, nRead);
		nTotalRead += nRead;
		wsprintf(m_szStatus, "%s (%d of %d  bytes transferred)", source, nTotalRead, m_dwFileLength); 	
		m_ProgressDlg.SetStatus(m_szStatus);

		if (m_dwFileLength > 0)
		{
			int nPos = (nTotalRead*100)/m_dwFileLength;
			m_ProgressDlg.SetPos(nPos);
		}
		else
		{
			m_ProgressDlg.SetPos(0);
		}
	}

	// close the file
	m_File.Close();

	// close internet file
	pInternetFile->Close();

	delete pInternetFile;
	
	// close FTP connection
	wsprintf(m_szStatus, "Closing connection to %s", m_strServerName); 
	m_ProgressDlg.SetStatus(m_szStatus);

	PostDownloadStatus("Finished");
}


/********************************************************************/
/*																	*/
/* Function name : GetLastError										*/
/* Description   : Get last error string.							*/
/*																	*/
/********************************************************************/
CString CDownloadThread::GetLastError()
{
	return m_strResult;
}


/********************************************************************/
/*																	*/
/* Function name : CreateLocalDirectory								*/
/* Description   : Create directory tree.							*/
/*																	*/
/********************************************************************/
BOOL CDownloadThread::CreateLocalDirectory(LPCTSTR lpszDirectory)
{
	CString strResult = lpszDirectory;
	
	CString strDir;
	BOOL bResult;
	// create directory structure one part at a time
	while (strResult != "")
	{
		strDir += strResult.Left(strResult.Find("\\")+1);
		strResult = strResult.Mid(strResult.Find("\\")+1);
		bResult = CreateDirectory(strDir, 0);
	}
	return bResult;
}


/********************************************************************/
/*																	*/
/* Function name : WaitForProgressDialog							*/
/* Description   : If the thread has just started, it may not have	*/
/*				   had time yet to initialize the dialog window.	*/
/*																	*/
/********************************************************************/
void CDownloadThread::WaitForProgressDialog()
{
	if(m_ProgressDlg.m_hWnd == NULL)
	{
		while(m_ProgressDlg.m_hWnd == NULL)
		{
			DoEvents();
		}
	}
	if(!::IsWindow(m_ProgressDlg.m_hWnd))
	{
		while(!::IsWindow(m_ProgressDlg.m_hWnd))
		{
			DoEvents();
		}
	}
}


/********************************************************************/
/*																	*/
/* Function name : PostDownloadStatus								*/
/* Description   : Post status to Transfer Manager.					*/
/*																	*/
/********************************************************************/
void CDownloadThread::PostDownloadStatus(LPCTSTR lpszStatus)
{
	int nLength = lstrlen(lpszStatus);
	// dynamically allocate memory for status message (receiver will delete it!)
	LPSTR lpszData = new char[nLength+1];
	lstrcpy(lpszData, lpszStatus);
	::PostMessage(m_pTransferManager->m_hWnd, WM_FTP_STATUS, (WPARAM)this, (LPARAM)lpszData);
}


⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -