简体   繁体   English

如何使用C语言传递输入并将输出检索到子进程

[英]How to pass an input and retrieve an output to a child process in C language

I have an exe program in Windows which in the terminal works as follows 我在Windows中有一个exe程序,终端的工作原理如下

> program.exe parameter01 file
entry01 (user types entry01)
output01
entry02 (user types entry02)
output02
...
until the combination Ctrl+D is pressed.

I need to create a "child process" in a C language which is capable to run the program and sent entries to the "child process" and receive the output in a char[] or string. 我需要用C语言创建一个“子进程”,它能够运行程序并将条目发送到“子进程”并以char []或字符串形式接收输出。

I know I have to use the CreateProcess method but I don't know how to pass an entry like an input and retrieve the output, How can I do this? 我知道我必须使用CreateProcess方法,但是我不知道如何像输入一样传递条目并检索输出,我该怎么做?

I have seen this using Java but I need to implement this functionality in C language. 我已经使用Java看到了这一点,但是我需要用C语言实现此功能。

Use the STARTUPINFO structure 使用STARTUPINFO结构

You must set the attribute hStdInput . 您必须设置属性hStdInput

More and less, this is what you need (it is C++ code and it may not compile, but you'll get the idea): 越来越少,这是你需要的(它是C ++代码,它可能无法编译,但你会得到这个想法):

std::string GetProcessOutput(HANDLE hStdOutProcess) {
    std::stringstream strStream;
    char lpBuffer[2] = {0};
    DWORD nBytesRead;
    while(true){
        BOOL bResult = ReadFile(hStdOutProcess, lpBuffer, sizeof(char), &nBytesRead, NULL);
        if (bResult && nBytesRead) {
            strStream << lpBuffer;
        } else {
            break;
        }
    }
    return strStream.str();
}

void RunAndGetOutout() {
    HANDLE hProcessStdOutRead = NULL;
    HANDLE hProcessStdOutWrite = NULL;

    SECURITY_ATTRIBUTES saAttr;
    saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
    saAttr.bInheritHandle = TRUE;  // allow to inherit handles from parent process
    saAttr.lpSecurityDescriptor = NULL; 

    if(!CreatePipe(&hProcessStdOutRead, &hProcessStdOutWrite, &saAttr, 0)) {
        return nResult;
    }
    if(!SetHandleInformation(hProcessStdOutRead, HANDLE_FLAG_INHERIT, 0)) {
        return nResult;
    }

    STARTUPINFO startInfo;
    PROCESS_INFORMATION processInfo;
    char cmdLine[ MAX_PATH*2 +40] = {0};
    char currentDir[MAX_PATH] = {0};

    ZeroMemory(&startInfo, sizeof(startInfo));
    startInfo.cb = sizeof(startInfo);
    startInfo.hStdOutput = hProcessStdOutWrite; // set the handle
    startInfo.dwFlags |= STARTF_USESTDHANDLES; // attention with this one

    ZeroMemory(&processInfo, sizeof(processInfo));

    GetCurrentDirectory(MAX_PATH, currentDir);
    sprintf(cmdLine, "\"%s\" %s", (const char*)m_path2Process, (const char*)m_processArgs);

    if(CreateProcess( NULL, cmdLine, NULL, NULL, TRUE, 0, NULL, currentDir, &startInfo, &processInfo)) {

        cout << GetProcessOutput(hProcessStdOutRead) << endl;

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

    CloseHandle(hProcessStdOutRead);
    CloseHandle(hProcessStdOutWrite)
}

You can try creating a Child Process with Redirected Input and Output, I adapted the code found here 您可以尝试使用重定向输入和输出创建子进程,我调整了此处的代码

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

#define BUFSIZE 4096 

/* child process's STDIN is the user input or data that you enter into the child process - READ */
HANDLE g_hChildStd_IN_Rd = NULL;
/* child process's STDIN is the user input or data that you enter into the child process - WRITE */
HANDLE g_hChildStd_IN_Wr = NULL;
/* child process's STDOUT is the program output or data that child process returns - READ */
HANDLE g_hChildStd_OUT_Rd = NULL;
/* child process's STDOUT is the program output or data that child process returns - WRITE */
HANDLE g_hChildStd_OUT_Wr = NULL;

void CreateChildProcess(void);
void WriteToPipe(CHAR chBuf[]);
void ReadFromPipe(void);
void ErrorExit(PTSTR);

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

    printf("\n->Start of parent execution.\n");

    // Set the bInheritHandle flag so pipe handles are inherited. 

    saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
    saAttr.bInheritHandle = TRUE;
    saAttr.lpSecurityDescriptor = NULL;

    //child process's STDOUT is the program output or data that child process returns
    // Create a pipe for the child process's STDOUT. 
    if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0))
        ErrorExit(TEXT("StdoutRd CreatePipe"));

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

    //child process's STDIN is the user input or data that you enter into the child process
    // Create a pipe for the child process's STDIN. 
    if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0))
        ErrorExit(TEXT("Stdin CreatePipe"));

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

    CreateChildProcess();

    /* variables */
    char FAR *lpsz;
    int cch;

    CHAR chBuf[BUFSIZE];
    DWORD dwRead = strlen(chBuf);
    HANDLE hStdin;
    BOOL bSuccess;

    hStdin = GetStdHandle(STD_INPUT_HANDLE);
    if (hStdin == INVALID_HANDLE_VALUE)
        ExitProcess(1);

    for (;;)
    {
        // Read from standard input and stop on error or no data.
        bSuccess = ReadFile(hStdin, chBuf, BUFSIZE, &dwRead, NULL);

        if (!bSuccess || dwRead == 0)
            break;

        lpsz = &chBuf[0];

        // 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.
        WriteToPipe(lpsz);
        printf("\n->Contents of %s written to child STDIN pipe.\n", argv[1]);
        // Read from pipe that is the standard output for child process. 
        printf("\n->Contents of child process STDOUT:\n\n", argv[1]);
        ReadFromPipe();
        printf("\n->End of parent execution.\n");
        // 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 CreateChildProcess()
// Create a child process that uses the previously created pipes for STDIN and STDOUT.
{
    TCHAR szCmdline[] = TEXT("cmd.exe /c \"C:\\path\\to\\exe\\program.exe -parameter C:\\path\\to\\file\\file.txt\"");
    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 = g_hChildStd_OUT_Wr;
    siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
    siStartInfo.hStdInput = g_hChildStd_IN_Rd;
    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(CHAR chBuf[])
// 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, dwWritten;
    // CHAR chBuf[] = "hola\n";
    dwRead = strlen(chBuf);
    BOOL bSuccess = FALSE;
    bSuccess = WriteFile(g_hChildStd_IN_Wr, chBuf, dwRead, &dwWritten, NULL);
    if (!bSuccess) ErrorExit(TEXT("StdInWr Cannot write into child process."));
    /*
    // Close the pipe handle so the child process stops reading. 
    if (!CloseHandle(g_hChildStd_IN_Wr))
        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, dwWritten;
    CHAR chBuf[BUFSIZE];
    BOOL bSuccess = FALSE;
    HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
    WORD wResult = 0;
    bSuccess = ReadFile(g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);
    if (!bSuccess || dwRead == 0) ErrorExit(TEXT("StdOutRd Cannot read child process's output."));
    if (chBuf[0] == '+' && chBuf[1] == '?') { printf("It's misspelled."); }
    else { printf("It's spelled correctly."); }
    // bSuccess = WriteFile(hParentStdOut, chBuf, dwRead, &dwWritten, NULL);
    // if (!bSuccess) ErrorExit(TEXT("StdOutWr Cannot write into parent process's output."));
}

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

A simple example: 一个简单的例子:

#include <signal.h> 

void SigQuit_Handle(int sig){
   exit(1);
}
int main(int argc, char *argv[]){
  char buffer[1024];
  signal( SIGQUIT, SigQuit_Handle );
  signal( SIGINT,  SIG_IGN ); // If you want to ignore Ctrl + C
  while ( true ){
   fgets(buffer, sizeof(buffer), INPUT_BUFFER);
  } 
  return 0;
}

Edit: In case of handling threads, you may need include sys/types.h. 编辑:如果处理线程,您可能需要包含sys / types.h。

Basically you will need to create an inter-process communication system on a Win32 platform. 基本上,您将需要在Win32平台上创建一个进程间通信系统。

You can do it in a few different ways: pipes, shared memory, IPC, WinSock, DDE... 您可以通过几种不同的方式实现:管道,共享内存,IPC,WinSock,DDE ......

All these features compete to offer you the crappiest possible API, with truckloads of inconsistent and useless parameters, unstandardized return codes and incoherent function names. 所有这些功能都可以为您提供最糟糕的API,包括大量不一致和无用的参数,不规范的返回码和不连续的函数名。 And the bloody awkward circa 1995 semi-unicode handling, on top of that. 最重要的是,这种血腥的尴尬之处大约发生在1995年的半unicode处理上。

Here is an example with a named pipe. 这是一个带有命名管道的示例。

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

// name of our glorious pipe
#define PIPE_NAME L"\\\\.\\pipe\\whatever" // bloody unicode string

// exit on fatal error
void panic(const char * msg)
{
    fprintf(stderr, "***PANIC*** %s\n", msg);
    exit(-1);
}

// father process
void father(const char * own_name) // name of our own executable to launch a copy of ourselve
{
    printf("Father process starting\n");

    // create a monodirectional father->child named pipe
    HANDLE pipe = CreateNamedPipe (
        PIPE_NAME,            // name of the pipe
        PIPE_ACCESS_OUTBOUND, // send only
        PIPE_TYPE_BYTE,       // send data as a byte stream
        1,                    // only one instance
        0, 0, 0, NULL);       // default junk
    if (pipe == INVALID_HANDLE_VALUE) panic("could not create pipe");

    // spawn child process
    {
        STARTUPINFOA si;
        PROCESS_INFORMATION pi;

        ZeroMemory(&si, sizeof(si));
        si.cb = sizeof(si);
        ZeroMemory(&pi, sizeof(pi));
        if (!CreateProcessA(    // using ASCII variant to be compatible with argv
            own_name,           // executable name (ourself)
            "child",            // command line. This will be seen as argv[0]
            NULL, NULL, FALSE,  // default junk
            CREATE_NEW_CONSOLE, // launch in another console window
            NULL, NULL,         // more junk
            &si, &pi))          // final useless junk
            panic("could not create child process");
    }

    // connect to child process
    BOOL result = ConnectNamedPipe(pipe, NULL);
    if (!result) panic("could not connect to child process");

    // talk to child
    for (;;)
    {
        // read an input line
        char line[100];
        printf("Say something >");
        if (fgets(line, sizeof(line), stdin) == NULL)
            panic("could not read from standard input");

        // exit on an empty line
        if (!strcmp(line, "\n")) break;

        // send the line to the child
        DWORD written = 0;
        if (!WriteFile(
            pipe,
            line,         // sent data
            strlen(line), // data length
            &written,     // bytes actually written
            NULL))
            panic("could not write to pipe");
    }

    // close the pipe
    CloseHandle(pipe);
}


void child(void)
{
    printf("Child process starting\n");

    // retrieve communication pipe
    HANDLE pipe = CreateFile(
         PIPE_NAME,      // name of the pipe
         GENERIC_READ,   // read ONLY access (or else the call will fail) 
         0, NULL,        // junk
         OPEN_EXISTING,  // opens existing pipe 
         0, NULL);       // more junk 
    if (pipe == INVALID_HANDLE_VALUE) panic("could not connect to the pipe");

    // read father's input
    for (;;)
    {
        char buffer[80];
        DWORD read = 0;
        if (!ReadFile(
                pipe,
                buffer,           // read data
                sizeof(buffer)-1, // max length (leave room for terminator)
                &read,            // bytes actually read
                NULL))
            break; // exit if the pipe has closed

        // display what our father said
        buffer[read] = '\0'; // make sure what we just read will be displayable as a string
        printf("Father said: %s", buffer);
    }

    // close pipe
    CloseHandle(pipe);
}

int main(int argc, char *argv[])
{
    // wait for a <return> keypress on exit
    atexit(getchar);

    // decide whether we are the father or the child
    if (!strcmp (argv[0], "child")) child();
    else                            father(argv[0]);

    printf("Done\n");
    return 0;
}

Sorry, I could not find a graceful way of using Ctrl-D as an exit signal. 对不起,我找不到使用Ctrl-D作为退出信号的优雅方式。 The only way I could think of achieving this would have required yet another couple of extremely tedious system calls, and I was daunted by the prospect. 我想到实现这一目标的唯一方法就是需要另外几个非常繁琐的系统调用,我被前景吓倒了。

So the father will terminate when the user enters an empty line. 因此父亲将在用户输入空行时终止。

By closing the pipe, the father will trigger a read error that will put the child out of its misery too (ie the child breaks out of the reading loop and dies when it gets a read error on the pipe). 通过关闭管道,父亲将触发读取错误,该错误也会使孩子摆脱痛苦(即,当孩子在管道上获得读取错误时,孩子会突破读取循环并死亡)。 You can easily have the child react to whatever message instead, if that can please your teacher better. 相反,您可以轻松地让孩子对任何消息做出反应,如果这样可以更好地让您的老师满意。

I have added an extra wait for another keypress in case you're running this from an IDE, to avoid closing the windows too abruptly. 我已经添加了额外的等待另一个按键,以防你从IDE运行它,以避免突然关闭窗口。 Just remove the atexit at the start of main() if you don't want that. 如果不希望,只需在main()开头删除atexit

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM