windows下,C++中调用命令行并且获取命令行的输出

说明,上一次写到了使用共享内存来实现获取命令行的输出。但是共享内存实际上是实现两个进程间的通信。其使用方式为在本进程创建共享内存,并在另外一个进程中调用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;
}



 

 

  • 12
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值