2014-10-16 9 views
1

Windows 응용 프로그램 프로그래밍에서 NamedPipe를 사용하여 cmd.exe 입력/출력 버퍼를 프로그램 콘솔로 리디렉션합니다. "ftp"와 같은 명령을 입력 할 때까지 모든 것이 잘 작동합니다. MSDN에서이 코드 링크가 C에서 ftp 파이프 리디렉션

#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); 
} 

입니다

: 당신이 진짜 cmd.exe를 콘솔에이를 입력하면 터미널에 "> FTP를"받아 봐,하지만 난 제발 ,, 나의 코드 http://msdn.microsoft.com/en-us/library/ms682499(v=VS.85).aspx 하고 좀했다 대화식으로 변경합니다. 감사합니다.

+0

WinInet과 같은 FTP 사용 가능 API를 사용하는 대신 cmd.exe를 사용하여 외부 ftp.exe 프로세스를 쉘링하여 앱 자체에서 직접 FTP 세션을 관리하는 이유는 무엇입니까? –

답변

1

stdin이 실제 콘솔인지 파이프인지를 확인하고 대화 형 및 비대화 형을 구분합니다. 비 대화식 모드로 실행될 때 ftp는 프롬프트를 표시하지 않습니다.

코드 더미를 작성하지 않고 테스트 할 수 있습니다. 명령 프롬프트를 실행에

open ftp.example.com 
quit 

: 포함 input.txt라는 텍스트 파일을 만듭니다

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

가 그런 경우 output.txt는 FTP에서 정상 및 오류 출력이 포함됩니다. 프롬프트가 표시되지 않습니다.

ftp의 동작이 도움이되지 않습니다. stdin은 파이프이기 때문에 비 대화식으로 실행되고 있다고 생각하지만, 실제로는 대화 형으로 실행하고 있습니다. 그렇지 않으면 설득 될 수 있다는 것을 나는 모른다.

+0

답변 해 주셔서 감사합니다. 하지만 내 문제는 내가 콘솔 (cmd.exe)처럼 행동하는 프로그램을 작성하려고 할 때 시작되었다. 나는 이것을 정확하게하고 싶다. 그리고 나는 명명 된 파이프를 발견했다. 그러나 나는 "ftp"뿐만 아니라 "PsExec"및 몇몇 다른 프로그램과 같은 프로그램에서도 이러한 문제를 겪고 있습니다. cmd.exe와 똑같이 동작하는 프로그램 (namedpipe 또는 sth와 함께)을 어떻게 작성할 수 있습니까? – QCoder

+0

cmd.exe가 ftp를 대화식으로 실행하면 파이프가 아닌 콘솔에서 입력되도록 설정합니다. 응용 프로그램이 cmd.exe처럼 동작하도록하려면 동일한 작업을 수행해야합니다. 파이프를 사용하도록 선택하면 cmd.exe와 다르게 동작하도록 선택합니다. – arx

+0

콘솔의 표준 입력이 리디렉션되었는지 여부를 확인하려면 GetStdHandle (및 STD_INPUT_HANDLE 지정)을 호출 한 다음이 핸들에서 GetFileType을 호출하여 핸들 유형을 찾습니다. 덕분에 – Stuart