Frage Erfassen Sie die Ausgabe des erzeugten Prozesses als Zeichenfolge


Hintergrund:


Ich arbeite an einem Programm, das in der Lage sein soll, die stdout, stderr und Rückgabewerte eines Programms. Im Idealfall möchte ich diese in einer Zeichenfolge erfassen, die ich in meinem Objekt ablege, das Details des Prozesses enthält. Ich habe derzeit einen Code, der funktioniert, indem er die Ausgabe in eine Datei speichert, indem er (meiner Meinung nach) eine archaische C-Datei verwendet, die Magie handhabt. Jedes Mal, wenn ich die Ergebnisse ausgeben möchte, öffne ich diese Datei und drucke den Inhalt aus.

Manchmal (wenn ein spawn-Prozess ausgeführt wird) wird die nächste Ausführung meiner ausführbaren Datei unterbrochen, da die Datei zum Schreiben nicht geöffnet werden kann.

Problemstellung:


Ich suche nach einer Möglichkeit, die Ausgabe zu speichern stdout eines erstellten Prozesses in Windows zu einer Zeichenfolge und der stderr zum anderen auf eine sicherere, modernere Art und Weise. So könnte ich diese Inhalte jederzeit ausdrucken, wenn ich das Ergebnis jedes erstellten Prozesses ausgeben möchte.

Mein hässlicher Code:


Hauptstück-

    int stdoutold = _dup(_fileno(stdout)); //make a copy of stdout
    int stderrold = _dup(_fileno(stdout)); //make a copy of stderr
    FILE *f; 

    if(!fopen_s(&f, "name_of_my_file", "w")){ //make sure I can write to the file
        _dup2(_fileno(f), _fileno(stdout)); //make stdout point to f
        _dup2(_fileno(f), _fileno(stderr)); //make stderr point to f

        fork("command_I_want_to_run", &pi); //run my fake fork (see below)
    }
    else{
        ...//error handling
    }
    _close(_fileno(stdout)); //close tainted stdout
    _close(_fileno(stderr)); //close tainted stderr
    _close(_fileno(f)); //close f
    _dup2(stdoutold, _fileno(stdout)); //fix stdout
    _dup2(stderrold, _fileno(stderr)); //fix stderr

Gabel- (Sie können sich das als CreateProcess vorstellen, aber nur für den Fall, dass jemand sehen muss, was hier passiert)

int fork(std::string s, PROCESS_INFORMATION* pi){
char infoBuf[INFO_BUFFER_SIZE];
int bufCharCount = 
    ExpandEnvironmentStrings(s.c_str(), infoBuf, INFO_BUFFER_SIZE ); 
...
    STARTUPINFO si;
    ZeroMemory( &si, sizeof(si) );
    si.cb = sizeof(si);
    ZeroMemory( pi, sizeof(*pi) );
    LPSTR str = const_cast<char *>(infoBuf);
    if(!CreateProcess(NULL,
        str,
        NULL,
        NULL,
        TRUE,
        0,
        NULL,
        NULL,
        &si,
        pi)
    ){
        int err = GetLastError();
        printf("CreateProcess failed (%d).\n", err);
        CloseHandle((*pi).hProcess);
        CloseHandle((*pi).hThread);
        return err;
    }
return 0;
}

Anmerkungen:


  • Ich benutze VS 2010
  • Ich möchte mehrere Prozesse verwenden, nicht Threads, weil ich brauche, was ich brauche, um die Freiheit des eigenen Prozesses zu haben

Bearbeiten:


Eine zusätzliche Anmerkung: Ich versuche auch zu warten, bis der Prozess beendet ist, nachdem ich die Funktion aufgerufen habe, die den angegebenen Code ausführt, also die Ergebnisse von stdout und stderr stehen mir zu dieser Zeit zur Verfügung.


6
2018-01-03 20:57


Ursprung


Antworten:


Sie müssen Pipes verwenden, um den Inhalt des Stdout-Streams Ihres Prozesses zu erfassen. Es gibt ein ausführliches Beispiel zu MSDN, wie Sie dies erreichen können:

MSDN: Erstellen eines unterordneten Prozesses mit umgeleitetem Input und Output


4
2018-01-03 22:10



Eddy Lutens Antwort führte mich in eine gute Richtung, aber die MSDN-Dokumentation (obwohl ausführlich) hatte einige Probleme. Hauptsächlich müssen Sie sicherstellen, dass Sie alle Griffe schließen, die Sie nicht verwenden. Es hat auch nur Code, den der Benutzer zu verstehen erwartet.

