Environment: Visual C++ 6.0
I wrote some classes that download ftp files with a status dialog. My classes use the internet
service classes included in MFC CInternetSession, CFtpConnection, and CFileFind. The downloading
is done in a seperate thread so that the cancel button can cancel a download operation.
(continued)
I want to mention two things up front on using CInternetSession.
- When you created your App with AppWizard if you selected link to MFC librarys dynamically be sure to
add wininet.lib to you project settings link tab. If you link statically you should not get any errors
for unresolved externals.
- If you try a debug build of a CInternetSession enabled app on computer that does not contain
a late version of Internet Explorer it will fail. Only bother trying release versions on other
computers. If you have VC++ 6.0, debug builds will work on the programmers computer
becuase VC++ 6.0 installation installs a late version of Internet Explorer.
There are three classes:
- FTPDownload() creates the dialog, monitors the cancel button and starts the seperate downloading thread.
- FTPDownLoadFiles() Does the actual downloading in a seperate thread.
- CProgressDlg() Displays the percent complete and has cancel button.
I have included a zip file containing the three classes each in thier own component (.ogx) file. I did
this to simplify adding these classes to your program and becuase the progress dialog resource is included.
If you are not sure of how to add components through the component gallery read about it in VC++ 6.0 help.
After the following implementation notes, I show code for the FTPDownload, and FTPDownLoadFiles classes.
I do not show the code for CProgressDlg because this code is pretty straight forward and the size of this
article would be much larger. To see the code for CProgressDlg just download the zip file.
The only thing you need to know about to use these classes is the FTPDownload constructor.
FTPDownload(CStringArray * t_Csa, DWORD ctout);//Constructor
To use pass a CStringArray * to the constructor
This CStringArray has certain important CStrings in each position.
Csa[0] == Server Name
Csa[1] == User Name
Csa[2] == Password
Csa[3] = Ftp file name
Csa[4] = Local file name (name on local drive after downloaded)
As many Files as desired can be downloaded. Just begin filenames at index 3.
File names are to be stored in alternating order, first the ftp file name then the local file name
An optional second argument specifies the connection timeout value. In my opinion 3000 seemed the best.
The default argument timeout of 0 will not change any timeout value in CInternetSession.
EXAMPLE:
#include "FTPDownload.h"
void CMainFrame::OnDownloadFiles()
{
CStringArray Csa;
Csa.SetSize(0,7);
Csa.Add(server_name);
Csa.Add(user_name);
Csa.Add(pass_word);
Csa.Add(ftp_fname1);
Csa.Add(fname1);
Csa.Add(ftp_fname2);
Csa.Add(fname2);
FTPDownload dl(&Csa, connection_timeout);
m_wndStatusBar.SetPaneText(0, dl.result_str);
return;
}
THE CLASSES----------------------------------------------------------------
FTPDownload CLASS HEADER FILE-----------------------------------------
#ifndef G_FTPDOWNLOAD_H
#define G_FTPDOWNLOAD_H
#include <afxinet.h>
#include "ProgDlg.h"
class FTPDownload
{
public:
unsigned int thread_finished_flag;
unsigned int abort_flag;
CString result_str;
CProgressDlg cpd;
CStringArray * Csa;
DWORD connection_timeout;
FTPDownload(CStringArray * t_Csa, DWORD ctout = 0);
};
#endif
FTPDownload CLASS CPP FILE-----------------------------------------
#include "stdafx.h"
#include "FTPDownload.h"
#include "FTPDownLoadFiles.h"
UINT DownLoadFunction(LPVOID lParam);
FTPDownload::FTPDownload(CStringArray * t_Csa, DWORD ctout)
{
Csa = t_Csa;
connection_timeout = ctout;
result_str ="";
abort_flag = FALSE;
thread_finished_flag = FALSE;
cpd.Create();
CWinThread * dl_thread = AfxBeginThread(::DownLoadFunction, this);
while (thread_finished_flag == FALSE)
if (cpd.CheckCancelButton()) abort_flag = TRUE;
if (result_str.IsEmpty()==FALSE) return;
if (abort_flag) result_str = "User Cancelled Download Operation";//Now set up result string
else result_str = "Internet Download Completed Successfully";
return;
}
UINT DownLoadFunction(LPVOID lParam)
{
FTPDownload * pFtpI = (FTPDownload *)lParam;
try { FTPDownLoadFiles dl(pFtpI); }
catch(char * str)
{ pFtpI->result_str = str; }
catch (CFileException * pEx)
{ pFtpI->result_str.Format("File Error %d %s", pEx->m_cause, pEx->m_strFileName); }
catch (CInternetException* pEx)
{
pFtpI->result_str.Format("Internet Exception Error %d", pEx->m_dwError);
if (pEx->m_dwError == ERROR_INTERNET_EXTENDED_ERROR)
{
char temp_str[1024];
DWORD length = 1024;
DWORD temp_int = 0;
::InternetGetLastResponseInfo(&temp_int, temp_str, &length);
pFtpI->result_str += temp_str;
}
}
pFtpI->thread_finished_flag = TRUE;
return 0;
}
FTPDownLoadFiles CLASS HEADER FILE-----------------------------------------
#ifndef G_FTPDOWNLOAD_FILES_H
#define G_FTPDOWNLOAD_FILES_H
#include "FTPDownload.h"
class FTPDownLoadFiles
{
protected:
CInternetSession Cis;
CFtpConnection* m_Ftp_Conn;
FTPDownload * pFtpI;
CFile cfo;
void ReadFile(CString & source, CString & dest);
BOOL ftp_file_exists(CString & source);
void file_not_found(CString & source);
void UpdateStatus(void);
char status_str[1024];
unsigned int file_size;
CString temp_ftp_name;
public:
FTPDownLoadFiles(FTPDownload * t_pFtpI);
};
#endif
FTPDownLoadFiles CLASS CPP FILE-----------------------------------------
#include "stdafx.h"
#include "FTPDownLoadFiles.h"
#define DL_BUFFER_SIZE 4096
FTPDownLoadFiles::FTPDownLoadFiles(FTPDownload * t_pFtpI)
{
pFtpI = t_pFtpI;
m_Ftp_Conn = 0;
if (pFtpI->connection_timeout)
Cis.SetOption(INTERNET_OPTION_CONNECT_TIMEOUT, pFtpI->connection_timeout);
int csa_size = pFtpI->Csa->GetSize();
for (int i = 3;i<csa_size;i+=2)
ReadFile(pFtpI->Csa->GetAt(i), pFtpI->Csa->GetAt(i+1));
Cis.Close();
return;
}
void FTPDownLoadFiles::ReadFile(CString &source, CString &dest)
{
if (pFtpI->abort_flag) return;
pFtpI->cpd.SetHeader(source);
wsprintf(status_str, "Connecting to %s", pFtpI->Csa->GetAt(0)); UpdateStatus();
if (pFtpI->abort_flag) return;
m_Ftp_Conn = Cis.GetFtpConnection(pFtpI->Csa->GetAt(0),
pFtpI->Csa->GetAt(1), pFtpI->Csa->GetAt(2));
strcpy(status_str, source); UpdateStatus();
if (ftp_file_exists(source)==FALSE) return;
pFtpI->cpd.SetUpper(file_size);
if (cfo.Open(dest, CFile::modeCreate | CFile::modeWrite, NULL)==FALSE)
{wsprintf(status_str, "Unable to create file %s", dest); AfxMessageBox(status_str); throw status_str; }
pFtpI->cpd.SetHeader(temp_ftp_name);
wsprintf(status_str, "Opening %s", temp_ftp_name); UpdateStatus();
if (pFtpI->abort_flag) return;
CInternetFile* ifp = m_Ftp_Conn->OpenFile(temp_ftp_name);
char buffer[DL_BUFFER_SIZE];
unsigned int amount_read = DL_BUFFER_SIZE;
unsigned int total_read = 0;
while (amount_read == DL_BUFFER_SIZE && pFtpI->abort_flag == FALSE)
{
amount_read = ifp->Read(buffer, DL_BUFFER_SIZE);
cfo.Write(buffer, amount_read);
total_read += amount_read;
wsprintf(status_str, "%d of %d bytes read", total_read, file_size); UpdateStatus();
pFtpI->cpd.SetPos(total_read);
}
cfo.Close();
wsprintf(status_str, "Closing connection to %s", pFtpI->Csa->GetAt(0)); UpdateStatus();
m_Ftp_Conn->Close();
delete m_Ftp_Conn;
return;
}
void FTPDownLoadFiles::UpdateStatus(void)
{
pFtpI->cpd.SetStatus(status_str);
return;
}
BOOL FTPDownLoadFiles::ftp_file_exists(CString &source)
{
wsprintf(status_str, "Getting File Information %s", source); UpdateStatus();
if (pFtpI->abort_flag) return FALSE;
CFtpFileFind finder(m_Ftp_Conn);
if (pFtpI->abort_flag) return FALSE;
if (finder.FindFile(source) == FALSE)
{ file_not_found(source); return FALSE; }
if (pFtpI->abort_flag) return FALSE;
finder.FindNextFile();
temp_ftp_name = "";
temp_ftp_name = finder.GetFilePath();
if (temp_ftp_name.IsEmpty()) temp_ftp_name = source;
file_size = 0;
file_size = finder.GetLength();
if (file_size < DL_BUFFER_SIZE) file_size = 0;
return TRUE;
}
void FTPDownLoadFiles::file_not_found(CString & source)
{
wsprintf(status_str, "File NOT found on Server: %s", source);
UpdateStatus();
AfxMessageBox(status_str);
return;
}
Download demo project - 23.1 Kb