1

コード、申し訳ありませんが、少し長すぎますが、私はそれをそのようなサイズまで短くすることができました。重要な問題は、(私が思うに) 最後にこの奇妙な for ループがあることです。いいえ、ループ ヘッダーが空である理由がわかりません。Microsoft はそのようにしたいと考えています。

問題は、コードが子アプリからのさらに多くのデータを永遠に待機することです。

完全なアルゴリズムのページ: http://msdn.microsoft.com/en-us/library/ms682499(VS.85).aspx

(はい、私はそれが混乱していることを知っていますが、少なくとも自律的な混乱です。)

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

using namespace std;

#define BUFSIZE 4096 

int main() { 
  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;
  // Create a pipe for the child process's STDOUT.
  HANDLE g_hChildStd_OUT_Rd = NULL;
  HANDLE g_hChildStd_OUT_Wr = NULL;
  CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0);
  // Ensure the read handle to the pipe for STDOUT is not inherited.
  SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0);
  // Create a pipe for the child process's STDIN.
  HANDLE g_hChildStd_IN_Rd = NULL;
  HANDLE g_hChildStd_IN_Wr = NULL;
  CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0);
  // Ensure the write handle to the pipe for STDIN is not inherited.
  SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0);
  // Create the child process.
  // Create a child process that uses the previously created pipes for STDIN and STDOUT.
  char szCmdline[]="cmd /c dir";
  PROCESS_INFORMATION piProcInfo;
  STARTUPINFO siStartInfo;
  BOOL bCreateSuccess = 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.
  bCreateSuccess = 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

  DWORD dwRead, dwWritten; 
  CHAR chBuf[BUFSIZE];
  BOOL bWriteSuccess = FALSE;

  BOOL bReadSuccess = FALSE;
  HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
  for (;;) {
    bReadSuccess = ReadFile( g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);
    if( ! bReadSuccess || dwRead == 0 ) break; 
    bReadSuccess = WriteFile(hParentStdOut, chBuf, dwRead, &dwWritten, NULL);
    if (! bReadSuccess ) break;
  }

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

  return 0; 
}
4

1 に答える 1

1

一見すると、子プロセスに渡すパイプの書き込み側への親のハンドルを閉じるのを忘れています。パイプへの有効な書き込みハンドルがまだあるため、システムはパイプへの書き込みが不可能になったことを検出できず、子が終了するまで無限に待機します。

子の標準出力のみをキャプチャする必要がある_popen場合は、はるかに簡単な方法です。

編集: さて、親に接続するパイプに向けられた 3 つの標準ストリームすべてで子プロセスを生成するための古いコード。これは、このような単純なタスクに必要な時間よりもはるかに長くなりますが、Windows API の寿命です。公平を期すために、おそらくもっと短いかもしれませんが、20歳(またはそれくらい)です。API も、当時のコードの書き方も、現在とはまったく異なります (ただし、新しいコードが改善されたとは考えていない人もいるかもしれません)。

#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>
#include <ctype.h>
#include <io.h>
#include <fcntl.h>
#include <stdlib.h>

#include "spawn.h"

static void system_error(char const *name) {
// A function to retrieve, format, and print out a message from the
// last error.  The `name' that's passed should be in the form of a
// present tense noun (phrase) such as "opening file".
//
    char *ptr = NULL;
    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER |
        FORMAT_MESSAGE_FROM_SYSTEM,
        0,
        GetLastError(),
        0,
        (char *)&ptr,
        1024,
        NULL);

    fprintf(stderr, "%s\n", ptr);
    LocalFree(ptr);
}

static void InitializeInheritableSA(SECURITY_ATTRIBUTES *sa) {

    sa->nLength = sizeof *sa;
    sa->bInheritHandle = TRUE;
    sa->lpSecurityDescriptor = NULL;
}


static HANDLE OpenInheritableFile(char const *name) {
    SECURITY_ATTRIBUTES sa;
    HANDLE retval;

    InitializeInheritableSA(&sa);

    retval = CreateFile(
        name,
        GENERIC_READ,
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        &sa,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        0);


    if (INVALID_HANDLE_VALUE == retval) {
        char buffer[100];

        sprintf(buffer, "opening file %s", name);

        system_error(buffer);
        return retval;
    }
}

static HANDLE CreateInheritableFile(char const *name, int mode) {
    SECURITY_ATTRIBUTES sa;
    HANDLE retval;

    DWORD FSmode = mode ? OPEN_ALWAYS : CREATE_NEW;

    InitializeInheritableSA(&sa);

    retval = CreateFile(
        name,
        GENERIC_WRITE,
        FILE_SHARE_READ,
        &sa,
        FSmode,
        FILE_ATTRIBUTE_NORMAL,
        0);

    if (INVALID_HANDLE_VALUE == retval) {
        char buffer[100];

        sprintf(buffer, "creating file %s", name);

        system_error(buffer);
        return retval;
    }

    if ( mode == APPEND ) 
        SetFilePointer(retval, 0, 0, FILE_END);
}

enum inheritance { inherit_read = 1, inherit_write = 2 };

static BOOL CreateInheritablePipe(HANDLE *read, HANDLE *write, int inheritance) {

    SECURITY_ATTRIBUTES sa;

    InitializeInheritableSA(&sa);

    if ( !CreatePipe(read, write, &sa, 0)) {
        system_error("Creating pipe");
        return FALSE;
    }

    if (!inheritance & inherit_read)
        DuplicateHandle(
            GetCurrentProcess(),
            *read,
            GetCurrentProcess(),
            NULL,
            0,
            FALSE,
            DUPLICATE_SAME_ACCESS);

    if (!inheritance & inherit_write) 
        DuplicateHandle(
            GetCurrentProcess(),
            *write,
            GetCurrentProcess(),
            NULL,
            0,
            FALSE,
            DUPLICATE_SAME_ACCESS);

    return TRUE;
}

static BOOL find_image(char const *name, char *buffer) {
// Try to find an image file named by the user.
// First search for the exact file name in the current
// directory.  If that's found, look for same base name
// with ".com", ".exe" and ".bat" appended, in that order.
// If we can't find it in the current directory, repeat
// the entire process on directories specified in the
// PATH environment variable.
//
#define elements(array) (sizeof(array)/sizeof(array[0]))

    static char *extensions[] = {".com", ".exe", ".bat", ".cmd"};
    int i;
    char temp[FILENAME_MAX];

    if (-1 != access(name, 0)) {
        strcpy(buffer, name);
        return TRUE;
    }

    for (i=0; i<elements(extensions); i++) {
        strcpy(temp, name);
        strcat(temp, extensions[i]);
        if ( -1 != access(temp, 0)) {
            strcpy(buffer, temp);
            return TRUE;
        }
    }

    _searchenv(name, "PATH", buffer);
    if ( buffer[0] != '\0')
        return TRUE;

    for ( i=0; i<elements(extensions); i++) {
        strcpy(temp, name);
        strcat(temp, extensions[i]);
        _searchenv(temp, "PATH", buffer);
        if ( buffer[0] != '\0')
            return TRUE;
    }

    return FALSE;
}


static HANDLE DetachProcess(char const *name, HANDLE const *streams) {
    STARTUPINFO s;
    PROCESS_INFORMATION p;
    char buffer[FILENAME_MAX];

    memset(&s, 0, sizeof s);
    s.cb = sizeof(s);
    s.dwFlags = STARTF_USESTDHANDLES;
    s.hStdInput = streams[0];
    s.hStdOutput = streams[1];
    s.hStdError = streams[2];

    if ( !find_image(name, buffer)) {
        system_error("Finding Image file");
        return INVALID_HANDLE_VALUE;
    }

// Since we've redirected the standard input, output and error handles
// of the child process, we create it without a console of its own.
// (That's the `DETACHED_PROCESS' part of the call.)  Other
// possibilities include passing 0 so the child inherits our console,
// or passing CREATE_NEW_CONSOLE so the child gets a console of its
// own.
//
    if (!CreateProcess(
        NULL,
        buffer, NULL, NULL,
        TRUE,
        DETACHED_PROCESS,
        NULL, NULL,
        &s,
        &p))
    {
        system_error("Spawning program");
        return INVALID_HANDLE_VALUE;
    }

// Since we don't need the handle to the child's thread, close it to
// save some resources.
    CloseHandle(p.hThread);

    return p.hProcess;
}

static HANDLE StartStreamHandler(ThrdProc proc, HANDLE stream) {

    DWORD ignore;

    return CreateThread(
        NULL,
        0,
        proc,
        (void *)stream,
        0,
        &ignore);
}

HANDLE CreateDetachedProcess(char const *name, stream_info *streams) {
// This Creates a detached process.
// First parameter: name of process to start.
// Second parameter: names of files to redirect the standard input, output and error 
//  streams of the child to (in that order.)  Any file name that is NULL will be 
//  redirected to an anonymous pipe connected to the parent.
// Third Parameter: handles of the anonymous pipe(s) for the standard input, output
// and/or error streams of the new child process.
//
// Return value: a handle to the newly created process.
//

    HANDLE child_handles[3];
    HANDLE process;

    int i;

// First handle the child's standard input.  This is separate from the 
// standard output and standard error because it's going the opposite 
// direction.  Basically, we create either a handle to a file the child
// will use, or else a pipe so the child can communicate with us.
// 
    if ( streams[0].filename != NULL ) {
        streams[0].handle = NULL;
        child_handles[0] = OpenInheritableFile(streams[0].filename);
    }
    else
        CreateInheritablePipe(child_handles, &(streams[0].handle), inherit_read);

// Now handle the child's standard output and standard error streams.  These
// are separate from the code above simply because they go in the opposite 
// direction.
//
    for ( i=1; i<3; i++) 
        if ( streams[i].filename != NULL) {
            streams[i].handle = NULL;
            child_handles[i] = CreateInheritableFile(streams[i].filename, APPEND);
        }
        else 
            CreateInheritablePipe(&(streams[i].handle), child_handles+i, inherit_write);

// Now that we've set up the pipes and/or files the child's going to use,
// we're ready to actually start up the child process:
    process = DetachProcess(name, child_handles);
    if (INVALID_HANDLE_VALUE == process)
        return process;

// Now that we've started the child, we close our handles to its ends of the pipes.
// If one or more of these happens to a handle to a file instead, it doesn't really 
// need to be closed, but it doesn't hurt either.  However, with the child's standard
// output and standard error streams, it's CRUCIAL to close our handles if either is a
// handle to a pipe.  The system detects the end of data on a pipe when ALL handles to
// the write end of the pipe are closed -- if we still have an open handle to the
// write end of one of these pipes, we won't be able to detect when the child is done
// writing to the pipe.
//
    for ( i=0; i<3; i++) {
        CloseHandle(child_handles[i]);
        if ( streams[i].handler ) 
            streams[i].handle = 
                StartStreamHandler(streams[i].handler, streams[i].handle);
    }
    return process;
}

#ifdef TEST

#define buf_size 256

unsigned long __stdcall handle_error(void *pipe) {
// The control (and only) function for a thread handling the standard
// error from the child process.  We'll handle it by displaying a
// message box each time we receive data on the standard error stream.
//
    char buffer[buf_size];
    HANDLE child_error_rd = (HANDLE)pipe;
    unsigned bytes;

    while (ERROR_BROKEN_PIPE != GetLastError() &&
        ReadFile(child_error_rd, buffer, 256, &bytes, NULL))
    {
        buffer[bytes+1] = '\0';
        MessageBox(NULL, buffer, "Error", MB_OK);
    }
    return 0;
}

unsigned long __stdcall handle_output(void *pipe) {
// A similar thread function to handle standard output from the child
// process.  Nothing special is done with the output - it's simply
// displayed in our console.  However, just for fun it opens a C high-
// level FILE * for the handle, and uses fgets to read it.  As
// expected, fgets detects the broken pipe as the end of the file.
//
    char buffer[buf_size];
    int handle;
    FILE *file;

    handle = _open_osfhandle((long)pipe, _O_RDONLY | _O_BINARY);
    file = _fdopen(handle, "r");

    if ( NULL == file )
        return 1;

    while ( fgets(buffer, buf_size, file))
        printf("%s", buffer);

    return 0;
}

int main(int argc, char **argv) {

    stream_info streams[3];
    HANDLE handles[3];
    int i;

    if ( argc < 3 ) {
        fputs("Usage: spawn prog datafile"
            "\nwhich will spawn `prog' with its standard input set to"
            "\nread from `datafile'.  Then `prog's standard output"
            "\nwill be captured and printed.  If `prog' writes to its"
            "\nstandard error, that output will be displayed in a"
            "\nMessageBox.\n",
                stderr);
        return 1;
    }

    memset(streams, 0, sizeof(streams));

    streams[0].filename = argv[2];
    streams[1].handler = handle_output;
    streams[2].handler = handle_error;

    handles[0] = CreateDetachedProcess(argv[1], streams);
    handles[1] = streams[1].handle;
    handles[2] = streams[2].handle;

    WaitForMultipleObjects(3, handles, TRUE, INFINITE);

    for ( i=0; i<3; i++)
        CloseHandle(handles[i]);

    return 0;
}

#endif
于 2012-06-10T23:26:13.227 に答える