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);
}