You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Windows API管道双向通信:写入cmd后无法读取输出

问题:通过管道与cmd.exe交互时ReadFile挂起,无法获取输入后的输出

我尝试编写一个程序,通过两组管道向cmd.exe提供输入并获取其输出。但通过PARENT_WRITE写入输入后,无法从PARENT_READ管道获取输出,ReadFile()出现挂起情况。

原代码

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

#define BUFFER_LENGTH 1024
void handle_cleanup(STARTUPINFOW,  PROCESS_INFORMATION);
DWORD WINAPI ReadPipe(LPVOID);

HANDLE CMD_WRITE, PARENT_READ, CMD_READ, PARENT_WRITE;

int CreateProcessCMD(void){
   DWORD ThreadID;
   DWORD bytes_written;
   HANDLE hProcessCMD;
   
   STARTUPINFOW PSTARTUPINFO;
   PROCESS_INFORMATION PPROCESSINFO;
   SECURITY_ATTRIBUTES SECURITYATTR;

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

   if(!CreatePipe(&PARENT_READ,&CMD_WRITE, &SECURITYATTR,0)){
       printf("Could not create pipe");
       return EXIT_FAILURE;
   }
   printf("CreatePipe(PARENT_READ, CMD_WRITE) -  Success!\n");
   
   if(!CreatePipe(&CMD_READ,&PARENT_WRITE, &SECURITYATTR,0)){
      printf("Could not create pipe");
      return EXIT_FAILURE;
   }
   printf("CreatePipe(CMD_READ, PARENT_WRITE) - Success!\n");

   ZeroMemory(&PSTARTUPINFO, sizeof(STARTUPINFO));
   printf("ZeroMemory(&PSTARTUPINFO, sizeof(STARTUPINFO) -  Zeroing STARTUPINFO structure success!\n");
   
   PSTARTUPINFO.cb = sizeof(STARTUPINFO);
   PSTARTUPINFO.hStdOutput = CMD_WRITE;
   printf("CMD_WRITE ---> %x\n", CMD_WRITE);
   printf("STARTINFO.hStsOutput ---> %x\n", PSTARTUPINFO.hStdOutput);
   PSTARTUPINFO.hStdInput = CMD_READ;
   printf("CMD_READ ---> %x\n", CMD_READ);
   printf("STARTINFO.hStdsInput ---> %x\n", PSTARTUPINFO.hStdInput);
   PSTARTUPINFO.dwFlags |= STARTF_USESTDHANDLES;
   PSTARTUPINFO.dwFlags |= STARTF_USESHOWWINDOW;
   PSTARTUPINFO.wShowWindow = SW_SHOWNORMAL;
   BOOL success = CreateProcessW(L"C:\\Windows\\System32\\cmd.exe",
                      NULL,
                      NULL,
                      NULL,
                      TRUE,
                      NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE,
                      NULL,
                      NULL,
                      &PSTARTUPINFO,
                      &PPROCESSINFO);
   if(!success){
      printf("CreateProcessW() --> Could not create process!\n");
      printf("[-] Closing program");
      return EXIT_FAILURE;
   }
   
   printf("C:\\Windows\\System32\\cmd.exe started with PID ---> %i\n", PPROCESSINFO.dwProcessId);

   HANDLE hThread = CreateThread(NULL, 0, ReadPipe, NULL, 0, &ThreadID);
   if(hThread == NULL){
      printf("CreateThread() --> Failed, Returned %i\n", GetLastError());
      return EXIT_FAILURE;
   }
   printf("CreateThread() --> New thread created with TID --> %i\n", ThreadID);
   Sleep(2000);
      
   while(TRUE){   
      char cmd[256];
      printf("Prompt> ");
      gets(cmd);
      WriteFile(PARENT_WRITE, cmd, strlen(cmd), &bytes_written, NULL);
      printf("WriteFile() --> Wrote %i bytes\n", bytes_written);
   }
   
   hProcessCMD = PPROCESSINFO.hProcess;
   switch (WaitForSingleObject(hProcessCMD, INFINITE))
   {
   case WAIT_ABANDONED :
      printf("WaitForSingleObject(hProcess, Timeout) ---> Returned WAIT_ABANDONED\n");
      break;
   case WAIT_OBJECT_0:
      printf("WaitForSingleObject(hProcess, Timeout) ---> The state of the specified object is signaled (Process has terminated)\n");
      break;
   case WAIT_TIMEOUT:
      printf("WaitForSingleObject(hProcess, Timeout) ---> Returned WAIT_TIMEOUT\n");
      break;
   case WAIT_FAILED:
      printf("WaitForSingleObject(hProcess, Timeout) ---> Failed, Returned &i\n", GetLastError());
      break;
   default:
      break;
   }
   

   handle_cleanup(PSTARTUPINFO, PPROCESSINFO);
   return EXIT_SUCCESS;
}

int main(void){

   printf("Parent PID ------> %u\n", GetCurrentProcessId());
   if(CreateProcessCMD()){
      printf("CreateProcessCMD() - Function returned EXIT_FAILURE\n");
      return EXIT_FAILURE;
   }
   return EXIT_SUCCESS;
}

void handle_cleanup(STARTUPINFOW startupinfo,  PROCESS_INFORMATION  processinfo ){
    if(!CloseHandle(startupinfo.hStdInput)) printf("[-] Could not close stdin handle\n");
    if(!CloseHandle(startupinfo.hStdOutput)) printf("[-] Could not close stdout handle\n");
    if(!CloseHandle(startupinfo.hStdError)) printf("[-] Could not close stderr handle\n");
    if(!CloseHandle(processinfo.hProcess)) printf("[-] Could not close process handle\n");
    if(!CloseHandle(processinfo.hThread)) printf("[-] Could not close thread handle\n");
}


DWORD  WINAPI ReadPipe(LPVOID lpThreadParameter){
     char buffer[BUFFER_LENGTH];
     
     DWORD bytes_read_from_pipe;
     while(TRUE) {
        int ret = ReadFile(PARENT_READ, buffer, BUFFER_LENGTH, &bytes_read_from_pipe, NULL);
        printf("ReadFile() --> Read %i\n", bytes_read_from_pipe);
        if(bytes_read_from_pipe>0){
           printf("%s\n", buffer);
           ZeroMemory(buffer, BUFFER_LENGTH);
           bytes_read_from_pipe = 0;
           continue;
        }
        break;
   }
}

原运行输出

Parent PID ------> 12064
CreatePipe(PARENT_READ, CMD_WRITE) -  Success!
CreatePipe(CMD_READ, PARENT_WRITE) - Success!
ZeroMemory(&PSTARTUPINFO, sizeof(STARTUPINFO) -  Zeroing STARTUPINFO structure success!
CMD_WRITE ---> 100
STARTINFO.hStsOutput ---> 100
CMD_READ ---> 104
STARTINFO.hStdsInput ---> 104
C:\Windows\System32\cmd.exe started with PID ---> 8700
CreateThread() --> New thread created with TID --> 16680
ReadFile() --> Read 43
Microsoft Windows [Version ...]
ReadFile() --> Read 89

(c) Microsoft Corporation. All rights reserved.

C:\Users\<USER>\Sandbox\>
Prompt> where calc.exe
WriteFile() --> Wrote 14 bytes
Prompt>
WriteFile() --> Wrote 0 bytes

问题分析与修复方案

1. 命令缺少换行符,cmd未执行

cmd.exe需要接收到\r\n换行符才会执行输入的命令。当前代码仅写入命令文本,cmd处于等待输入完成的状态,不会输出结果。

修复:写入命令时追加\r\n,并处理输入的换行符:

char cmd[256];
printf("Prompt> ");
fgets(cmd, sizeof(cmd), stdin);
size_t cmd_len = strlen(cmd);
if (cmd[cmd_len-1] == '\n') {
    cmd[cmd_len-1] = '\0'; // 去掉fgets读取的换行
}
strcat(cmd, "\r\n"); // 追加cmd需要的换行
WriteFile(PARENT_WRITE, cmd, strlen(cmd), &bytes_written, NULL);

2. 未重定向标准错误输出

cmd的错误输出默认未进入管道,部分场景下会导致阻塞。需要将hStdError也指向CMD_WRITE管道:

PSTARTUPINFO.hStdError = CMD_WRITE;

3. 管道句柄继承问题

创建子进程时开启了句柄继承,但父进程的PARENT_READ和PARENT_WRITE不需要被子进程继承,否则子进程会持有这些句柄,导致管道不会被正确关闭,ReadFile会一直等待。

修复:创建管道后,关闭这两个句柄的继承属性:

// 创建PARENT_READ/CMD_WRITE后
SetHandleInformation(PARENT_READ, HANDLE_FLAG_INHERIT, 0);
// 创建CMD_READ/PARENT_WRITE后
SetHandleInformation(PARENT_WRITE, HANDLE_FLAG_INHERIT, 0);

4. ReadPipe线程错误处理完善

当ReadFile返回FALSE时,需要检查错误码,比如ERROR_BROKEN_PIPE表示管道已关闭,此时应该退出线程:

DWORD WINAPI ReadPipe(LPVOID lpThreadParameter){
     char buffer[BUFFER_LENGTH];
     DWORD bytes_read_from_pipe;
     while(TRUE) {
        BOOL ret = ReadFile(PARENT_READ, buffer, BUFFER_LENGTH-1, &bytes_read_from_pipe, NULL);
        if (!ret) {
            DWORD err = GetLastError();
            if (err == ERROR_BROKEN_PIPE) {
                printf("ReadPipe: Pipe broken, exiting thread\n");
                break;
            } else {
                printf("ReadFile failed, error: %lu\n", err);
                break;
            }
        }
        if(bytes_read_from_pipe>0){
           buffer[bytes_read_from_pipe] = '\0'; // 确保字符串终止
           printf("ReadFile() --> Read %lu bytes\n", bytes_read_from_pipe);
           printf("%s", buffer); // 输出内容,避免重复换行
           ZeroMemory(buffer, BUFFER_LENGTH);
        }
   }
   return 0;
}

5. 替换不安全的gets函数

gets存在缓冲区溢出风险,改用fgets读取输入,同时处理换行符。


修复后的完整代码

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

#define BUFFER_LENGTH 1024
void handle_cleanup(STARTUPINFOW, PROCESS_INFORMATION);
DWORD WINAPI ReadPipe(LPVOID);

HANDLE CMD_WRITE, PARENT_READ, CMD_READ, PARENT_WRITE;

int CreateProcessCMD(void){
   DWORD ThreadID;
   DWORD bytes_written;
   HANDLE hProcessCMD;
   
   STARTUPINFOW PSTARTUPINFO;
   PROCESS_INFORMATION PPROCESSINFO;
   SECURITY_ATTRIBUTES SECURITYATTR;

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

   if(!CreatePipe(&PARENT_READ,&CMD_WRITE, &SECURITYATTR,0)){
       printf("Could not create pipe");
       return EXIT_FAILURE;
   }
   printf("CreatePipe(PARENT_READ, CMD_WRITE) -  Success!\n");
   SetHandleInformation(PARENT_READ, HANDLE_FLAG_INHERIT, 0);
   
   if(!CreatePipe(&CMD_READ,&PARENT_WRITE, &SECURITYATTR,0)){
      printf("Could not create pipe");
      return EXIT_FAILURE;
   }
   printf("CreatePipe(CMD_READ, PARENT_WRITE) - Success!\n");
   SetHandleInformation(PARENT_WRITE, HANDLE_FLAG_INHERIT, 0);

   ZeroMemory(&PSTARTUPINFO, sizeof(STARTUPINFO));
   printf("ZeroMemory(&PSTARTUPINFO, sizeof(STARTUPINFO) -  Zeroing STARTUPINFO structure success!\n");
   
   PSTARTUPINFO.cb = sizeof(STARTUPINFO);
   PSTARTUPINFO.hStdOutput = CMD_WRITE;
   PSTARTUPINFO.hStdInput = CMD_READ;
   PSTARTUPINFO.hStdError = CMD_WRITE;
   printf("CMD_WRITE ---> %p\n", CMD_WRITE);
   printf("STARTINFO.hStdOutput ---> %p\n", PSTARTUPINFO.hStdOutput);
   printf("CMD_READ ---> %p\n", CMD_READ);
   printf("STARTINFO.hStdInput ---> %p\n", PSTARTUPINFO.hStdInput);
   PSTARTUPINFO.dwFlags |= STARTF_USESTDHANDLES;
   PSTARTUPINFO.dwFlags |= STARTF_USESHOWWINDOW;
   PSTARTUPINFO.wShowWindow = SW_HIDE;
   BOOL success = CreateProcessW(L"C:\\Windows\\System32\\cmd.exe",
                      NULL,
                      NULL,
                      NULL,
                      TRUE,
                      NORMAL_PRIORITY_CLASS,
                      NULL,
                      NULL,
                      &PSTARTUPINFO,
                      &PPROCESSINFO);
   if(!success){
      printf("CreateProcessW() --> Could not create process! Error: %lu\n", GetLastError());
      printf("[-] Closing program");
      return EXIT_FAILURE;
   }
   
   printf("C:\\Windows\\System32\\cmd.exe started with PID ---> %lu\n", PPROCESSINFO.dwProcessId);

   CloseHandle(CMD_WRITE);
   CloseHandle(CMD_READ);

   HANDLE hThread = CreateThread(NULL, 0, ReadPipe, NULL, 0, &ThreadID);
   if(hThread == NULL){
      printf("CreateThread() --> Failed, Returned %lu\n", GetLastError());
      return EXIT_FAILURE;
   }
   printf("CreateThread() --> New thread created with TID --> %lu\n", ThreadID);
   Sleep(500);
      
   while(TRUE){   
      char cmd[256];
      printf("Prompt> ");
      if(fgets(cmd, sizeof(cmd), stdin) == NULL){
          break;
      }
      size_t cmd_len = strlen(cmd);
      if (cmd[cmd_len-1] == '\n') {
          cmd[cmd_len-1] = '\0';
      }
      strcat(cmd, "\r\n");
      if(!WriteFile(PARENT_WRITE, cmd, strlen(cmd), &bytes_written, NULL)){
          printf("WriteFile failed, error: %lu\n", GetLastError());
          break;
      }
      printf("WriteFile() --> Wrote %lu bytes\n", bytes_written);
   }
   
   CloseHandle(PARENT_WRITE);
   
   hProcessCMD = PPROCESSINFO.hProcess;
   switch (WaitForSingleObject(hProcessCMD, INFINITE))
   {
   case WAIT_ABANDONED :
      printf("WaitForSingleObject(hProcess, Timeout) ---> Returned WAIT_ABANDONED\n");
      break;
   case WAIT_OBJECT_0:
      printf("WaitForSingleObject(hProcess, Timeout) ---> The state of the specified object is signaled (Process has terminated)\n");
      break;
   case WAIT_TIMEOUT:
      printf("WaitForSingleObject(hProcess, Timeout) ---> Returned WAIT_TIMEOUT\n");
      break;
   case WAIT_FAILED:
      printf("WaitForSingleObject(hProcess, Timeout) ---> Failed, Returned %lu\n", GetLastError());
      break;
   default:
      break;
   }
   
   WaitForSingleObject(hThread, INFINITE);
   CloseHandle(hThread);

   handle_cleanup(PSTARTUPINFO, PPROCESSINFO);
   CloseHandle(PARENT_READ);
   return EXIT_SUCCESS;
}

int main(void){
   printf("Parent PID ------> %lu\n", (DWORD)GetCurrentProcessId());
   if(CreateProcessCMD() != EXIT_SUCCESS){
      printf("CreateProcessCMD() - Function returned EXIT_FAILURE\n");
      return EXIT_FAILURE;
   }
   return EXIT_SUCCESS;
}

void handle_cleanup(STARTUPINFOW startupinfo, PROCESS_INFORMATION processinfo ){
    if(!CloseHandle(processinfo.hProcess)) 
        printf("[-] Could not close process handle\n");
    if(!CloseHandle(processinfo.hThread)) 
        printf("[-] Could not close thread handle\n");
}

DWORD WINAPI ReadPipe(LPVOID lpThreadParameter){
     char buffer[BUFFER_LENGTH];
     DWORD bytes_read_from_pipe;
     while(TRUE) {
        BOOL ret = ReadFile(PARENT_READ, buffer, BUFFER_LENGTH-1, &bytes_read_from_pipe, NULL);
        if (!ret) {
            DWORD err = GetLastError();
            if (err == ERROR_BROKEN_PIPE) {
                printf("\nReadPipe: Pipe closed by cmd, exiting thread\n");
                break;
            } else {
                printf("\nReadFile failed, error: %lu\n", err);
                break;
            }
        }
        if(bytes_read_from_pipe>0){
           buffer[bytes_read_from_pipe] = '\0';
           printf("\nReadFile() --> Read %lu bytes:\n%s", bytes_read_from_pipe, buffer);
           ZeroMemory(buffer, BUFFER_LENGTH);
        }
   }
   return 0;
}

内容的提问来源于stack exchange,提问作者Yxd

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 23:40:45