?? downloadthread.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 + -