views:

508

answers:

2

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);
+3  A: 

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.
Daniel LeCheminant
FtpWebRequest does not work for me. I can use it in some cases, but I've had other problems using it.
scottm
When I pass the handle received in dataSocket to InternetReadFile, the app just exits after that call with no errors or exceptions. If I pass the HINTERNET handle, the call works, returns false and the number of bytesRead = 0
scottm
A: 

The key things that corrected my problem were making sure to send raw FTP commands, rather than commands such as "DIR", pass true (that I expected a data socket), and that the CharSet in the method declaration was CharSet.Ansi. If I replace CharSet.Ansi with CharSet.Auto or CharSet.Unicode, I just get a bunch of gibberish back.

I've update the code samples in the question to reflect what works.

scottm