Also, hier ist meine Codewand, von der ich erwarte, dass die Leute sie einfach verstehen: D

#include <string>
#include <iostream>
#include <windows.h> 
#include <stdio.h>
#pragma warning( disable : 4800 ) // stupid warning about bool
#define BUFSIZE 4096
HANDLE g_hChildStd_OUT_Rd = NULL;
HANDLE g_hChildStd_OUT_Wr = NULL;
HANDLE g_hChildStd_ERR_Rd = NULL;
HANDLE g_hChildStd_ERR_Wr = NULL;

PROCESS_INFORMATION CreateChildProcess(void); 
void ReadFromPipe(PROCESS_INFORMATION); 

int main(int argc, char *argv[]){ 
    SECURITY_ATTRIBUTES sa; 
    printf("\n->Start of parent execution.\n");
    // Set the bInheritHandle flag so pipe handles are inherited. 
    sa.nLength = sizeof(SECURITY_ATTRIBUTES); 
    sa.bInheritHandle = TRUE; 
    sa.lpSecurityDescriptor = NULL; 
    // Create a pipe for the child process's STDERR. 
    if ( ! CreatePipe(&g_hChildStd_ERR_Rd, &g_hChildStd_ERR_Wr, &sa, 0) ) {
        exit(1); 
    }
    // Ensure the read handle to the pipe for STDERR is not inherited.
    if ( ! SetHandleInformation(g_hChildStd_ERR_Rd, HANDLE_FLAG_INHERIT, 0) ){
        exit(1);
    }
    // Create a pipe for the child process's STDOUT. 
    if ( ! CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &sa, 0) ) {
        exit(1);
    }
    // Ensure the read handle to the pipe for STDOUT is not inherited
    if ( ! SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0) ){
        exit(1); 
    }
    // Create the child process. 
    PROCESS_INFORMATION piProcInfo = CreateChildProcess();

    // Read from pipe that is the standard output for child process. 
    printf( "\n->Contents of child process STDOUT:\n\n", argv[1]);
    ReadFromPipe(piProcInfo); 

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

// Create a child process that uses the previously created pipes
//  for STDERR and STDOUT.
PROCESS_INFORMATION CreateChildProcess(){
    // Set the text I want to run
    char szCmdline[]="test --log_level=all --report_level=detailed";
    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 STDERR and STDOUT handles for redirection.
    ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
    siStartInfo.cb = sizeof(STARTUPINFO); 
    siStartInfo.hStdError = g_hChildStd_ERR_Wr;
    siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
    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
    CloseHandle(g_hChildStd_ERR_Wr);
    CloseHandle(g_hChildStd_OUT_Wr);
    // If an error occurs, exit the application. 
    if ( ! bSuccess ) {
        exit(1);
    }
    return piProcInfo;
}

// 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. 
void ReadFromPipe(PROCESS_INFORMATION piProcInfo) {
    DWORD dwRead; 
    CHAR chBuf[BUFSIZE];
    bool bSuccess = FALSE;
    std::string out = "", err = "";
    for (;;) { 
        bSuccess=ReadFile( g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);
        if( ! bSuccess || dwRead == 0 ) break; 

        std::string s(chBuf, dwRead);
        out += s;
    } 
    dwRead = 0;
    for (;;) { 
        bSuccess=ReadFile( g_hChildStd_ERR_Rd, chBuf, BUFSIZE, &dwRead, NULL);
        if( ! bSuccess || dwRead == 0 ) break; 

        std::string s(chBuf, dwRead);
        err += s;

    } 
    std::cout << "stdout:" << out << std::endl;
    std::cout << "stderr:" << err << std::endl;
}

18
2018-01-03 23:55



Shawn Blakesley-Code ist eine gute Nacharbeit von Microsoft-Beispielcode, aber es hat ein kleines Problem, wenn es massive stdout- und stderr-verschachtelte Streams gibt, die nicht in Ordnung sind. Und einige Griffe sind durchgesickert (was für den Beispielcode OK ist). Durch den Aufruf von Hintergrundthread und PeekNamedPipe () wird sichergestellt, dass sich der Code dem POSIX-Systemaufruf ähnlicher verhält:

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

#ifdef __cplusplus
#define BEGIN_C extern "C" {
#define END_C } // extern "C"
#define null nullptr
#else
#define BEGIN_C
#define END_C
#define null ((void*)0)
#endif

