Click here to Skip to main content
13,357,648 members (65,743 online)
Click here to Skip to main content
Add your own
alternative version


57 bookmarked
Posted 19 May 2004

Using FTP with .NET including sending server commands

, 19 May 2004
Rate this:
Please Sign up or sign in to vote.
How to send and receive files, enumerate directories and subdirectories remotely on demand, send server commands through FTP control port.


This article describes the usage of the CFTPConnection class wrapped in a managed C++ assembly. The following functions are used and discussed:

  1. Sending / receiving files from FTP server.
  2. Enumerating directory contents.
  3. Sending server commands, i.e., NLST to the FTP server using CFTPConnection::Command method and parsing the server's response synchronously. The FTP data (20) and control (21) ports.
  4. Using these functions from a simple UI.


Recently, I had a need to develop an FTP client as a sub-function of a large C# Windows Form application. I quickly discovered that FTP support was not part of the FCL. I decided that the simplest way to address my needs was to develop a wrapper around the CFTPConnection class MFC WinInet wrapper (yes a wrapper of a wrapper -- :) ).

I started out by just creating a purely unmanaged C++ .dll and just exporting a couple of functions, and then using PInvoke to call the functions from C#. I found this to be both inelegant and largely infeasible when it came to enumerating directory contents and returning these contents to a C# application. The solution I finally settled on was to create a managed C++ assembly that would interact with CFTPConnection and would package things up into nice managed classes for the C# clients.

Sending / Receiving files

Sending and receiving files is supported right out of the box by CFTPConnection. My wrapper around these functions is thin at best. Here's a simple example of sending a file with the wrapper:

bSent = FTP_Wrapper.SendFTPFile(Mode, Convert.ToInt32(szFTPServerPort), 
                     szFTPServerName, szFTPUserName, 
                     szFTPUserPassword, szFTPFile, 
                     ref szErrorMsg);
if (!bSent)

Enumerating Directory Contents

Getting the contents of an FTP directory is pretty straightforward using CFtpFileFind. I wanted to use this class and return a collection of files and subfolders to my managed clients. One thing to watch out for when enumerating directory contents of a server is that you don't try to pull back an entire server's contents before showing them in your GUI. For a larger server, this reading of the entire directory structure could take quite a while. A better approach (and one that is shown in the sample application) is to just enumerate the root directory, show the results, and then when a user wants to go down into a subdirectory, enumerate that subdirectory.

Enter the DirectoryContents class:

