Search code examples
c#winapiftppinvoke

How do I get the results from an FTP command run through FtpCommand?


I've wrapped most of wininet with no problems, but now I'm stuck. I am trying to p/invoke FtpCommand from wininet.dll, but every command I run returns "500 syntax error". Even simple commands like dir, or ls. If I connect to the same server with ftp.exe the commands work fine and return expected results.

Here's the method definition:

[DllImport("wininet.dll", CharSet = CharSet.Auto, SetLastError=true)]
extern public static int FtpCommand(
    [In]  IntPtr hConnect,      
    [In]  bool fExpectResponse, 
    [In]  int dwFlags,          
    [In]  string command,
    [In]  IntPtr dwContext,
    [In][Out]  ref int ftpCommand);

And the code where I'm calling it:

    public string SendCommand(string cmd)
    {
        int result;
        IntPtr dataSocket = new IntPtr();
        switch(cmd)
        {
            case "PASV":
                result = WININET.FtpCommand(_hConnect, false, WININET.FTP_TRANSFER_TYPE_ASCII, cmd, IntPtr.Zero, ref dataSocket);
                break;
            default:
                result = WININET.FtpCommand(_hConnect, true, WININET.FTP_TRANSFER_TYPE_ASCII, cmd, IntPtr.Zero, ref dataSocket);
                break;
        }

        Console.WriteLine(InternetLastResponseInfo());

        int BUFFER_SIZE = 8192;

        if(result == 0){
            Error();
        }
        else if(dataSocket != IntPtr.Zero)
        {
            StringBuilder buffer = new StringBuilder(BUFFER_SIZE);
            int bytesRead = 0;

            do
            {
                result = WININET.InternetReadFile(dataSocket, buffer, BUFFER_SIZE, ref bytesRead);
            } while (result == 1 && bytesRead > 1);

            return buffer.ToString();

        }

        return "";
    }

Everything else is working, I can download files, upload files and imitate the dir command using FtpFindFirstFile(), but I can't seem to send commands using the above method.

EDIT

My code for SendCommand has just been SendCommand("DIR") or SendCommand("LS"). After reading one of the answers (can't see who while I'm editing) I changed it to SendCommand("LIST") and that returned successfully.

However, my question was how to read the result, what do I use to read the data returned from the LIST command, so that I can output it in a readable format?

I've updated the SendCommand method to show how I plan on reading the data returned, but I always get 0 for bytesRead. I've also tried passing the handle received by dataSocket in the FtpCommand call, but the app just exits with no errors if I do that.

EDIT 2

I'm using InternetReadFile to read data from the handle of the data socket returned in my call to FtpCommand. The method signature for InternetReadFile I am using is this:

[DllImport("wininet.dll", CharSet = CharSet.Ansi, SetLastError = true)]
extern public static int InternetReadFile(
    [In] IntPtr hConnect,
    [In][Out] StringBuilder buffer,
    [In] int buffCount,
    [In][Out] ref int bytesRead);

FINAL EDIT

I released this code on codeplex:


Solution

  • Why are you getting 500 Syntax errors?

    Remember that unlike an FTP client (like ftp.exe) the actual FTP protocol doesn't take commands like ls, it takes commands like LIST and NLST

    See the Raw FTP Command List or RFC 959 for a list of commands accepted by FTP

    How do you get the results of a LIST or NLST command?

    The last parameter to FtpCommand is

    A pointer to a handle that is created if a valid data socket is opened. The fExpectResponse parameter must be set to TRUE for phFtpCommand to be filled.

    You can use this handle to read out the list with InternetReadFile.

    Notes:

    • You might consider using something like WireShark to see the actual network traffic sent by ftp.exe and compare it to what your code is sending
    • You might also consider looking into FtpWebRequest; if it meets your needs, it might be easier to use.