没有控制台的 C++ popen 命令
Posted
技术标签:
【中文标题】没有控制台的 C++ popen 命令【英文标题】:C++ popen command without console 【发布时间】:2011-10-27 04:34:49 【问题描述】:当我使用 popen 获取命令的输出时,比如 dir,它会提示一个控制台。
但是,我可以在没有控制台出现的情况下获得命令的输出吗?
我正在使用 Visual C++,并希望创建一个库来返回某些命令的输出,例如 dir。
【问题讨论】:
您使用的是什么平台/工具链? 您使用的是什么操作系统?这不会在适当的操作系统中发生,例如Linux,但也许你正在使用例如Windows下的cygwin? 如果这是 Windows(我 100% 确定它是,因为多年来我已经为此奋斗了无数次),唯一可靠的方法是使用 CreateProcess。大多数其他库跳过了防止子控制台打开所必需的标志。 不显示命令窗口,也可以试试这个:***.com/a/16953192/453673 这是一个有效的解决方案! ***.com/a/43600962/82856 【参考方案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 库。 有用,但我必须使用不同的标志才能使其工作。看到这个:***.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。以上是关于没有控制台的 C++ popen 命令的主要内容,如果未能解决你的问题,请参考以下文章