【问题标题】:C++ popen command without console没有控制台的 C++ popen 命令
【发布时间】:2011-10-27 04:34:49
【问题描述】:

当我使用 popen 获取命令的输出时,比如 dir,它会提示一个控制台。

但是,我可以在没有控制台出现的情况下获得命令的输出吗?

我正在使用 Visual C++,并希望创建一个库来返回某些命令的输出,例如 dir。

【问题讨论】:

  • 您使用的是什么平台/工具链?
  • 您使用的是什么操作系统?这不会在适当的操作系统中发生,例如Linux,但也许你正在使用例如Windows下的cygwin?
  • 如果这是 Windows(我 100% 确定它是,因为多年来我已经为此奋斗了无数次),唯一可靠的方法是使用 CreateProcess。大多数其他库跳过了防止子控制台打开所必需的标志。
  • 不显示命令窗口,也可以试试这个:stackoverflow.com/a/16953192/453673
  • 这是一个有效的解决方案! stackoverflow.com/a/43600962/82856

标签: c++ console command popen


【解决方案1】:

也许是这样的? 此代码将返回输出,但必须等待进程完成。

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

int runCmd(const char* cmd, std::string& outOutput) {

    HANDLE g_hChildStd_OUT_Rd = NULL;
    HANDLE g_hChildStd_OUT_Wr = NULL;
    HANDLE g_hChildStd_ERR_Rd = NULL;
    HANDLE g_hChildStd_ERR_Wr = NULL;

    SECURITY_ATTRIBUTES sa;
    // Set the bInheritHandle flag so pipe handles are inherited.
    sa.nLength = sizeof(SECURITY_ATTRIBUTES);
    sa.bInheritHandle = TRUE;
    sa.lpSecurityDescriptor = NULL;
    if (!CreatePipe(&g_hChildStd_ERR_Rd, &g_hChildStd_ERR_Wr, &sa, 0))     { return 1; } // Create a pipe for the child process's STDERR.
    if (!SetHandleInformation(g_hChildStd_ERR_Rd, HANDLE_FLAG_INHERIT, 0)) { return 1; } // Ensure the read handle to the pipe for STDERR is not inherited.
    if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &sa, 0))     { return 1; } // Create a pipe for the child process's STDOUT.
    if (!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)) { return 1; } // Ensure the read handle to the pipe for STDOUT is not inherited

    PROCESS_INFORMATION piProcInfo;
    STARTUPINFO siStartInfo;
    bool bSuccess = FALSE;

    // Set up members of the PROCESS_INFORMATION structure.
    ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));

    // Set up members of the STARTUPINFO structure.
    // This structure specifies the STDERR and STDOUT handles for redirection.
    ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
    siStartInfo.cb = sizeof(STARTUPINFO);
    siStartInfo.hStdError  = g_hChildStd_ERR_Wr;
    siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
    siStartInfo.dwFlags |= STARTF_USESTDHANDLES;

    // Create the child process.
    bSuccess = CreateProcess(
        NULL,             // program name
        (char*)cmd,       // command line
        NULL,             // process security attributes
        NULL,             // primary thread security attributes
        TRUE,             // handles are inherited
        CREATE_NO_WINDOW, // creation flags (this is what hides the window)
        NULL,             // use parent's environment
        NULL,             // use parent's current directory
        &siStartInfo,     // STARTUPINFO pointer
        &piProcInfo       // receives PROCESS_INFORMATION
    );

    CloseHandle(g_hChildStd_ERR_Wr);
    CloseHandle(g_hChildStd_OUT_Wr);

    // read output
    #define BUFSIZE 4096
    DWORD dwRead;
    CHAR chBuf[BUFSIZE];
    bool bSuccess2 = FALSE;
    for (;;) { // read stdout
        bSuccess2 = ReadFile(g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);
        if(!bSuccess2 || dwRead == 0) break;
        std::string s(chBuf, dwRead);
        outOutput += s;
    }
    dwRead = 0;
    for (;;) { // read stderr
        bSuccess2 = ReadFile(g_hChildStd_ERR_Rd, chBuf, BUFSIZE, &dwRead, NULL);
        if(!bSuccess2 || dwRead == 0) break;
        std::string s(chBuf, dwRead);
        outOutput += s;
    }

    // The remaining open handles are cleaned up when this process terminates.
    // To avoid resource leaks in a larger application,
    // close handles explicitly.
    return 0;
}

