WinInet 封裝了 Winsock 細節,提供 CInternetSession、CFtpConnection、CFtpFileFind 等類別,大幅簡化 FTP 用戶端開發。本文示範如何結合遞迴演算法遍歷 FTP 伺服器完整目錄樹,並以縮排顯示層級結構,同時解決 CFtpFileFind 不支援並行實例的限制。
CInternetSession:初始化網際網路會話CFtpConnection:建立 FTP 連線(主機、連接埠、帳號、密碼)CFtpFileFind:列舉檔案/目錄(同一時間僅能存在一個實例)
m_pInetSession = new CInternetSession(AfxGetAppName(), 1, PRE_CONFIG_INTERNET_ACCESS);
try {
m_pFtpConnection = m_pInetSession->GetFtpConnection(m_host, m_username, m_password, m_port);
if (m_pFtpConnection) {
n = 0; // 層級縮排計數器
List(); // 遞迴遍歷入口
}
} catch (CInternetException* pEx) {
TCHAR szErr[1024];
pEx->GetErrorMessage(szErr, 1024);
// 顯示錯誤...
pEx->Delete();
}
void CMyDlg::List()
{
CFtpFileFind finder(m_pFtpConnection);
CStringArray dirs; // 暫存子目錄名稱
BOOL bWorking = finder.FindFile(_T("*"));
// 第一輪:區分檔案與目錄
while (bWorking) {
bWorking = finder.FindNextFile();
if (finder.IsDots()) continue;
if (finder.IsDirectory()) {
dirs.Add(finder.GetFileName()); // 目錄暫存,稍後遞迴
} else {
// 顯示檔案(含層級縮排)
CString indent;
for (int j = 0; j < n; j++) indent += _T("\t");
m_ftpInfo += indent + finder.GetFileName() + _T("\r\n");
UpdateData(FALSE);
}
}
finder.Close(); // 必須關閉,釋放連線控制權
// 第二輪:遞迴進入子目錄
for (int i = 0; i < dirs.GetSize(); i++) {
n++; // 增加縮排層級
CString dirName = _T("[") + dirs[i] + _T("]\r\n");
CString indent;
for (int j = 1; j < n; j++) indent += _T("\t");
m_ftpInfo += indent + dirName;
UpdateData(FALSE);
// 切換工作目錄
while (!m_pFtpConnection->SetCurrentDirectory(dirs[i])) { /* 重試 */ }
List(); // 遞迴呼叫
// 返回上層目錄
while (!m_pFtpConnection->SetCurrentDirectory(_T(".."))) { /* 重試 */ }
n--;
}
}
問題:
CFtpFileFind同一連線同一時間只能有一個作用中實例。若在遞迴呼叫List()時外層finder尚未Close(),將觸發ERROR_FTP_TRANSFER_IN_PROGRESS。解法:第一輪掃描時將子目錄名稱收集至
CStringArray,finder.Close()釋放控制權後,再逐一進入子目錄遞迴。此為「深度優先、串行化」策略。
AfxBeginThread),避免 UI 凍結CAsyncSocket 或 WinInet 非同步模式提升響應性
void ExploreDir(CString strDir) {
CFtpFileFind finder(pFtp);
CStringList list;
pFtp->SetCurrentDirectory(strDir);
BOOL bContinue = finder.FindFile(_T("*"));
while (bContinue) {
bContinue = finder.FindNextFile();
CString name = finder.GetFileName();
if (name == _T(".") || name == _T("..")) continue;
if (finder.IsDirectory()) list.AddTail(name);
}
finder.Close();
while (!list.IsEmpty()) {
ExploreDir(list.RemoveHead());
}
}
此版本同樣遵循「掃描收集 → 關閉 → 遞迴」模式,使用 CStringList 替代陣列。
WinInet 提供高層級 FTP 操作介面,配合遞迴演算法可輕鬆實現目錄樹遍歷。關鍵在於理解 CFtpFileFind 的單實例限制,並採用「兩階段掃描」策略繞過此限制。實際部署時建議加入執行緒隔離、進度回報與錯誤恢復機制,構建健壯的 FTP 管理工具。