www.pudn.com > 完整的FTP客户端ftpwanderersrc.zip > UploadThread.cpp


/****************************************************************/ 
/*																*/ 
/*  UploadThread.cpp											*/ 
/*																*/ 
/*  Implementation of the CUploadThread class.					*/ 
/*	This class uploads a file to 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 "ftpwanderer.h" 
#include "UploadThread.h" 
 
#ifdef _DEBUG 
#define new DEBUG_NEW 
#undef THIS_FILE 
static char THIS_FILE[] = __FILE__; 
#endif 
 
 
IMPLEMENT_DYNCREATE(CUploadThread, CWinThread) 
 
CUploadThread::CUploadThread() 
{ 
	m_bAutoDelete = FALSE; 
	m_dwFileLength = 0; 
	 
	m_nConnectionTimeout = 0; 
	m_pFtpConnection = NULL; 
	m_bTransferFailed = FALSE; 
	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; 
} 
 
CUploadThread::~CUploadThread() 
{ 
	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 CUploadThread::InitInstance() 
{ 
	if (WaitForSingleObject(m_hEventKill, 0) != WAIT_TIMEOUT) 
	{ 
		// aborted by user 
		m_strResult = "\"" + m_strRemoteName + "\" upload cancelled."; 
 
		// Let the main thread know we finished 
		::PostMessage(m_pTransferManager->m_hWnd, WM_UPLOAD_FINISHED, (WPARAM)this, (LPARAM)FALSE); 
 
		// event is signaled 
		return FALSE; 
	} 
 
	PostUploadStatus("Initializing"); 
 
	// set animation to 'Uploading' 
	m_ProgressDlg.m_nAnimationID = IDR_AVI2; 
 
	// create dummy window as parent for the progress dialog 
	if (!m_wndDummy.CreateEx(0, AfxRegisterWndClass(0), "CUpload 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; 
 
	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 sending file  
		UploadFile(m_strLocalName, m_strRemoteName); 
	} 
	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; 
		} 
	} 
 
	m_strResult.Replace('\n', ' '); 
	m_strResult.Remove('\r'); 
 
	BOOL bSucceeded = FALSE; 
	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 + "\" upload cancelled."; 
	} 
	else  
	{ 
		// successfull 
		m_strResult = "\"" + m_strRemoteName + "\" successfully uploaded."; 
		bSucceeded = TRUE; 
	} 
 
	// Let the main thread know we are finished 
	::PostMessage(m_pTransferManager->m_hWnd, WM_UPLOAD_FINISHED, (WPARAM)this, (LPARAM)bSucceeded); 
 
    // avoid entering standard message loop by returning FALSE 
    return FALSE; 
} 
 
 
/********************************************************************/ 
/*																	*/ 
/* Function name : ExitInstance										*/ 
/* Description   : Perform any per-thread cleanup here.				*/ 
/*																	*/ 
/********************************************************************/ 
int CUploadThread::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(CUploadThread, CWinThread) 
	//{{AFX_MSG_MAP(CUploadThread) 
		// 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 CUploadThread::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 no longer necessary 
    delete this; 
} 
 
 
/********************************************************************/ 
/*																	*/ 
/* Function name : Delete											*/ 
/* Description   : Called when thread is destructed.				*/ 
/*																	*/ 
/********************************************************************/ 
void CUploadThread::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 : UploadFile										*/ 
/* Description   : This is where the real uploading takes place.	*/ 
/*																	*/ 
/********************************************************************/ 
void CUploadThread::UploadFile(CString &source, CString &dest) 
{ 
	m_ProgressDlg.SetWindowTitle("Sending " + source); 
 
	m_ProgressDlg.SetPos(0); 
	m_ProgressDlg.SetSecondStatus(""); 
 
	BOOL bCreateDirectory = FALSE; 
 
	// specified a complete directory path 
	int nPos = dest.ReverseFind('/'); 
	if (nPos != -1) 
	{ 
		m_strCurrentDirectory.TrimRight('/'); 
		m_strCurrentDirectory += "/"; 
		m_strCurrentDirectory += dest.Left(nPos); 
		// filename specified? 
		if (nPos == (dest.GetLength() - 1)) 
		{ 
			// no, try to create directory 
			bCreateDirectory = TRUE; 
		} 
	} 
 
	PostUploadStatus("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;  
				} 
				PostUploadStatus("Retrying"); 
				m_ProgressDlg.SetSecondStatus(""); 
			} 
			else 
			{ 
				throw m_szStatus;  
			} 
		} 
	} 
 
	// do we need to create a directory? 
	if (bCreateDirectory) 
	{ 
		// create remote direcory 
		if (!m_pFtpConnection->CreateDirectory(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;  
		} 
		// get current directory 
		m_pFtpConnection->GetCurrentDirectory(m_strCurrentDirectory); 
 
		// mark as folder 
		m_dwFileLength = -1; 
 
		// close FTP connection 
		wsprintf(m_szStatus, "Closing connection to %s", m_strServerName);  
		m_ProgressDlg.SetStatus(m_szStatus); 
		 
		PostUploadStatus("Finished"); 
		return; 
	} 
 
	// 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 source file 
	if (m_File.Open(source, CFile::modeRead, NULL) == FALSE) 
	{ 
		wsprintf(m_szStatus, "Unable to open file %s", source);  
		throw m_szStatus;  
	} 
	 
	wsprintf(m_szStatus, "Opening %s", dest);  
	m_ProgressDlg.SetStatus(m_szStatus); 
 
	CInternetFile* pInternetFile = m_pFtpConnection->OpenFile(dest, GENERIC_WRITE); 
	if (!pInternetFile) 
	{ 
		wsprintf(m_szStatus, "Unable to open remote file %s", dest);  
		throw m_szStatus;  
	} 
	 
	// set max position to 100% 
	m_ProgressDlg.SetUpper(100); 
 
	PostUploadStatus("Uploading");	 
 
	// get file size 
	m_dwFileLength = m_File.GetLength(); 
 
	char buffer[BUF_SIZE]; 
	unsigned int nRead = BUF_SIZE; 
	unsigned int nTotalRead = 0; 
	while (nRead == BUF_SIZE && (WaitForSingleObject(m_hEventKill, 0) == WAIT_TIMEOUT)) 
	{ 
		// read data into buffer 
		nRead = m_File.Read(buffer, BUF_SIZE); 
		// write buffer to remote data file 
		pInternetFile->Write(buffer, nRead); 
		nTotalRead += nRead; 
		wsprintf(m_szStatus, "%s (%d of %d  bytes transferred)", source, nTotalRead, m_dwFileLength); 	 
		m_ProgressDlg.SetStatus(m_szStatus); 
 
		// update progressbar 
		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); 
	 
	PostUploadStatus("Finished"); 
} 
 
 
/********************************************************************/ 
/*																	*/ 
/* Function name : GetLastError										*/ 
/* Description   : Get last error string.							*/ 
/*																	*/ 
/********************************************************************/ 
CString CUploadThread::GetLastError() 
{ 
	return m_strResult; 
} 
 
 
/********************************************************************/ 
/*																	*/ 
/* Function name : WaitForProgressDialog							*/ 
/* Description   : If the thread has just started, it may not have	*/ 
/*				   had time yet to initialize the dialog window.	*/ 
/*																	*/ 
/********************************************************************/ 
void CUploadThread::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 : PostUploadStatus									*/ 
/* Description   : Post status to Transfer Manager.					*/ 
/*																	*/ 
/********************************************************************/ 
void CUploadThread::PostUploadStatus(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); 
}