c++ - 使用CreateProcess()和CreatePipe()从cmd.exe重定向I/O

标签 c++ winapi

我的问题是我无法使用管道从命令行获取输出。
我的任务:“将命令输入流重定向到cmd.exe |将cmd.exe命令的结果输出到主进程。”

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


using namespace std;



#define deffBuffSize 1024




int funSC() {

    HANDLE writeToCL, readFromCL, writeToProcess, readFromProcess;
    char lpCmdLine[] = "cmd.exe /k ipconfig";
    STARTUPINFOA siA;
    PROCESS_INFORMATION piApp;
    SECURITY_ATTRIBUTES secAttr;
    ZeroMemory(&secAttr, sizeof(SECURITY_ATTRIBUTES));
    ZeroMemory(&siA, sizeof(STARTUPINFOA));

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

    if (CreatePipe(&readFromCL, &writeToProcess, &secAttr, 0) == 0) {
        cout << "Create pipe error :: " << GetLastError() << endl;
        return 1;
    }
    if (!SetHandleInformation(writeToProcess, HANDLE_FLAG_INHERIT, 0)) {
        cout << "SetHandleInformation error :: " << GetLastError() << endl;
    }

    if (CreatePipe(&readFromProcess, &writeToCL, &secAttr, 0) == 0) {
        cout << "Create pipe error :: " << GetLastError() << endl;
        return 1;
    }
    if (!SetHandleInformation(readFromProcess, HANDLE_FLAG_INHERIT, 0)) {
        cout << "SetHandleInformation error :: " << GetLastError() << endl;
    }

    siA.cb = sizeof(STARTUPINFOA);
    siA.hStdInput = readFromProcess;
    siA.hStdOutput = writeToProcess;
    siA.hStdError = writeToProcess;
    siA.dwFlags = STARTF_USESTDHANDLES;


    if (CreateProcessA(NULL, lpCmdLine, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &siA, &piApp) == 0) {
        cout << "CreateProcessA error :: " << GetLastError() << endl;
        return 1;
    }
    else {
        CloseHandle(readFromProcess);
        CloseHandle(writeToProcess);
    }
    
    DWORD dRead = 0;
    char chBuff[deffBuffSize];
    bool bSuccess = FALSE;
    memset(chBuff, '\0', deffBuffSize);
    string outStd;
    bSuccess = ReadFile(readFromCL, chBuff, deffBuffSize, &dRead, NULL);

    cout << "bSuccess: " << bSuccess << endl;
    cout << "GetLastError: " << GetLastError() << endl;


    cout << "Message: " << chBuff;
    //CloseHandle(writeToCL);
    //CloseHandle(readFromCL);
    CloseHandle(piApp.hProcess);
    CloseHandle(piApp.hThread);
    return 0;
}

int main() {
    int result = 0;
    result = funSC();
    system("pause");
    return result;
}
当我执行ReadFile()时,我得到了结果。
ERROR_BROKEN_PIPE(109)-管道已结束。
据我了解,管道在记录结束时关闭=>问题:“为什么CreateProcess()应该执行“ipconfig”而不将其输出到覆盖的输出流中?”
我阅读了MSDN,试图使用现成的代码(以供理解),但这并没有带来积极的结果。
请帮助我,如果我了解如何解决此问题,我将非常高兴=)
PS:我不能“关闭”控制台窗口,因为我必须有一个有效的目录(例如:cd anyFolder)。如果我关闭该进程,则将丢失用户传递到的目录。
这就是我尝试从管道读取的方式
for (;;) {
        bSuccess = ReadFile(readFromCL, chBuff, deffBuffSize, &dRead, NULL);
        if (!bSuccess || dRead == 0) {
            break;
        }
        else {
            cout << "Message: " << chBuff;
        }
     }
这个无休止的循环什么也没读。
P.S:这是一个正在运行的程序的输出,但是仅输出“ipconfig”命令的一行
This is the output of a running program, but it only outputs one line of the "ipconfig" command

最佳答案

首先,由cmd.exe引起的ERROR_BROKEN_PIPE(109)错误意外退出。根据Creating a Child Process with Redirected Input and Output示例:

The parent process uses the opposite ends of these two pipes to write to the child process's input and read from the child process's output. As specified in the STARTUPINFO structure, these handles are also inheritable. However, these handles must not be inherited. Therefore, before creating the child process, the parent process uses the SetHandleInformation function to ensure that the write handle for the child process's standard input and the read handle for the child process's standard output cannot be inherited.


您已将其他两个句柄设置为未继承,以便cmd.exe退出时没有可用的标准句柄。
设置子进程的管道侧:
if (!SetHandleInformation(readFromCL, HANDLE_FLAG_INHERIT, 0)) {
    cout << "SetHandleInformation error :: " << GetLastError() << endl;
}
if (!SetHandleInformation(writeToCL, HANDLE_FLAG_INHERIT, 0)) {
    cout << "SetHandleInformation error :: " << GetLastError() << endl;
}
内容大小的问题可能是因为您需要等待cmd.exe的输出。在Sleep(1000)之前添加像ReadFile这样的函数可以解决它。然后,您可以选择最合适的方法来同步两个进程的输入和输出。例如在for循环中读取:
for (;;) {
    memset(chBuff, '\0', deffBuffSize);
    bSuccess = ReadFile(readFromCL, chBuff, deffBuffSize, &dRead, NULL);
    if (!bSuccess || dRead == 0) {
        break;
    }
    else {

        cout << chBuff;
    }
}

关于c++ - 使用CreateProcess()和CreatePipe()从cmd.exe重定向I/O,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63717924/

相关文章:

c++ - 如何在 makefile 中为目标编写特定规则?

java - 对 C/C++ 对象和 C/C++ 对象对 Java/Objective C 的 JSON 响应

windows - 获取批处理文件中 uuidgen.exe 返回的值

c++ - 方法指针映射,编译器说它们不接受任何参数

c++ - GetRawInputData 不适用于 x64 构建

c++ - 从 std::fstream 检索文件描述符

c++ - getcwd 第二个参数

c++ - 我应该返回 bool 还是 const bool?

c++ - 在 Qt OSE 项目中使用 Win32 API

c++ - 解释 HRESULT 的声明/定义