redirect ftp pipe in C

2019-07-24 11:25发布

I am using NamedPipe in windows Application Programming to redirect cmd.exe Input/Output buffer to my program console. everything works good until I enter commands like "ftp". if you type this to real cmd.exe console you recive "ftp>" in terminal, but I don't.it is my code:

#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <strsafe.h>

#define BUFSIZE 4096

HANDLE hReadChildInput = NULL;
HANDLE hWriteChildInput = NULL;
HANDLE hReadChildOutput = NULL;
HANDLE hWriteChildOutput = NULL;

HANDLE hConsoleStd = NULL;
HANDLE hThread     = NULL;
BOOL   bRunThread  = TRUE;

void ChildProcess(void);
void WriteToPipe(void*);
void ReadFromPipe(void);
void ErrorExit(PTSTR);

int _tmain(int argc, TCHAR *argv[])
{
    SECURITY_ATTRIBUTES saAttr;

    // Set the bInheritHandle flag so pipe handles are inherited.
    saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
    saAttr.bInheritHandle = TRUE;
    saAttr.lpSecurityDescriptor = NULL;

    // Create a pipe for the child process's STDOUT.
    if ( ! CreatePipe(&hReadChildOutput, &hWriteChildOutput, &saAttr, 0) )
        ErrorExit(TEXT("StdoutRd CreatePipe"));

    // Ensure the read handle to the pipe for STDOUT is not inherited.
    if ( ! SetHandleInformation(hReadChildOutput, HANDLE_FLAG_INHERIT, 0) )
        ErrorExit(TEXT("Stdout SetHandleInformation"));

    // Create a pipe for the child process's STDIN.
    if (! CreatePipe(&hReadChildInput, &hWriteChildInput, &saAttr, 0))
        ErrorExit(TEXT("Stdin CreatePipe"));

    // Ensure the write handle to the pipe for STDIN is not inherited.
    if ( ! SetHandleInformation(hWriteChildInput, HANDLE_FLAG_INHERIT, 0) )
        ErrorExit(TEXT("Stdin SetHandleInformation")); 


    // Get std input handle so you can close it and force the ReadFile to
    // fail when you want the input thread to exit.
    if ( (hConsoleStd = GetStdHandle(STD_INPUT_HANDLE)) ==
        INVALID_HANDLE_VALUE )
        ErrorExit(TEXT("GetStdHandle"));


    // Write to the pipe that is the standard input for a child process.
    // Data is written to the pipe's buffers, so it is not necessary to wait
    // until the child process is running before writing data.
    hThread = CreateThread(0,0,(LPTHREAD_START_ROUTINE)WriteToPipe,0,0,0);

    // Create the child process.
    ChildProcess();

    // Read from pipe that is the standard output for child process.
    ReadFromPipe();

    // Tell the thread to exit and wait for thread to die.
    bRunThread = FALSE;

    if (WaitForSingleObject(hThread,INFINITE) == WAIT_FAILED)
        ErrorExit(TEXT("WaitForSingleObject"));

    // The remaining open handles are cleaned up when this process terminates.
    // To avoid resource leaks in a larger application, close handles explicitly.

    return 0;
}

void ChildProcess()
    // Create a child process that uses the previously created pipes for STDIN and STDOUT.
{
    TCHAR szCmdline[]=TEXT("cmd.exe");
    PROCESS_INFORMATION piProcInfo;
    STARTUPINFO siStartInfo;
    BOOL bSuccess = FALSE;

    // Set up members of the PROCESS_INFORMATION structure.

    ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );

    // Set up members of the STARTUPINFO structure.
    // This structure specifies the STDIN and STDOUT handles for redirection.

    ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
    siStartInfo.cb         = sizeof(STARTUPINFO);
    siStartInfo.hStdError  = hWriteChildOutput;
    siStartInfo.hStdOutput = hWriteChildOutput;
    siStartInfo.hStdInput  = hReadChildInput;
    siStartInfo.dwFlags   |= STARTF_USESTDHANDLES;

    // Create the child process.

    bSuccess = CreateProcess(NULL,
        szCmdline,     // command line
        NULL,          // process security attributes
        NULL,          // primary thread security attributes
        TRUE,          // handles are inherited
        0,             // creation flags
        NULL,          // use parent's environment
        NULL,          // use parent's current directory
        &siStartInfo,  // STARTUPINFO pointer
        &piProcInfo);  // receives PROCESS_INFORMATION

    // If an error occurs, exit the application.
    if ( ! bSuccess )
        ErrorExit(TEXT("CreateProcess"));
    else
    {
        // Close handles to the child process and its primary thread.
        // Some applications might keep these handles to monitor the status
        // of the child process, for example.

        CloseHandle(piProcInfo.hProcess);
        CloseHandle(piProcInfo.hThread);
    }
}

void WriteToPipe(void*)

    // Read from a file and write its contents to the pipe for the child's STDIN.
    // Stop when there is no more data.
{
    DWORD dwRead = 0, dwWritten = 0;
    CHAR chBuf[BUFSIZE];
    BOOL bSuccess = FALSE;

    for (;bRunThread;)
    {
        chBuf[dwRead] = '\0';
        bSuccess = ReadConsole(hConsoleStd,chBuf,1,&dwRead,NULL);
        if ( ! bSuccess || dwRead == 0 ) break;

        chBuf[dwRead] = '\0';
        bSuccess = WriteFile(hWriteChildInput, chBuf, dwRead, &dwWritten, NULL);
        if ( ! bSuccess ) break;
    }

    // Close the pipe handle so the child process stops reading.

    if ( ! CloseHandle(hWriteChildInput) )
        ErrorExit(TEXT("StdInWr CloseHandle"));
}

void ReadFromPipe(void)

    // Read output from the child process's pipe for STDOUT
    // and write to the parent process's pipe for STDOUT.
    // Stop when there is no more data.
{
    DWORD dwRead = 0, dwWritten = 0;
    CHAR chBuf[BUFSIZE];
    BOOL bSuccess = FALSE;
    HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

    for (;;)
    {
        chBuf[dwRead] = '\0';
        bSuccess = ReadFile( hReadChildOutput, chBuf, BUFSIZE, &dwRead, NULL);
        if( ! bSuccess || dwRead == 0 ) break;

        chBuf[dwRead] = '\0';
        bSuccess = WriteConsole(hParentStdOut,chBuf,dwRead,&dwWritten,NULL);
        if (! bSuccess ) break;
    }
}

void ErrorExit(PTSTR lpszFunction)

    // Format a readable error message, display a message box,
    // and exit from the application.
{
    LPVOID lpMsgBuf;
    LPVOID lpDisplayBuf;
    DWORD dw = GetLastError();

    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER |
        FORMAT_MESSAGE_FROM_SYSTEM |
        FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        dw,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR) &lpMsgBuf,
        0, NULL );

    lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
        (lstrlen((LPCTSTR)lpMsgBuf)+lstrlen((LPCTSTR)lpszFunction)+40)*sizeof(TCHAR));
    StringCchPrintf((LPTSTR)lpDisplayBuf,
        LocalSize(lpDisplayBuf) / sizeof(TCHAR),
        TEXT("%s failed with error %d: %s"),
        lpszFunction, dw, lpMsgBuf);
    MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);

    LocalFree(lpMsgBuf);
    LocalFree(lpDisplayBuf);
    ExitProcess(1);
}

this code link in msdn is: http://msdn.microsoft.com/en-us/library/ms682499(v=VS.85).aspx and I did some changes to make it interactive. thanks

1条回答
Summer. ? 凉城
2楼-- · 2019-07-24 12:03

Many console programs, ftp included, check whether stdin is an actual console or a pipe and distinguish between interactive and non-interactive use. When running in non-interactive mode ftp doesn't bother to display the prompt.

You can test this without writing a pile of code. Create a text file called input.txt containing:

open ftp.example.com
quit

At a command-prompt run:

ftp < input.txt > output.txt 2>&1

Then output.txt will contain the normal and error output from ftp. You'll see that there is no prompt.

In your case ftp's behaviour isn't helpful. It thinks it is running non-interactively because stdin is a pipe, but you are actually running it interactively. I don't know that it can be persuaded otherwise.

查看更多
登录 后发表回答