1. 程式人生 > >在Windows下獲取控制檯(DOS)下可執行檔案的標準輸入輸出

在Windows下獲取控制檯(DOS)下可執行檔案的標準輸入輸出

我們在開發軟體時,常常會用到控制檯下的程式,比如make,link,ftp等等。除此之外,還有一些開源的軟體都是在控制檯下使用的,這樣,如果我們想方便的在Windows程式中直接呼叫這些程序和他們進行互動,那麼就需要獲取它們的標準輸入輸出。

在Windows下獲取這種輸出最常用的方法是通過建立子程序和管道。

例子如下:

首先,我們先建立一個用來測試的console程式。

#include <stdio.h>
 
int main(int argc, char* argv[])
{
    printf("hello world!/n");
    return 0;
}


編譯為可執行檔案hello.exe

接下來,可以通過如下示例程式碼對其標準輸出進行讀出。

1.首先創立子程序,並創立管道附加到子程序上(注意管道流向)

2.讀出子程序的標準輸出(直接使用ReadFile讀取管道),存入緩衝

3.把緩衝列印到螢幕上

我用的是C++的編譯器,C編譯器可能會報錯。

#include <stdio.h> 
#include <windows.h>
typedef struct
{
    HANDLE hRead;               /* stdout */
    HANDLE hWrite;              /* stdin */
    HANDLE hError;              /* stderr */
    HANDLE hReadU;            
    HANDLE hWriteU;            
    HANDLE hErrorU;            
    HANDLE hProcess;            /* child process handle */
    HANDLE hThread;             /* child main thread handle */
}CPWP_STRUCT;
 
int DestroyProcessWithPipe(CPWP_STRUCT* pcs)
{
    int nResult = 0;
    if(pcs->hError)
        CloseHandle(pcs->hError) ? TRUE : nResult--;
    if(pcs->hErrorU)
        CloseHandle(pcs->hErrorU) ? TRUE : nResult--;
    if(pcs->hProcess)
        CloseHandle(pcs->hProcess) ? TRUE : nResult--;
    if(pcs->hRead)
        CloseHandle(pcs->hRead) ? TRUE : nResult--;
    if(pcs->hReadU)
        CloseHandle(pcs->hReadU) ? TRUE : nResult--;
    if(pcs->hThread)
        CloseHandle(pcs->hThread) ? TRUE : nResult--;
    if(pcs->hWrite)
        CloseHandle(pcs->hWrite) ? TRUE : nResult--;
    if(pcs->hWriteU)
        CloseHandle(pcs->hWriteU) ? TRUE : nResult--;
    return nResult;
}
int CreateProcessWithPipe(char* szCmdLine, CPWP_STRUCT* pcs)
{
    if(!pcs)
        return -1;
    /* init */
    memset(pcs, 0, sizeof(CPWP_STRUCT));
    /* create pipes for read, write and error */
    SECURITY_ATTRIBUTES saAttr;
    saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
    saAttr.bInheritHandle = TRUE; 
    saAttr.lpSecurityDescriptor = NULL;
    if(!CreatePipe(&pcs->hRead, &pcs->hReadU, &saAttr, 0)) 
        /* create stdout pipe failed */
        return -2;
    if(!CreatePipe(&pcs->hWriteU, &pcs->hWrite, &saAttr, 0)) 
        /* create stdin pipe failed */
        return -3;
    if(!CreatePipe(&pcs->hError, &pcs->hErrorU, &saAttr, 0)) 
        /* create error pipe failed */
        return -4;

    /* create child process */
    PROCESS_INFORMATION pi; 
    STARTUPINFO si;
    memset(&pi, 0, sizeof(PROCESS_INFORMATION));
    memset(&si, 0, sizeof(STARTUPINFO));
    si.cb = sizeof(STARTUPINFO); 
    si.dwFlags = STARTF_USESTDHANDLES;
    si.hStdError = pcs->hErrorU;
    si.hStdInput = pcs->hWriteU;
    si.hStdOutput = pcs->hReadU;
    if(!CreateProcess(0, szCmdLine, 0, 0, TRUE, 0, 0, 0, &si, &pi))
    {
        DestroyProcessWithPipe(pcs);
        return -5;
    }
    pcs->hProcess = pi.hProcess;
    pcs->hThread = pi.hThread;
    /* success */
    return 0;
}
int ShowHelp()
{
    printf("usage: SuperPipe [command file path]/n"
           "example: SuperPipe hello.exe/n"
          );
    return -1;
}
int main(int argc, char *argv[]) 
{ 
    CPWP_STRUCT cs = {0};
    char szBuffer[0x400] = {0};
    DWORD dwWrite = 0, dwRead = 0;
    if(argc != 2)
        return ShowHelp();
 
    /* start child process */
    if(CreateProcessWithPipe(argv[1], &cs) < 0)
            return -3;
    
    /* read data stream from the pipe */
    if(!::ReadFile(cs.hRead, szBuffer, 0x400, &dwRead, NULL))
            exit(0);
    if(!dwRead)
            exit(0);
    printf(szBuffer);
    /* kill child process */
    ::TerminateProcess(cs.hProcess, 0);
    /* close pipe and handle */
    DestroyProcessWithPipe(&cs);
    return 0; 
}


只要建立了子程序我們就可以通過使用ReadFile來讀取cs.hRead獲取標準輸出。通過WirteFile寫入cs.hWrite進行標準輸入:)