【问题标题】:Is there any way of stopping _popen opening a dos window?有没有办法阻止 _popen 打开一个dos窗口?
【发布时间】:2012-04-18 17:22:18
【问题描述】:

我正在使用 _popen 启动一个进程来运行命令并收集输出

这是我的 C++ 代码:

bool exec(string &cmd, string &result)
{
   result = "";

   FILE* pipe = _popen(cmd.c_str(), "rt");
   if (!pipe)
      return(false);

   char buffer[128];
   while(!feof(pipe))
   {
        if(fgets(buffer, 128, pipe) != NULL)
               result += buffer;
   }
   _pclose(pipe);
   return(true);
}

有没有办法在不打开控制台窗口的情况下执行此操作(就像当前在 _popen 语句中所做的那样)?

【问题讨论】:

标签: c++ windows shell popen


【解决方案1】:

在 Windows 上,具有 STARTUPINFO 结构的 CreateProcess 具有包含 STARTF_USESSHOWWINDOW 的 dwFlags。然后将 STARTUPINFO.dwFlags 设置为 SW_HIDE 将导致控制台窗口在触发时隐藏。示例代码(可能格式不正确,包含 C++ 和 WinAPI 的混合):

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

using std::cout;
using std::endl;

void printError(DWORD);

int main()
{
  STARTUPINFOA si = {0};
  PROCESS_INFORMATION pi = { 0 };

  si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
  si.wShowWindow = SW_HIDE;
  BOOL result = ::CreateProcessA("c:/windows/system32/notepad.exe", NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
  if(result == 0) {
      DWORD error = ::GetLastError();
      printError(error);
      std::string dummy;
      std::getline(std::cin, dummy);
      return error;
  }

  LPDWORD retval = new DWORD[1];
  ::GetExitCodeProcess(pi.hProcess, retval);
  cout << "Retval: " << retval[0] << endl;
  delete[] retval;

  cout << "Press enter to continue..." << endl;
  std::string dummy;
  std::getline(std::cin, dummy);

  return 0;
}

void printError(DWORD error) {
    LPTSTR lpMsgBuf = nullptr;
     FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER | 
        FORMAT_MESSAGE_FROM_SYSTEM |
        FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        error,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR) &lpMsgBuf,
        0, NULL );
     cout << reinterpret_cast<char*>(lpMsgBuf) << endl;
     LocalFree(lpMsgBuf);
}

【讨论】:

  • 通过我的尝试编辑 (stackoverflow.com/review/suggested-edits/4113333) 应该注意到,您不需要为 retval 调用 new[] delete[]。通过创建 DWORD retval = 0; 会更好,更容易阅读/维护;并传递 ::GetExitCodeProcess(pi.hProcess, &retval); cout 行也会从 retval[0] 变为 retval。
  • ::CreateProcessA() 和 ::GetLastError() 之前的空白命名空间是什么?
【解决方案2】:

据我所知,你不能1:你正在启动一个控制台应用程序(cmd.exe,它将运行指定的命令),Windows 总是在启动时创建一个控制台窗口一个控制台应用程序。


  1. 虽然,您可以在进程启动后隐藏窗口,或者如果您将适当的标志传递给CreateProcess,甚至可以将其隐藏;问题是,_popen 不传递这些标志,因此您必须使用 Win32 API 而不是 _popen 来创建管道。

【讨论】:

  • 我在想也许我可以运行一个子进程...可以这样做而不显示窗口吗?使用“系统”也会显示一个窗口吗? - 谢谢
  • 我看到让它隐藏窗口的唯一方法是直接调用CreateProcess,在wShowWindow 结构的wShowWindow 成员中指定SW_HIDE
【解决方案3】:

[最终编辑]

类似的 SO 问题合并了上述所有内容并为您提供输出 C++ popen command without console

[再次编辑]

呃。抱歉,我对产卵过程感到兴奋。我重读了你的q。除了额外的窗口之外,您实际上是在尝试获取进程的标准输出/标准错误。为此,我想补充一点,可悲的是,我的所有建议都无关紧要。但我会把它们留在这里以供参考。

[已编辑]

没有很好的具体原因(除了“打开”适用于 windows 和 mac 之外),我使用 ShellExecute 来生成进程而不是 CreateProcess。我稍后会研究..但这是我的 StartProcess 函数。

隐藏或最小化似乎产生相同的结果。 cmd 窗口确实出现了,但它被最小化了,并且永远不会在桌面上弹出,这可能是您的主要目标。

#if defined(PLATFORM_WIN32)
#include <Windows.h>
#include <shellapi.h>
#elif defined(PLATFORM_OSX)
#include <sys/param.h>
#endif
namespace LGSysUtils
{
// -----------------------------------------------------------------------
// pWindow      : {Optional} - can be NULL
// pOperation   : "edit", "explore", "find", "open", "print"
// pFile        : url, local file to execute
// pParameters  : {Optional} - can be NULL otherwise a string of args to pass to pFile
// pDirectory   : {Optional} - set cwd for process
// type         : kProcessWinNormal, kProcessWinMinimized, kProcessWinMaximized, kProcessHidden
//
bool StartProcess(void* pWindow, const char* pOperation, const char* pFile, const char* pParameters, const char* pDirectory, LGSysUtils::eProcessWin type)
{
    bool rc = false;
#if defined(PLATFORM_WIN32)

    int showCmd;
    switch(type)
    {
    case kProcessWinMaximized:
        showCmd = SW_SHOWMAXIMIZED;
        break;

    case kProcessWinMinimized:
        showCmd = SW_SHOWMINIMIZED;
        break;

    case kProcessHidden:
        showCmd = SW_HIDE;
        break;

    case kProcessWinNormal:
    default:
        showCmd = SW_NORMAL;
    }

    int shellRC = (int)ShellExecute(reinterpret_cast<HWND>(pWindow), pOperation,pFile,pParameters,pDirectory,showCmd);
    //Returns a value greater than 32 if successful, or an error value that is less than or equal to 32 otherwise.
    if( shellRC > 32 )
    {
        rc = true;
    }

#elif defined(PLATFORM_OSX)
    char cmd[1024];
    sprintf(cmd, "%s %s", pOperation, pFile);
    int sysrc = system( cmd );
    dbPrintf("sysrc = %d", sysrc);
    rc = true;
#endif
    return rc;
}

}

[和前面提到的]

如果您可以控制启动的应用程序的源代码,您可以尝试将其添加到您的 main.cpp 的顶部(或您命名的任何名称)

// make this process windowless/aka no console window
#pragma comment( linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"" )

您也可以直接将这些选项提供给链接器。对于不同的构建配置,以上内容更容易使用。

【讨论】:

  • 不是那么容易,因为_popen 总是启动cmd.exe 来运行给定的命令,并且超过cmd.exe 链接器标志你无法控制。
  • 感谢您的建议,但很遗憾我无法控制启动的代码。
  • @user236520 您不一定需要访问源代码。如果您自己提供二进制文件,则可以使用十六进制编辑器更改 PE 标头中的子系统。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-05
  • 1970-01-01
  • 1970-01-01
  • 2022-06-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多