【问题标题】:Redirecting I / O from cmd.exe using CreateProcess () and CreatePipe()使用 CreateProcess () 和 CreatePipe() 从 cmd.exe 重定向 I/O
【发布时间】:2020-09-03 06:20:28
【问题描述】:

我的问题是我无法使用管道从命令行获取输出。

我的任务:“将命令输入流重定向到 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”命令

【问题讨论】:

  • 作为这里的新用户,请拨打tour并阅读How to Ask。关于您的问题,有两个注意事项:它缺少minimal reproducible example,并且不清楚您的问题是特定于 cmd.exe 还是发生在任何控制台应用程序上。
  • @UlrichEckhardt 在你面前,你看到了我需要的真实代码。至于其他控制台应用程序,除了 cmd .exe 之外我没有做过测试
  • @UlrichEckhardt 或者你是什么意思?
  • 该代码不能用于重现您的问题,它甚至没有main() 等,因此无法编译。你的任务是在你输入问题之前从代码中删除任何不必要的内容以发布到这里,作为缩小问题范围的一种手段。
  • SetHandleInformation(writeToProcess, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT),也将最后一个参数设置为HANDLE_FLAG_INHERIT。并将secAttr的初始化放在调用CreatePipe之前。然后它对我有用。

标签: c++ winapi


【解决方案1】:

首先,cmd.exe意外退出导致ERROR_BROKEN_PIPE(109)错误。根据样本Creating a Child Process with Redirected Input and Output

父进程使用这两个管道的相反端来写 到子进程的输入并从子进程的输出中读取。 正如 STARTUPINFO 结构中所指定的,这些句柄也是 可继承。 但是,这些句柄不能被继承。所以, 在创建子进程之前,父进程使用 SetHandleInformation 函数确保写句柄为 子进程的标准输入和子进程的读取句柄 进程的标准输出不能被继承。

您已将其他两个句柄设置为未继承,因此 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 的输出。在ReadFile之前添加类似:Sleep(1000)的功能,简单地解决它。然后,您可以选择最合适的方法来同步两个进程的输入和输出。比如在for循环中读取:

for (;;) {
    memset(chBuff, '\0', deffBuffSize);
    bSuccess = ReadFile(readFromCL, chBuff, deffBuffSize, &dRead, NULL);
    if (!bSuccess || dRead == 0) {
        break;
    }
    else {

        cout << chBuff;
    }
}

【讨论】:

  • 非常感谢。你救了我很多。
  • Sleep 听起来不像是本主题中提出的任何问题的解决方案
  • 是的,谢谢指出。但是只是用来证明问题的原因,OP可以选择最合适的方法来同步两个进程的输入输出。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-09
  • 2010-10-22
相关资源
最近更新 更多