说明,上一次写到了使用共享内存来实现获取命令行的输出。但是共享内存实际上是实现两个进程间的通信。其使用方式为在本进程创建共享内存,并在另外一个进程中调用cmd将结果写入到共享内存中,实际没有处理如何调用命令行的问题,与本文的主题不是特别符合。因此这里将其删去,并增加了使用boost库(因为在项目中使用不方便贴代码,有空的时候另外配环境写个demo)的方法。共享内存的话,就偷个懒转个msdn上的demo,Creating Named Shared Memory - Win32 apps | Microsoft Docs
最近在做C++时遇到了一个棘手的问题,我需要获取组策略中的密码配置策略,但是本地用户没有权限。只能使用转调命令行来获取密码配置的输出,因此对C++中调用命令行的几种方式进行了汇总整理。
1. system,system函数在不同平台下都有实现,但是system函数无法获取cmd执行的结果
#include "stdafx.h"
/**
* @brief 通过system调用命令行
*/
void cmdSystem(const std::string& cmdLine) {
system(cmdLine.c_str());
}
int main(int argc, char* argv[])
{
std::string cmdLine(R"("echo Hello,World!")");
cmdSystem(cmdLine);
system("pause");
return 0;
}
2. _popen,使用匿名管道执行cmd命令并获取执行结果
/**
* @brief 通过_popen调用命令行并获取输出结果
*/
std::string cmdPopen(const std::string& cmdLine) {
char buffer[1024] = { '\0' };
FILE* pf = NULL;
pf = _popen(cmdLine.c_str(), "r");
if (NULL == pf) {
printf("open pipe failed\n");
return std::string("");
}
std::string ret;
while (fgets(buffer, sizeof(buffer), pf)) {
ret += buffer;
}
_pclose(pf);
return ret;
}
int main(int argc, char* argv[])
{
std::string cmdLine(R"("echo Hello,World!")");
// cmdSystem(cmdLine);
std::string tmp=cmdPopen(cmdLine);
printf("the resule of cmd is %s", tmp.c_str());
system("pause");
return 0;
}
3.CreateProcess(windows下)
#include <windows.h>
/**
* @brief 通过CreateProcess调用命令行并获取输出结果
*/
std::string cmdProcess(const std::string& cmdLine) {
/* 创建匿名管道 */
SECURITY_ATTRIBUTES _security = { 0 };
_security.bInheritHandle = TRUE;
_security.nLength = sizeof(_security);
_security.lpSecurityDescriptor = NULL;
HANDLE hRead = NULL, hWrite = NULL;
if (!CreatePipe(&hRead, &hWrite, &_security, 0)) {
printf("创建管道失败,error code=%d \n", GetLastError());
}
/* cmd命令行转换为Unicode编码 */
int convLength = MultiByteToWideChar(CP_UTF8, 0, cmdLine.c_str(), (int)strlen(cmdLine.c_str()), NULL, 0);
if (convLength <= 0) {
printf("字符串转换长度计算出错\n");
}
std::wstring wCmdLine;
wCmdLine.resize(convLength + 10);
convLength = MultiByteToWideChar(CP_UTF8, 0, cmdLine.c_str(), (int)strlen(cmdLine.c_str()), &wCmdLine[0], (int)wCmdLine.size());
if (convLength <= 0) {
printf("字符串转换出错\n");
}
/* 创建新进程执行cmd命令并将结果写入到管道 */
PROCESS_INFORMATION pi = { 0 };
STARTUPINFO si = { 0 };
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.wShowWindow = SW_HIDE; // 隐藏cmd执行的窗口
si.hStdError = hWrite;
si.hStdOutput = hWrite;
if (!CreateProcess(NULL,
&wCmdLine[0],
NULL,
NULL,
TRUE,
0,
NULL,
NULL,
&si,
&pi)) {
printf("创建子进程失败,error code=%d \n", GetLastError());
}
/* 等待进程执行命令结束 */
::WaitForSingleObject(pi.hThread, INFINITE);
::WaitForSingleObject(pi.hProcess, INFINITE);
/* 从管道中读取数据 */
DWORD bufferLen = 10240;
char *buffer =(char*)malloc(10240);
memset(buffer, '\0', bufferLen);
DWORD recLen = 0;
if (!ReadFile(hRead, buffer, bufferLen, &recLen, NULL)) {
printf("读取管道内容失败, error code=%d\n", GetLastError());
}
std::string ret(buffer);
/* 关闭句柄 */
CloseHandle(hRead);
CloseHandle(hWrite);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
free(buffer);
return ret;
}
int main(int argc, char* argv[])
{
std::string cmdLine(R"("ipconfig")");
// cmdSystem(cmdLine);
//std::string tmp=cmdPopen(cmdLine);
std::string tmp = cmdProcess(cmdLine);
printf("the resule of cmd is %s", tmp.c_str());
system("pause");
return 0;
}
4. 使用boost库的child类(过了一年再来填坑)
boost库提供了一个好用的跨平台的方案,实现也相当简单优雅。并且提供进程控制和获取返回值等多种强大功能。具体的功能的话就直接参见boost文档里,这里直接上测试代码和测试结果
#include <iostream>
#include <boost/process.hpp>
using namespace std;
using namespace boost::process;
/**
* @brief 启动命令行,并获取命令行的数据
* 执行proc1.exe,proc1.exe会输出Hello,proc1
* 在当前进程中读取子进程的输出
*/
static void startCmd()
{
int result = boost::process::system("proc1.exe", /* 要执行的命令或者程序,如果有参数的话可以在后面追加 */
boost::process::std_out > stdout /* 重定向子进程的输出流,方便查看一下输出
也可以重定向到当前任何刘或者文件
如果要读取数据的话可以直接从流中读取 */
);
}
/**
* @brief 启动子进程,并获取子进程中的数据
*/
static void startChildProc()
{
boost::process::ipstream is;
child c("proc1.exe", boost::process::std_out > is); /* 注意:这里的proc1.exe是实现准备好的一个测试程序
会使用std::out输出一些数据
z这里重定向到一个流对象,然后逐行读取其输出*/
std::vector<std::string> data;
std::string line;
while (c.running() && std::getline(is, line) && !line.empty())
data.push_back(line);
c.wait();
for (const auto & s : data) {
cout << s << endl;
}
}
int main(int argc, char* argv[])
{
cout<<"Hello, proc2"<<endl;
cout << "proc2 pid:" << boost::this_process::get_id() << endl;
//startCmd();
startChildProc();
return 0;
}