BEGIN_C

int system_np(const char* command, int timeout_milliseconds, 
              char* stdout_data, int stdout_data_size, 
              char* stderr_data, int stderr_data_size, int* exit_code);

typedef struct system_np_s {
    HANDLE child_stdout_read;
    HANDLE child_stderr_read;
    HANDLE reader;
    PROCESS_INFORMATION pi;
    const char* command;
    char* stdout_data;
    int   stdout_data_size;
    char* stderr_data;
    int   stderr_data_size;
    int*  exit_code;
    int   timeout; // timeout in milliseconds or -1 for INIFINTE
} system_np_t;

static char stdout_data[16 * 1024 * 1024];
static char stderr_data[16 * 1024 * 1024];

int main(int argc, char *argv[]) { 
    int bytes = 1;
    for (int i = 1; i < argc; i++) {
        bytes += (int)strlen(argv[i]) + 1;
    }
    char* command = (char*)alloca(bytes);
    command[0] = 0;
    char* p = command;
    for (int i = 1; i < argc; i++) {
        int n = (int)strlen(argv[i]);
        memcpy(p, argv[i], n); p += n;
        *p = (i == argc - 1) ? 0x00 : 0x20; 
        p++;
    }
    int exit_code = 0;
    if (command[0] == 0) {
        command = (char*)"cmd.exe /c \"dir /w /b\"";
    }
    int r = system_np(command, 100 * 1000, stdout_data, sizeof(stdout_data), stderr_data, sizeof(stderr_data), &exit_code);
    if (r != 0) {
        fprintf(stderr, "system_np failed: %d 0x%08x %s", r, r, strerror(r));
        return r;
    } else {
        fwrite(stdout_data, strlen(stdout_data), 1, stdout);
        fwrite(stderr_data, strlen(stderr_data), 1, stderr);
        return exit_code;
    }
}

static int peek_pipe(HANDLE pipe, char* data, int size) {         
    char buffer[4 * 1024];
    DWORD read = 0;  
    DWORD available = 0;
    bool b = PeekNamedPipe(pipe, null, sizeof(data), null, &available, null);
    if (!b) {
        return -1;
    } else if (available > 0) {
        int bytes = min(sizeof(buffer), available);
        b = ReadFile(pipe, buffer, bytes, &read, null);
        if (!b) {
            return -1;
        }
        if (data != null && size > 0) {
            int n = min(size - 1, (int)read);
            memcpy(data, buffer, n);
            data[n + 1] = 0; // always zero terminated
            return n;
        }
    } 
    return 0;
}

static DWORD WINAPI read_from_all_pipes_fully(void* p) {
    system_np_t* system = (system_np_t*)p;
    unsigned long long milliseconds = GetTickCount64(); // since boot time
    char* out = system->stdout_data != null && system->stdout_data_size > 0 ? system->stdout_data : null;
    char* err = system->stderr_data != null && system->stderr_data_size > 0 ? system->stderr_data : null;
    int out_bytes = system->stdout_data != null && system->stdout_data_size > 0 ? system->stdout_data_size - 1 : 0;
    int err_bytes = system->stderr_data != null && system->stderr_data_size > 0 ? system->stderr_data_size - 1 : 0;
    for (;;) {
        int read_stdout = peek_pipe(system->child_stdout_read, out, out_bytes);
        if (read_stdout > 0 && out != null) { out += read_stdout; out_bytes -= read_stdout; } 
        int read_stderr = peek_pipe(system->child_stderr_read, err, err_bytes);
        if (read_stderr > 0 && err != null) { err += read_stderr; err_bytes -= read_stderr; } 
        if (read_stdout < 0 && read_stderr < 0) { break; } // both pipes are closed
        unsigned long long time_spent_in_milliseconds = GetTickCount64() - milliseconds;
        if (system->timeout > 0 && time_spent_in_milliseconds > system->timeout) { break; }
        if (read_stdout == 0 && read_stderr == 0) { // nothing has been read from both pipes
            HANDLE handles[2] = {system->child_stdout_read, system->child_stderr_read};
            WaitForMultipleObjects(2, handles, false, 1); // wait for at least 1 millisecond (more likely 16)
        }
    }
    if (out != null) { *out = 0; }
    if (err != null) { *err = 0; }
    return 0;
}

static int create_child_process(system_np_t* system) {
    SECURITY_ATTRIBUTES sa = {0}; 
    sa.nLength = sizeof(SECURITY_ATTRIBUTES); 
    sa.bInheritHandle = true; 
    sa.lpSecurityDescriptor = null; 
    HANDLE child_stdout_write = INVALID_HANDLE_VALUE;
    HANDLE child_stderr_write = INVALID_HANDLE_VALUE;
    if (!CreatePipe(&system->child_stderr_read, &child_stderr_write, &sa, 0) ) {
        return GetLastError(); 
    }
    if (!SetHandleInformation(system->child_stderr_read, HANDLE_FLAG_INHERIT, 0) ){
        return GetLastError(); 
    }
    if (!CreatePipe(&system->child_stdout_read, &child_stdout_write, &sa, 0) ) {
        return GetLastError(); 
    }
    if (!SetHandleInformation(system->child_stdout_read, HANDLE_FLAG_INHERIT, 0) ){
        return GetLastError(); 
    }
    // Set the text I want to run
    STARTUPINFO siStartInfo = {0};
    siStartInfo.cb = sizeof(STARTUPINFO); 
    siStartInfo.hStdError = child_stderr_write;
    siStartInfo.hStdOutput = child_stdout_write;
    siStartInfo.dwFlags |= STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
    siStartInfo.wShowWindow = SW_HIDE;
    bool b = CreateProcessA(null, 
        (char*)system->command,
        null,               // process security attributes 
        null,               // primary thread security attributes 
        true,               // handles are inherited 
        CREATE_NO_WINDOW,   // creation flags 
        null,               // use parent's environment 
        null,               // use parent's current directory 
        &siStartInfo,       // STARTUPINFO pointer 
        &system->pi);       // receives PROCESS_INFORMATION
    int err = GetLastError();
    CloseHandle(child_stderr_write);
    CloseHandle(child_stdout_write);
    if (!b) {
        CloseHandle(system->child_stdout_read); system->child_stdout_read = INVALID_HANDLE_VALUE;
        CloseHandle(system->child_stderr_read); system->child_stderr_read = INVALID_HANDLE_VALUE;
    }
    return b ? 0 : err;
}

int system_np(const char* command, int timeout_milliseconds, 
              char* stdout_data, int stdout_data_size, 
              char* stderr_data, int stderr_data_size, int* exit_code) {
    system_np_t system = {0};
    if (exit_code != null) { *exit_code = 0; }
    if (stdout_data != null && stdout_data_size > 0) { stdout_data[0] = 0; }
    if (stderr_data != null && stderr_data_size > 0) { stderr_data[0] = 0; }
    system.timeout = timeout_milliseconds > 0 ? timeout_milliseconds : -1; 
    system.command = command;
    system.stdout_data = stdout_data;
    system.stderr_data = stderr_data;
    system.stdout_data_size = stdout_data_size;
    system.stderr_data_size = stderr_data_size;
    int r = create_child_process(&system);
    if (r == 0) {
        system.reader = CreateThread(null, 0, read_from_all_pipes_fully, &system, 0, null);
        if (system.reader == null) { // in theory should rarely happen only when system super low on resources
            r = GetLastError();
            TerminateProcess(system.pi.hProcess, ECANCELED);
        } else {
            bool thread_done  = WaitForSingleObject(system.pi.hThread, timeout_milliseconds) == 0;
            bool process_done = WaitForSingleObject(system.pi.hProcess, timeout_milliseconds) == 0;
            if (!thread_done || !process_done) {
                TerminateProcess(system.pi.hProcess, ETIME);
            }
            if (exit_code != null) {
                GetExitCodeProcess(system.pi.hProcess, (DWORD*)exit_code);
            }        
            CloseHandle(system.pi.hThread);
            CloseHandle(system.pi.hProcess);
            CloseHandle(system.child_stdout_read); system.child_stdout_read = INVALID_HANDLE_VALUE;
            CloseHandle(system.child_stderr_read); system.child_stderr_read = INVALID_HANDLE_VALUE;
            WaitForSingleObject(system.reader, INFINITE); // join thread
            CloseHandle(system.reader);
        }
    }
    if (stdout_data != null && stdout_data_size > 0) { stdout_data[stdout_data_size - 1] = 0; }
    if (stderr_data != null && stderr_data_size > 0) { stderr_data[stderr_data_size - 1] = 0; }
    return r; 
} 

END_C

2
2018-02-01 00:25