无法在 Windows 7 中的命名管道内创建进程

Posted

技术标签:

【中文标题】无法在 Windows 7 中的命名管道内创建进程【英文标题】:Unable to create process inside a namedpipe in Windows 7 【发布时间】:2014-05-23 11:21:26 【问题描述】:

我从this 问题的第一个答案中获取了以下 c 代码,我在 VS2008 中使用 C89 对其进行编译,因此我对代码进行了一些更改以使其正常工作,它编译得很好,但在成功创建命名管道后无法创建进程(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>

&lt; comments &gt; 之间的行只是 cmets。

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

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

【问题讨论】:

GetLastError() 说什么? "CreateNamedPipe - 如果函数失败,返回值为INVALID_HANDLE_VALUE。要获取扩展错误信息,请调用GetLastError。" 我添加了行 printf("In the child thread: Last Error is %lu\n", GetLastError());我得到以下信息:父进程在子线程中启动:Last Error is 123 PANIC could not create pipe 我该如何解决这个错误? ERROR_INVALID_NAME 123 (0x7B) 文件名、目录名或卷标语法不正确。 ERROR_INVALID_NAME 表示您提供的名称无效。我的猜测是名为name 的内核对象已经存在于另一个内核命名空间中。为您的管道使用更独特的名称。 我改了但是错误不断 【参考方案1】:

问题出在这里:

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() 生成的错误代码。应该让您更好地诊断问题。如果您在解释问题时仍有问题,请更新您的问题。

【讨论】:

不完全确定我应该看什么。看起来恭喜是为了,修复错误报告似乎已经解除了您的阻止。 ERROR_FILE_NOT_FOUND 当然是一个非常常见的错误,尤其是在您使用 CreateProcess() 时。我当然不能帮你找回文件。

以上是关于无法在 Windows 7 中的命名管道内创建进程的主要内容,如果未能解决你的问题,请参考以下文章

Windows进程间通信—命名管道

无法打开 Windows 命名管道进行写入?

windows命名管道

windows命名管道

Windows中的命名管道

如何使用 Unix(或 Windows)中的(最好是未命名的)管道将一个进程的标准输出发送到多个进程?