Windows下实现控制台程序输出重定向到其他进程(iperf为例)
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了Windows下实现控制台程序输出重定向到其他进程(iperf为例),小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含10767字,纯文字阅读大概需要16分钟。
内容图文
![Windows下实现控制台程序输出重定向到其他进程(iperf为例)](/upload/InfoBanner/zyjiaocheng/952/95735dfbde8d4c0c9e416249ec527b0f.jpg)
本篇文章主要介绍windows下如何将一个应用程序的输出重定向到另一个进程中,主要原理是通过匿名管道实现进程信息的重定向。
工作中有时会遇到这样一个场景,应用程序A.exe需要调用应用程序B.exe进行一些辅助性操作,同时,A还需要获取B执行后的输出结果,以便于在A中根据B的输出结果进行相关的操作。本文主要解决的就是上述问题。以iperf为例,iperf是一个网络吞吐量测试工具,它可以测试当前的网络状况。本文使用一个控制台程序(A.exe)调用iperf.exe(B.exe)进行网络性能测试,然后将iperf.exe的输出信息重定位到A的控制台窗口进行输出。
系统环境:Win10_x64、VS2017
内容安排:
一、下载iperf;
二、代码实现。
一、下载iperf
1.iperf下载地址:Iperf for Windows
下载iperf后将解压得到的iperf3.exe、cygwin1.dll放入对应项目目录下供应用程序A调用。
注:简单起见,建议重定向你自己的exe文件而不是iperf,这只需要修改main函数中的命令行即可。
二、代码实现
代码利用匿名管道实现输出重定向,其中对重定向部分的代码进行了封装,便于调用。下面给出具体实现:
1.封装类cmdhandler.h实现:
#ifndef __CMD_HANDLER_H__
#define __CMD_HANDLER_H__
#include <Windows.h>
#include <string>
/* buffer的最大长度 */
#define PIPE_BUFFER_SIZE 4096
#define PORT 12306
/* 命令参数 */
typedef struct _CHCmdParam CHCmdParam;
struct _CHCmdParam
{
/* 初始化为 sizeof(CommandParam) */
int iSize;
/* 外部命令,不需要用则设置为-1, 提供给外部使用
*/
int iCommand;
/* 超时时间,单位秒 */
long long iTimeOut;
/* 命令行要执行的命令 */
char* szCommand;
/* 用户数据 */
void* pUserData;
/* 命令执行后的回调 */
void(*OnCmdEvent)(const CHCmdParam* pParam, HRESULT hResultCode, char* szResult);
};
class CCmdHandler
{
private:
BOOL m_bInit;
DWORD m_port;
STARTUPINFO m_startupInfo;
PROCESS_INFORMATION m_processInfo;
SECURITY_ATTRIBUTES m_saOutPipe;
DWORD m_dwErrorCode;
HANDLE m_hPipeRead;
HANDLE m_hPipeWrite;
CHCmdParam m_CommandParam;
TCHAR m_szReadBuffer[PIPE_BUFFER_SIZE];
TCHAR m_szWriteBuffer[PIPE_BUFFER_SIZE];
char m_szPipeOut[PIPE_BUFFER_SIZE];
HRESULT ExecuteCmdWait();
public:
CCmdHandler();
~CCmdHandler();
/*
* 客户端初始化接口,调用其余接口之前调用
* 成功返回S_OK
*/
HRESULT Cli_Initalize();
/*
* 服务器初始化接口,调用其余接口之前调用
* 成功返回S_OK
*/
HRESULT Ser_Initalize(DWORD port = PORT);
/*
* 结束接口
*/
HRESULT Finish();
/*
* 执行命令接口,接口调用成功返回S_OK
* param[in] pCommmandParam: 指向一个CHCmdParam命令参数结构的指针
*/
HRESULT HandleCommand(CHCmdParam* pCommmandParam);
/*
* 返回错误码,便于差距接口调用失败后产生什么错误
*/
DWORD GetErrorCode() { return m_dwErrorCode; }
};
#endif // !__CMD_HANDLER_H__
2.封装类cmdhandler.cpp实现:
#include "cmdhandler.h"
#include <tchar.h>
#include <string>
#ifdef WIN32
#include <Tlhelp32.h>
#endif
#include <algorithm>
#define EXCEPTIION_STATE_CHECK \
if (!m_bInit) return E_NOTIMPL
CCmdHandler::CCmdHandler()
: m_bInit(FALSE)
, m_dwErrorCode(0)
, m_hPipeRead(NULL)
, m_hPipeWrite(NULL)
, m_port(PORT)
{
ZeroMemory(m_szReadBuffer, sizeof(m_szReadBuffer));
ZeroMemory(m_szWriteBuffer, sizeof(m_szWriteBuffer));
ZeroMemory(&m_CommandParam, sizeof(m_CommandParam));
}
CCmdHandler::~CCmdHandler()
{
}
HRESULT CCmdHandler::Cli_Initalize()
{
// 初始化,创建匿名管道
if (m_bInit) return S_OK;
m_bInit = TRUE;
ZeroMemory(m_szReadBuffer, sizeof(m_szReadBuffer));
ZeroMemory(&m_saOutPipe, sizeof(m_saOutPipe));
m_saOutPipe.nLength = sizeof(SECURITY_ATTRIBUTES);
m_saOutPipe.lpSecurityDescriptor = NULL;
m_saOutPipe.bInheritHandle = TRUE;
ZeroMemory(&m_startupInfo, sizeof(STARTUPINFO));
ZeroMemory(&m_processInfo, sizeof(PROCESS_INFORMATION));
if (!CreatePipe(&m_hPipeRead, &m_hPipeWrite, &m_saOutPipe, PIPE_BUFFER_SIZE))
{
m_dwErrorCode = GetLastError();
return E_FAIL;
}
return S_OK;
}
HRESULT CCmdHandler::Ser_Initalize(DWORD port)
{
m_port = port;
// 初始化,创建匿名管道
if (m_bInit) return S_OK;
m_bInit = TRUE;
ZeroMemory(m_szReadBuffer, sizeof(m_szReadBuffer));
ZeroMemory(&m_saOutPipe, sizeof(m_saOutPipe));
m_saOutPipe.nLength = sizeof(SECURITY_ATTRIBUTES);
m_saOutPipe.lpSecurityDescriptor = NULL;
m_saOutPipe.bInheritHandle = TRUE;
ZeroMemory(&m_startupInfo, sizeof(STARTUPINFO));
ZeroMemory(&m_processInfo, sizeof(PROCESS_INFORMATION));
if (!CreatePipe(&m_hPipeRead, &m_hPipeWrite, &m_saOutPipe, PIPE_BUFFER_SIZE))
{
m_dwErrorCode = GetLastError();
printf("CreatePipe failed!\n");
return E_FAIL;
}
//检测系统中是否已开启iperf3服务
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (INVALID_HANDLE_VALUE == hSnapshot)
{
printf("error:invalid handle value!\n");
return E_FAIL;
}
PROCESSENTRY32 pi;
pi.dwSize = sizeof(PROCESSENTRY32); //第一次使用必须初始化成员
BOOL bRet = Process32First(hSnapshot, &pi);
std::string item("iperf3.exe");
while (bRet)
{
std::string process(pi.szExeFile);
transform(process.begin(), process.end(), process.begin(), ::tolower);
if (process.find(item) <= process.length())//find
{
HANDLE hProcess;
//printf("Find the process! id:%d\n", pi.th32ProcessID);
hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pi.th32ProcessID);
if (hProcess)
{
printf("Close %d precess successful!\n", pi.th32ProcessID);
TerminateProcess(hProcess, 0);
CloseHandle(hProcess);//OpenProcess打开的也要关闭 ?
}
else
{
printf("Close iperf precess failed!\n");
}
}
bRet = Process32Next(hSnapshot, &pi);
}//close all iperf3 services
CloseHandle(hSnapshot);
return S_OK;
}
HRESULT CCmdHandler::Finish()
{
EXCEPTIION_STATE_CHECK;
if (m_hPipeRead)
{
CloseHandle(m_hPipeRead);
m_hPipeRead = NULL;
}
if (m_hPipeWrite)
{
CloseHandle(m_hPipeWrite);
m_hPipeWrite = NULL;
}
return S_OK;
}
HRESULT CCmdHandler::HandleCommand(CHCmdParam* pCommmandParam)
{
EXCEPTIION_STATE_CHECK;
if (!pCommmandParam || pCommmandParam->iSize != sizeof(CHCmdParam))
return E_INVALIDARG;
if (strlen(pCommmandParam->szCommand) <= 0)
return E_UNEXPECTED;
memset(&m_CommandParam, 0, sizeof(m_CommandParam));
m_CommandParam = *pCommmandParam;
return ExecuteCmdWait();
}
HRESULT CCmdHandler::ExecuteCmdWait()
{
EXCEPTIION_STATE_CHECK;
HRESULT hResult = E_FAIL;
DWORD dwReadLen = 0;
DWORD dwStdLen = 0;
m_startupInfo.cb = sizeof(STARTUPINFO);
m_startupInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
m_startupInfo.hStdOutput = m_hPipeWrite;
m_startupInfo.hStdError = m_hPipeWrite;
m_startupInfo.wShowWindow = SW_HIDE;
DWORD dTimeOut = (DWORD)m_CommandParam.iTimeOut;// >= 3000 ? m_CommandParam.iTimeOut : 5000;
if (!CreateProcess(NULL, m_CommandParam.szCommand,
NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL,
&m_startupInfo, &m_processInfo))
{
m_dwErrorCode = GetLastError();
hResult = E_FAIL;
goto over;
}
//等待时间到达或进程退出,进程不退出将一直读取管道数据(每隔dTimeOut读取一次)
while (WAIT_TIMEOUT == WaitForSingleObject(m_processInfo.hProcess, dTimeOut))
{
// 预览管道中数据的内容
if (!PeekNamedPipe(m_hPipeRead, NULL, 0, NULL, &dwReadLen, NULL)
|| dwReadLen <= 0)
{
m_dwErrorCode = GetLastError();
hResult = E_FAIL;
continue;
}
else
{
ZeroMemory(m_szPipeOut, sizeof(m_szPipeOut));
// 读取管道中的数据
if (ReadFile(m_hPipeRead, m_szPipeOut, dwReadLen, &dwStdLen, NULL))
{
hResult = S_OK;
if (m_CommandParam.OnCmdEvent)
m_CommandParam.OnCmdEvent(&m_CommandParam, S_OK, m_szPipeOut);
//break;
}
else
{
m_dwErrorCode = GetLastError();
//break;
}
}
}
// 预览管道中数据的内容
if (!PeekNamedPipe(m_hPipeRead, NULL, 0, NULL, &dwReadLen, NULL)
|| dwReadLen <= 0)
{
m_dwErrorCode = GetLastError();
hResult = E_FAIL;
//continue;
}
else
{
ZeroMemory(m_szPipeOut, sizeof(m_szPipeOut));
// 读取管道中的数据
if (ReadFile(m_hPipeRead, m_szPipeOut, dwReadLen, &dwStdLen, NULL))
{
hResult = S_OK;
if (m_CommandParam.OnCmdEvent)
m_CommandParam.OnCmdEvent(&m_CommandParam, S_OK, m_szPipeOut);
//break;
}
else
{
m_dwErrorCode = GetLastError();
//break;
}
}
over:
if (m_processInfo.hThread)
{
CloseHandle(m_processInfo.hThread);
m_processInfo.hThread = NULL;
}
if (m_processInfo.hProcess)
{
CloseHandle(m_processInfo.hProcess);
m_processInfo.hProcess = NULL;
}
return hResult;
}
3.测试main函数示例:
iperf的使用需要一个客户端和一个服务端,下面给出的示例为客户端的代码,服务端代码可直接copy一份该代码到另一台电脑上(此时两个电脑(需位于局域网内)上的两个应用程序将分别调用各自的iperf.exe并将iperf输出结果重定向到自身控制台中进行输出)。替换下列两句代码即可:
将语句
std::string szCmd = "cmd.exe /C iperf3.exe -u -c 192.168.1.1 -b 1000M -p 12306 -R -l 60k && echo S_OK || echo E_FAIL";//cmd执行成功返回S_OK,失败返回E_FAIL 192.168.1.1为服务器地址
注释,并替换为:
std::string szCmd = "cmd.exe /C iperf3.exe -s -p 12306";
将语句
cmdResult = cmdHandler.Cli_Initalize();
注释,并替换为:
cmdResult = cmdHandler.Ser_Initalize();
即可。
当然,简单起见,也可以不使用iperf而直接修改命令行(定义szCmd的值,如szCmd = "cmd.exe /C 123.exe";),使用你需要重定向的*.exe文件。
// iperf.cpp : This file contains the 'main' function. Program execution begins and ends there.
//
#include <iostream>
#include <Windows.h>
#include <stdio.h>
#include <tchar.h>
#include <string>
#include "cmdhandler.h"
#define SYSTEM_PAUSE system("pause")
//回调函数,用于打印输出信息
void OnCommandEvent(const CHCmdParam* pParam, HRESULT hResultCode, char* szResult);
int main(int argc, TCHAR** argv)
{
CHCmdParam cmdParam;
CCmdHandler cmdHandler;
HRESULT cmdResult = S_OK;
ZeroMemory(&cmdParam, sizeof(cmdParam));
cmdParam.iSize = sizeof(CHCmdParam);
//std::string szCmd = "cmd.exe /C iperf3.exe -s -p 12306";
std::string szCmd = "cmd.exe /C iperf3.exe -u -c 192.168.1.1 -b 1000M -p 12306 -R -l 60k && echo S_OK || echo E_FAIL";//cmd执行成功返回S_OK,失败返回E_FAIL 192.168.1.1为服务器地址
cmdParam.szCommand = (char*)szCmd.c_str();
cmdParam.OnCmdEvent = OnCommandEvent;
cmdParam.iTimeOut = 1000;//设置等待时间,每隔1s读取一次子进程管道数据
//cmdResult = cmdHandler.Ser_Initalize();
cmdResult = cmdHandler.Cli_Initalize();
if (cmdResult != S_OK)
{
printf("cmd handler init error\n");
SYSTEM_PAUSE;
return 0;
}
cmdResult = cmdHandler.HandleCommand(&cmdParam);
if (cmdResult != S_OK)
{
printf("cmd handler exec error\n");
cmdHandler.Finish();
SYSTEM_PAUSE;
return 0;
}
// std::string close("taskkill /f /t /im iperf3.exe");
// WinExec(close.c_str(), SW_HIDE);
system("pause");
return 0;
}
void OnCommandEvent(const CHCmdParam* pParam, HRESULT hResultCode, char* szResult)
{
if (!szResult || !szResult[0]) return;
if (!pParam || hResultCode != S_OK) return;
//printf("========================================\n");
printf("%s\n", szResult);
//printf("========================================\n");
}
iperf重定向的测试结果:
注意事项:
1.输出重定向时,被重定向进程(B.exe)输出每条信息时需要手动刷新缓冲区(通过fflush(stdout)语句实现),不然父进程不能立马读取到通过匿名管道传送的子进程信息,这点需要注意(参考博客2)。iperf默认输出信息时没有刷新,所以如果想每1s读取到重定向的输出信息,可手动修改源码(建议更换命令行进行其他exe测试而不是iperf,本文主要是需要重定向iperf才这么做的),不然只能等子进程退出时才可得到重定向的输出。
2.代码中设置只有子进程退出时才会得到重定向信息,如果需要实时获取重定向的输出信息,请在B.exe中每条打印语句后添加刷新缓冲区语句:fflush(stdout);。注意如果既没有添加刷新语句,子进程也一直不退出,则A.exe中无法看到重定向的信息。
3.示例代码中可设置读取管道数据的时间间隔,具体视实际情况而定。
4.修改示例中的szCmd的值即可重定向其他的exe文件。
内容总结
以上是互联网集市为您收集整理的Windows下实现控制台程序输出重定向到其他进程(iperf为例)全部内容,希望文章能够帮你解决Windows下实现控制台程序输出重定向到其他进程(iperf为例)所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。