int main(int argc, char* argv[]) {

    std::string output;
    runCmd("cmd /c dir", output);

    std::cout << output << std::endl;

    return 0;
}

【讨论】:

    【解决方案2】:

    我需要为我的全屏 OpenGL Windows 应用程序解决此问题,但无法阻止弹出控制台窗口。取而代之的是,在短暂的延迟后收回焦点似乎可以很好地避免看到它。

    _popen(cmd, "wb");
    
    Sleep(100);
    
    ShowWindow(hWnd, SW_SHOWDEFAULT);
    SetForegroundWindow(hWnd);
    

    更新:如果程序是从资源管理器启动的,这显然不起作用。从 Visual Studio 启动时它正在工作。

    【讨论】:

      【解决方案3】:

      假设 Windows(因为这是唯一普遍存在这种行为的平台):

      CreatePipe() 创建通信所需的管道,CreateProcess 创建子进程。

      HANDLE StdInHandles[2]; 
      HANDLE StdOutHandles[2]; 
      HANDLE StdErrHandles[2]; 
      
      CreatePipe(&StdInHandles[0], &StdInHandles[1], NULL, 4096); 
      CreatePipe(&StdOutHandles[0], &StdOutHandles[1], NULL, 4096); 
      CreatePipe(&StdErrHandles[0], &StdErrHandles[1], NULL, 4096); 
      
      
      STARTUPINFO si;   memset(&si, 0, sizeof(si));  /* zero out */ 
      
      si.dwFlags =  STARTF_USESTDHANDLES; 
      si.hStdInput = StdInHandles[0];  /* read handle */ 
      si.hStdOutput = StdOutHandles[1];  /* write handle */
      si.hStdError = StdErrHandles[1];  /* write handle */
      
      /* fix other stuff in si */
      
      PROCESS_INFORMATION pi; 
      /* fix stuff in pi */
      
      
      CreateProcess(AppName, commandline, SECURITY_ATTRIBUTES, SECURITY_ATTRIBUTES, FALSE, CREATE_NO_WINDOW |DETACHED_PROCESS, lpEnvironment, lpCurrentDirectory, &si, &pi); 
      

      这应该不仅仅是让你做你想做的事。

      【讨论】:

      • 对不起。有哪些必要的库?
      • MSDN 文档的链接告诉您要包​​含哪些头文件和库,但本例中的库是 WIN32 kernel32 库。
      • 有用,但我必须使用不同的标志才能使其工作。看到这个:stackoverflow.com/a/16953192/453673
      • 当我使用 DETACHED_PROCESS 标志时,我无法获得进程的输出。
      【解决方案4】:

      对于 POSIX,它应该是这样的:

      //Create the pipe.
      int lsOutPipe[2];
      pipe(lsOutPipe);
      
      //Fork to two processes.
      pid_t lsPid=fork();
      
      //Check if I'm the child or parent.
      if ( 0 == lsPid )
      {//I'm the child.
        //Close the read end of the pipe.
        close(lsOutPipe[0]);
      
        //Make the pipe be my stdout.
        dup2(lsOutPipe[1],STDOUT_FILENO);
      
        //Replace my self with ls (using one of the exec() functions):
        exec("ls"....);//This never returns.  
      } // if
      
      //I'm the parent.
      //Close the read side of the pipe.
      close(lsOutPipe[1]);
      
      //Read stuff from ls:
      char buffer[1024];
      int bytesRead;
      do
      {
        bytesRead = read(emacsInPipe[0], buffer, 1024);
      
        // Do something with the read information.
        if (bytesRead > 0) printf(buffer, bytesRead);
      } while (bytesRead > 0);
      

      您当然应该检查返回值等...

      【讨论】:

      • 什么是管道? Visual Studio 突出显示了这个词。
      • 是否应该包含任何库?
      • POSIX 是类 Unix 操作系统的标准。 Windows 不是其中之一。此代码适用于 Windows。
      猜你喜欢
      • 1970-01-01
      • 2021-04-12
      • 1970-01-01
      • 2014-02-22
      • 1970-01-01
      • 2014-02-23
      • 2023-03-23
      • 2019-10-23
      • 1970-01-01
      相关资源
      最近更新 更多