<PRE lang=mc++>namespace FTP_Wrap { public __gc class DirectoryContents; public __gc class DirectoryItem { public: System::String* m_Name; System::String* m_PathName; int m_Type; // 0 = file, 1 = subdirectory; long m_Size; // file size in bytes bool Equals(System::Object* pObj); }; public __gc class DirectoryContents { public: DirectoryContents(); virtual ~DirectoryContents() {}; bool LoadList(int SourceType, System::String* pServerNm, int ServerPort, System::String* pUserNm, System::String* pPwd, System::String* pRemoteDir, System::String* pMask, System::String*& pErrorMsg); System::Collections::ArrayList* m_DirectoryList; }; }

Using the DirectoryContents class from C#:

static void Main(string[] args)
  // TODO: Add code to start application here

  //Do the FTP dir command and recurse all sub folders:

static void ShowDirContents(String Dir)
  FTP_Wrap.DirectoryContents dc = new DirectoryContents();
  String ErrorMsg = "";
  bool Loaded = dc.LoadList("localhost", 21, "USERID", "PASSWORD", 
                Dir, "*", ref ErrorMsg);

  if (!Loaded)
  foreach (DirectoryItem item in dc.m_DirectoryList)
    if (item.m_Type == 0) //files
      Console.WriteLine("File: " + item.m_PathName + " Size: " + item.m_Size);
    else //sub directories


One additional challenge I faced in implementing my client functionality was that one of the FTP machine types that would typically be interfaced with did not support truly standard FTP. As a result, using CFTPFileFind did not work with the server to enumerate directories. Trying to use CFTPFileFind would return a bunch of garbage that was barely if at all interpretable as a list of files. I needed a better way to get the file lists from the server.

After digging around a little more, I noticed that there is a set of 'server' commands that can be sent to an FTP server using the Command method of CFTPConnection. The commands are sent on port 21 (the FTP control port) but the responses are sent back on port 20 (the data port). Luckily (or I'd have more code to deal with), CFTPConnection handles these communications.

In order to get the directory contents using a server command, I issued a NLST command to the server and parsed the response.

Here's what happens at the TCP/IP level:

Send NLST through port 21 to the server:

Sample screenshot

Get response to NLST from server to my local port 20:

Sample screenshot

Code from directorycontents.cpp:

<PRE lang=mc++>// Issue the command to the server: CString dir(pRemoteDir); InternetFile* pFile = NULL; System::String* pRes = NULL; BOOL bChg = pFtpConn->SetCurrentDirectory(dir); CString strDir; pFtpConn->GetCurrentDirectory(strDir); File = pFtpConn->Command(_T("NLST"), CFtpConnection::CmdRespRead, FTP_TRANSFER_TYPE_BINARY); //Process the response (the file list): ULONGLONG a = pFile->GetLength(); char readBuf[256]; unsigned int rd = 0; System::Text::StringBuilder* pSb = new System::Text::StringBuilder(); do { rd = pFile->Read(readBuf, 256); if (rd > 0) { System::String* pStr = new System::String(readBuf); pSb->Append(pStr, 0, rd); } } while (rd > 0); pRes = pSb->ToString(); // ok, parse the response: System::String* pDelim = "\r\n"; System::Text::RegularExpressions::Regex* pRegex = new System::Text::RegularExpressions::Regex(pDelim); System::String* parts[] = pRegex->Split(pRes); for (int n = 0; n < parts->Length; n++) { String* pItemNm = parts[n]; if (pItemNm->Length == 0) continue; DirectoryItem* pItem = new DirectoryItem; if (pItemNm->EndsWith("/")) { pItem->m_Type = 1; // a directory String* pTemp = pItemNm->Substring(0, pItemNm->Length - 1); int nNameBegin = pTemp->LastIndexOf("/"); pItem->m_Name = pTemp->Substring(nNameBegin + 1); System::Text::StringBuilder* pSb = new System::Text::StringBuilder(); pSb->Append(pRemoteDir); pSb->Append(pItem->m_Name); pSb->Append("/"); pItem->m_PathName = pSb->ToString(); } else { pItem->m_Type = 0; // a file int nNameBegin = pItemNm->LastIndexOf("/"); pItem->m_Name = pItemNm->Substring(nNameBegin + 1); System::Text::StringBuilder* pSb = new System::Text::StringBuilder(); pSb->Append(pRemoteDir); pSb->Append(pItem->m_Name); pItem->m_PathName = pSb->ToString(); } pItem->m_Size = 0; m_DirectoryList->Add(pItem); }

The big thing to note is that the Command method returns a CInternetFile. To get the actual response data, you just read that file till EOF.

The sample application

I've included a very simple stripped down sample application to show the functionality of the classes. In order to play with the DirectoryContents enumerations, select the '...' button beside 'file to get' in the get file form, or the '...' button beside the 'FTP Destination File' in the send file form. Go ahead and step through these to get a better idea if I've left something out.

The sample app will save your FTP settings (port, server name, most recent file names, etc.) to the current user registry under the software key.


These classes solved what I was trying to do. I hope they can help someone here.


I used numerous resources for figuring out how to use the CFTPConnection class. Plenty of good information on it is in MSDN. Also, I used the ethereal packet capture utility not only for this but for a lot of network troubleshooting. It can be downloaded from here.


This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


About the Author

Tim Kohler
Chief Technology Officer
United States United States
Working to keep a technology company up to date. Wondering when Microsoft will hire a fresh, innovative guy to run the company.

You may also be interested in...


Comments and Discussions

GeneralMy vote of 2 Pin
hadree25-Jan-12 23:22
memberhadree25-Jan-12 23:22 
GeneralCompling error Pin
vijayr8-Apr-10 2:04
membervijayr8-Apr-10 2:04 
QuestionDependencies? Pin
jess_le_chat22-Jul-09 8:53
memberjess_le_chat22-Jul-09 8:53 
QuestionHow to run FTP commands Pin
12kaunas7-Feb-07 7:13
member12kaunas7-Feb-07 7:13 
QuestionHow to use your FTP_Wrap? [modified] Pin
fnjcr4-Dec-06 16:08
memberfnjcr4-Dec-06 16:08 
GeneralQuick question Pin
Tom Wright8-Jun-05 7:12
memberTom Wright8-Jun-05 7:12 
GeneralRe: Quick question Pin
Tim Kohler8-Jun-05 9:49
memberTim Kohler8-Jun-05 9:49 
GeneralAnother free, open-source .NET FTP component Pin
h_c_a_andersen12-Nov-04 16:08
memberh_c_a_andersen12-Nov-04 16:08 
GeneralLINK : fatal error LNK1256 Pin
Seong Hyun3-Aug-04 17:38
memberSeong Hyun3-Aug-04 17:38 
GeneralRe: LINK : fatal error LNK1256 Pin
vijayr8-Apr-10 2:07
membervijayr8-Apr-10 2:07 
QuestionWhy FTP_Wrap is not by C#? Pin
gamebaby9-Jun-04 2:35
membergamebaby9-Jun-04 2:35 
AnswerRe: Why FTP_Wrap is not by C#? Pin
Tim Kohler14-Jun-04 11:45
memberTim Kohler14-Jun-04 11:45 
QuestionWhy FTP_Wrap is not by C#? Pin
gamebaby9-Jun-04 2:35
membergamebaby9-Jun-04 2:35 
GeneralBroken link... Pin
Karby20-May-04 13:31
memberKarby20-May-04 13:31 
GeneralRe: Broken link... Pin
Tim Kohler21-May-04 6:19
memberTim Kohler21-May-04 6:19 
GeneralRe: Broken link... Pin
Magadass3-Feb-06 21:22
memberMagadass3-Feb-06 21:22 
GeneralWow... Pin
bryansp20-May-04 7:58
memberbryansp20-May-04 7:58 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.180111.1 | Last Updated 20 May 2004
Article Copyright 2004 by Tim Kohler
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid