无法在Windows 7中的namedpipe内创建进程

时间:2014-05-23 11:21:27

标签: c visual-studio process named-pipes createprocess

我从this问题的第一个答案中得到了以下c代码,我在VS2008中使用C89编译它,所以我对代码进行了一些更改以正常工作,它只编译很好,但是在成功创建namedpipe之后无法创建进程(CreateProcessA不起作用)总是返回错误2并在panic函数中打印消息。

我尝试在CreateProcessA中运行的程序可以下载here,我通常运行它并按如下方式使用它:

> C:\qhichwa>cmd.exe /c "C:\qhichwa\flookup.exe -bx C:\qhichwa\qhichwa.fst"
    wasi <user writes wasi>
    wasi <program responds printing wasi>

    hola <user writes hola>
    + ? <program responds printing + ?>

    <pres ctrl + c to terminate program>

> C:\qhichwa>

< comments >之间的界限只是评论。

为了成功创建命名管道需要哪些更正?

#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)
{
    int err = GetLastError();
    fprintf(stderr, "***PANIC*** %s\n", msg);
    printf("In the child thread: Last Error is %lu\n", err);
    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 1");

    // 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
            "cmd.exe",           // executable name (ourself) 
            "/c \"C:\\qhichwa\\flookup.exe -bx C:\\qhichwa\\qhichwa.fst\"",            // 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 2");
    }

    // 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);
    father(argv[0]);
    // decide whether we are the father or the child
    //if (!strcmp(argv[0], "child")) child();
    //else                            father(argv[0]);

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

1 个答案:

答案 0 :(得分:6)

问题出在这里:

fprintf(stderr, "***PANIC*** %s\n", msg);
printf("In the child thread: Last Error is %lu\n", GetLastError());

这是一个标准的Windows编程错误,每个程序员都犯了一次这个错误。只有一次,这很难被诊断出来,你永远不会忘记在尝试发现它的过程中失去一周。

底层问题是GetLastError()的工作方式。它返回一个内部全局变量的值,它存储在TEB(线程环境块)中。它有全局变量这么常见的问题,你对winapi函数的每次调用都有可能改变它,包括那些实际上没有失败的函数。 Windows本身也使用该变量。 ERROR_INVALID_NAME是一个非常受欢迎的内部错误代码。

由于您无法看到这些winapi通话,因此更加复杂。破坏错误代码的是fprintf()。它在带有winapi调用的CRT中实现。必然如此,I / O是一项操作系统职责。

所以绝对必要的是,立即获取错误代码,然后再执行任何其他操作。虽然最好将值传递给panic()函数,因为它无法预测其他代码在其之前运行,快速修复就是重写它:

int err = GetLastError();
fprintf(stderr, "***PANIC*** %s\n", msg);
printf("In the child thread: Last Error is %lu\n", err);

您现在将获得真正的错误代码,即CreateNamedPipe()生成的错误代码。应该给你一个更好的诊断问题的机会。如果您在解释问题时仍然有问题,请更新您的问题。