2017-01-18 160 views
0

我被困在這個日子以來,我瘋了。 基本上我試圖在一個線程中打開cmd.exe並給它輸入並從它的父節點讀取它的輸出。就像在Linux中分配tty一樣,因爲windows中沒有這樣的東西。我對linux系統有很好的理解,但對windows的說法並不一樣。無法從CreateProcess()線程讀取管道

所以,這裏的「我」的代碼:

#undef UNICODE 


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


    //using namespace std; 
    #define BUFFER_SIZE 99 

    // handles for cmd thread pipes 
    HANDLE cmd_in_rd = NULL; 
    HANDLE cmd_in_wr = NULL; 
    HANDLE cmd_out_rd = NULL; 
    HANDLE cmd_out_wr = NULL; 

    HANDLE cmd_thread_handle; 


    void PrintError(char *text, int err) { 
    DWORD retSize; 
    LPTSTR pTemp = NULL; 

    if (!err) return; 

    retSize = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | 
     FORMAT_MESSAGE_FROM_SYSTEM | 
     FORMAT_MESSAGE_ARGUMENT_ARRAY, 
     NULL, 
     err, 
     LANG_NEUTRAL, 
     (LPTSTR)&pTemp, 
     0, 
     NULL); 


    if (pTemp) printf("%s: %s\n", text, pTemp); 
    LocalFree((HLOCAL)pTemp); 
    return; 

    } 


    int pipewrite(char *command) { 
    DWORD dwRead, dwWritten; 
    BOOL bSuccess = FALSE; 
    SetLastError(0); 
    WriteFile(cmd_in_wr, command, strlen(command), &dwWritten, NULL); 
    bSuccess = GetLastError(); 
    PrintError("WriteToPipe", bSuccess); 
    return (bSuccess == 0) || (bSuccess == ERROR_IO_PENDING); 
    } 

    int __stdcall cmd_thread(int arg) { 
    // this function only prints when data is ready 
    DWORD dwRead, dwWritten; 
    CHAR chBuf[BUFFER_SIZE]; 
    BOOL bSuccess = FALSE; 
    HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE); 

    int rf_ret, wf_ret; 

    //CloseHandle(cmd_out_wr); makes readfile fail!! 

    SetLastError(0); 
    while (1) { // only executes once!!!!!!! 
     (rf_ret = ReadFile(cmd_out_rd, chBuf, BUFFER_SIZE, &dwRead, NULL)) 
      && 
      (wf_ret = WriteFile(hParentStdOut, chBuf, dwRead, &dwWritten, NULL)); 
     printf("ReadFile returned: %d\nWriteFile returned: %d\n", rf_ret, wf_ret); 
     bSuccess = GetLastError(); 
     PrintError("ReadingFromPipe", bSuccess); 
    } 

    bSuccess = GetLastError(); 
    return (bSuccess == 0) || (bSuccess == ERROR_IO_PENDING); 
    } 


    int main(void) { 

    char buffer[BUFFER_SIZE]; 

    // init the pipes 
    SECURITY_ATTRIBUTES cmd_sa; 
    cmd_sa.nLength = sizeof(SECURITY_ATTRIBUTES); 
    cmd_sa.bInheritHandle = TRUE; 
    cmd_sa.lpSecurityDescriptor = NULL; 

    if (!CreatePipe(&cmd_out_rd, &cmd_out_wr, &cmd_sa, 0)) { 
     printf("%s\n", "Error creating pipes"); 
     return 1; 
    } 

    if (!SetHandleInformation(cmd_out_rd, HANDLE_FLAG_INHERIT, 0)) { 
     printf("%s\n", "Error setting handle infos"); 
     return 1; 
    } 

    if (!CreatePipe(&cmd_in_rd, &cmd_in_wr, &cmd_sa, 0)) { 
     printf("%s\n", "Error creating pipes"); 
     return 1; 
    } 

    if (!SetHandleInformation(cmd_in_rd, HANDLE_FLAG_INHERIT, 0)) { 
     printf("%s\n", "Error setting handle infos"); 
     return 1; 
    } 

    // create the cmd thread 
    PROCESS_INFORMATION cmd_pi; 
    STARTUPINFO cmd_si; 
    ZeroMemory(&cmd_pi, sizeof(PROCESS_INFORMATION)); 
    ZeroMemory(&cmd_si, sizeof(STARTUPINFO)); 
    cmd_si.cb = sizeof(STARTUPINFO); 
    cmd_si.hStdError = cmd_out_wr; 
    cmd_si.hStdOutput = cmd_out_wr; 
    cmd_si.hStdInput = cmd_in_rd; 
    cmd_si.dwFlags |= STARTF_USESTDHANDLES; 

    TCHAR comm[] = TEXT("cmd.exe"); 
    BOOL th = CreateProcess(NULL, 
     comm, 
     NULL, 
     NULL, 
     TRUE, // handles are inherited 
     0, 
     NULL, 
     NULL, 
     &cmd_si, 
     &cmd_pi); 

    if (th) { 
     CloseHandle(cmd_pi.hProcess); 
     CloseHandle(cmd_pi.hThread); 
    } 

    cmd_thread_handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)cmd_thread, NULL, 0, NULL); 

    // read commands from shell and send them to cmd 
    ZeroMemory(&buffer, BUFFER_SIZE); 
    while (1) { 
     fgets(buffer, BUFFER_SIZE, stdin); 
     if (!pipewrite(buffer)) break; 
    } 
    printf("Program terminated\n"); 

    return 0; 
    } 

我其實,出於測試目的,複製了很多其他問題上的計算器,並從MSDN因爲我無法得到它在我的主程序工作。我不明白的東西是:

爲什麼cmd_thread中的while循環在啓動時被執行,然後在那裏等待世界的盡頭?我試圖在閱讀之前關閉管道out_write句柄,但是這使得其他部分無法正常工作。我認爲關於使用/追蹤程序或運行程序,我認爲這是一個很好的選擇,但是我不知道cmd.exe線程是否能夠接收並運行輸入...因爲我沒有輸出:/ 它變成一個調試器,但我知道沒有工具... 奇怪的是,original工程(我從哪裏得到的代碼)。我試圖發現兩者之間的差異,但即使我並肩看着他們,他們似乎也做了完全相同的事情。

+0

爲什麼要將GetLastError的返回值放入BOOL中? –

+0

[使用重定向輸入和輸出創建子進程](https://msdn.microsoft.com/en-us/library/windows/desktop/ms682499.aspx) –

回答

0

子進程,一旦死去,因爲它試圖從標準輸入讀取,這是因爲:

if (!SetHandleInformation(cmd_in_rd, HANDLE_FLAG_INHERIT, 0)) { 

這應該是:

if (!SetHandleInformation(cmd_in_wr, HANDLE_FLAG_INHERIT, 0)) { 

像原來的代碼。

另外,您的錯誤處理在很大程度上是不正確的;您不會始終檢查錯誤,並且在沒有錯誤發生時您有時會調用GetLastError()。 (這些問題也在原始代碼中。)

您還需要重新撥打電話CloseHandle(cmd_out_wr);,因爲否則您將無法知道孩子什麼時候退出。

呵呵,順便說一下,cmd.exe是一個進程,而不是一個線程。

+0

謝謝!我不敢相信我沒有看到......!我知道錯誤處理是一團糟,所有的代碼都是一團糟,我修改它只是爲了讓該死的東西工作,這是出生哈哈 –