Windows进程间通信 匿名管道 共享内存 示例:MFC输入cmd命令 cmd执行后输出到MFC界面 不显示CMD窗口

一、管 道

管 道(Pipe)是用于进程间通信的一段共享内存。

可以改变程序默认的input与output与管道连接。创建了Pipe内核对象的进程就是一个Pipe Server, 当另一个进程与这个进程创建的Pipe Server连接时,就称为Pipe Client.当一个进程往Piple当中写入信息时,另一个进程便可以从这个Pipe读出这个信息。

二、示例

MFC输入cmd命令,cmd执行后,输出到MFC界面。不显示CMD窗口。

CreeatePipe(&hRead,&hWrite,&sa,0); //创建pipe内核对象,设置好hRead,hWrite.

管道2:cmd进程输入重定向到hReadChild从这里读,把MFC命令dir写到hWriteParent里,cmd进程会执行这个命令。
管道1:cmd进程输出重定向到hWriteChild向这里写,把MFC从hReadParent这里读,即可读出内容,输出dir结果

步骤:管道2发送ver(显示版本),管道1返回版本内容。

#include <stdio.h>
#include <Windows.h>
//cmd命令后面一定要加\n
int pipe_system(_In_opt_z_ const char* szBuffer)
{
	HANDLE hParentRead, hParentWrite, hChildRead, hChildWrite; //创建4个句柄

	//1.创建一个安全属性描述符,设置句柄可继承
	SECURITY_ATTRIBUTES sa = { 0 };                //安全属性描述符        
	sa.nLength = sizeof(SECURITY_ATTRIBUTES);
	sa.bInheritHandle = TRUE;                      //设置句柄可继承

	//2.创建两个管道.父读->子写 子读->父写的
	BOOL bRet = CreatePipe(&hParentRead, &hChildWrite, &sa, 0); //创建管道1. 父进程读 -> 子进程写入
	if (!bRet) { printf("error:%d", GetLastError()); return bRet; }

	bRet = CreatePipe(&hChildRead, &hParentWrite, &sa, 0);//创建管道2.  子进程读->父进程写.
	if (!bRet) { printf("error:%d", GetLastError()); return bRet; }

	PROCESS_INFORMATION pi = { 0 };                 //进程信息结构体
	STARTUPINFO si = { 0 };                         //启动信息结构体
	si.cb = sizeof(si);
	//3.重定向输出, 将子进程的读 以及子进程的写重定向.
	si.hStdInput = hChildRead;    //将子进程读取重定向到stdinput中
	si.hStdOutput = hChildWrite;  //将子进程写重定向到 stdout中. 
	si.dwFlags = STARTF_USESTDHANDLES;   //设置窗口隐藏启动
	//4.创建子进程
	bRet = CreateProcess(NULL, "cmd.exe", NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi);//创建cmd进程.默认寻找cmd进程.
	if (!bRet) { printf("error:%d", GetLastError()); return bRet; }

	//Sleep(1000);
	//DWORD dwFileSize = GetFileSize(hParentRead, NULL); //可以把头长度去掉

	//char szBuffer[] = "ver\n";
	//5.写入命令数据给子进程.
	DWORD dwWrite = 0; //实际写入字节数
	bRet = WriteFile(hParentWrite, szBuffer, strlen(szBuffer), &dwWrite, 0);//使用writeFile操作管道,给cmd发送数据命令.
	if (!bRet) { printf("error:%d", GetLastError()); return bRet; }

	//6.读出响应
	char pReadBuf[5000] = { 0 };
	DWORD dataLength = sizeof(pReadBuf);
	Sleep(100); //延时200ms
	if (NULL == ReadFile(hParentRead, pReadBuf, dataLength, &dataLength, NULL)) {  /*break;*/ } //读cmd显示内容
	printf("-----------------(%dByte)[%s]", dataLength, pReadBuf);

	//7.退出子进程 退出cmd
	//char *exit="exit\n"; 
	//WriteFile(hParentWrite, exit, strlen(exit), NULL, 0);//使用writeFile操作管道,给cmd发送数据命令.
	//if (!bRet) { printf("error:%d", GetLastError()); return bRet; }
	TerminateProcess(pi.hProcess, 0); //退出子进程

	CloseHandle(hParentWrite); //关闭写入句柄
	CloseHandle(hParentRead);
	CloseHandle(hChildWrite);
	CloseHandle(hChildRead);
	CloseHandle(pi.hThread); //关闭句柄
	CloseHandle(pi.hProcess);
	//printf("end\n");
	return 1;
}
int main()
{
	pipe_system("dir\n");
	pipe_system("ver\n");
	pipe_system("ipconfig\n");
	printf("end\n");
	return 0;
}

优化把cmd错误也定向到输出,改成类。

#include "StdAfx.h"
#include "CAdb.h"
//使用方法:adb.RunCmd("") 然后adb.waitResponse() 每次使用新管道再结束掉。打开子进程,运行命令,结束,stop,返回。

CAdb adb;
CAdb::CAdb(void)
{
	m_hEvent = NULL;
}
CAdb::~CAdb(void)
{}

UINT  adbReadProc(LPVOID  pVoid)
{
	CAdb *adb = (CAdb *)pVoid;
	while (true)
	{
		if (FALSE == adb->Read())
		{
			break;
		}
	}
	adb->Stop();
	logger.INFO_F("adbReadProc Exit");
	return true;
}
//读到的会内容m_strOutput
BOOL CAdb::Read()
{
	//6.读出响应
	char pReadBuf[5000] = { 0 };
	DWORD dataLength = sizeof(pReadBuf);
	if (FALSE == ReadFile(hParentRead, pReadBuf, dataLength, &dataLength, NULL)) {
		SetEvent(m_hEvent);
		logger.ERROR_F(FUN_LINE + "ReadFile ret=FALSE " + to_string(GetLastError()));
		return FALSE; //跳出循环
	} //读cmd显示内容
	if (dataLength > 0)
	{
		logger.INFO_F(string("-----------------(" + to_string(dataLength) + "Byte)[" + pReadBuf + "]"));
		m_strOutput += pReadBuf;

		if (m_strOutput.ReverseFind('>')>0 && StrStrA(m_strOutput.Right(5), "exit\n") != NULL) { //尾部找退出标志
			SetEvent(m_hEvent);
			return FALSE;//跳出循环
		}
		else
		{
			adb.Write("exit\n");//没有退出就使退出
		}
	}
	return TRUE;
}
//启动adb shell,如果成功则进入#状态,可以使用RunCmd执行命令
BOOL CAdb::Start()
{
	m_strOutput.Empty();
	//1.创建一个安全属性描述符,设置句柄可继承
	SECURITY_ATTRIBUTES sa = { 0 };                //安全属性描述符
	sa.nLength = sizeof(SECURITY_ATTRIBUTES);
	sa.bInheritHandle = TRUE;                      //设置句柄可继承

	//2.创建两个管道.父读->子写 子读->父写的
	BOOL bRet = CreatePipe(&hParentRead, &hChildWrite, &sa, 0); //创建管道1. 父进程读 -> 子进程写入
	if (!bRet) { logger.ERROR_F(__FUNCTION__ + string(" error:") + to_string(GetLastError()));  return bRet; }

	bRet = CreatePipe(&hChildRead, &hParentWrite, &sa, 0);//创建管道2.  子进程读->父进程写.
	if (!bRet) { logger.ERROR_F(__FUNCTION__ + string(" error:") + to_string(GetLastError()));  return bRet; }

	PROCESS_INFORMATION pi = { 0 };                 //进程信息结构体
	STARTUPINFO si = { 0 };                         //启动信息结构体
	si.cb = sizeof(si);
	//3.重定向输出, 将子进程的读 以及子进程的写重定向.
	si.hStdInput = hChildRead;    //将子进程读取重定向到stdinput中
	si.hStdOutput = hChildWrite;  //将子进程写重定向到 stdout中. 
	si.hStdError = hChildWrite;
	si.dwFlags = STARTF_USESTDHANDLES;   //设置窗口隐藏启动
	//4.创建子进程
	bRet = CreateProcess(NULL, "cmd.exe", NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi);//创建cmd进程.默认寻找cmd进程.
	if (!bRet) { logger.ERROR_F(__FUNCTION__ + string(" error:") + to_string(GetLastError()));  return bRet; }
	subProcess = pi.hProcess;
	//Sleep(1000);
	//DWORD dwFileSize = GetFileSize(hParentRead, NULL); //可以把头长度去掉



	AfxBeginThread(adbReadProc, (LPVOID)this);//启动新的线程

	m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
	//TerminateProcess(pi.hProcess, 0); //退出子进程
	//CloseHandle(pi.hThread); //关闭句柄
	//CloseHandle(pi.hProcess);
	return TRUE;
}

//这里输入的是在adb shell状态下的批处理命令,如果有多条请以\n分隔
BOOL CAdb::Write(CString strCmdline)
{
	if (strCmdline.Right(1).Compare("\n") != 0)
	{
		strCmdline += "\n"; //没有\n加上\n
	}
	//strCmdline += "exit\n"; //最后加 退出
	//5.写入命令数据给子进程.
	DWORD dwWrite = 0;
	BOOL bRet = WriteFile(hParentWrite, strCmdline, strCmdline.GetLength(), &dwWrite, 0);//使用writeFile操作管道,给cmd发送数据命令.
	if (!bRet) { logger.ERROR_F(__FUNCTION__ +string(" error:")+to_string(GetLastError())+ " "+strCmdline.GetBuffer());  return bRet; }
	return bRet;
}
BOOL CAdb::RunCmd(CString strCmdline)
{
	logger.INFO_F(__FUNCTION__ + string(":") + string(strCmdline.GetBuffer()));
	ResetEvent(m_hEvent);
	m_strOutput = ""; //清空

	if (adb.Start() == FALSE) { return FALSE; }//开始并启动接收

	return adb.Write(strCmdline);
}


//退出shell命令状态,关闭进程。不能通过TerminateProcess方式结束,否则会有读取不全的情况
BOOL CAdb::Stop()
{
	//7.退出子进程 退出cmd
	//RunCmd("exit\n");

	//TerminateProcess(subProcess, 0); //强制退出子进程 可能还没执行完
	if (GetLastError() == 0)
	{
		CloseHandle(hParentWrite); //关闭写入句柄
		CloseHandle(hParentRead);
		CloseHandle(hChildWrite);
		CloseHandle(hChildRead);
		CloseHandle(m_hEvent);
	}
	else
	{
		logger.ERROR_F(FUN_LINE + to_string(GetLastError()));
		pipe_system("taskkill /f /t /im cmd.exe"); //可能存在的全部结束掉
	}
	m_hEvent = NULL;
	return TRUE;
}
//等待包含的字段或超时  有信号即输出包含相应内容 return true 
BOOL CAdb::WaitResponse(CString strPass,CString strFail, DWORD dwMilliseconds)
{
	DWORD ret = WaitForSingleObject(m_hEvent, dwMilliseconds);
	if (ret == WAIT_OBJECT_0 && (StrStrA(m_strOutput, strFail) != NULL))
	{
		logger.ERROR_F(FUN_LINE + P(ret)+" Output contains strFail value.");
	}
	else if (ret == WAIT_OBJECT_0 && (StrStrA(m_strOutput, strPass) != NULL))
	{
		return TRUE;
	}
	else if (ret == WAIT_TIMEOUT)
	{
		logger.ERROR_F(FUN_LINE"WAIT_TIMEOUT ,"+P(dwMilliseconds)+"ms");
	}
	else
	{
		logger.ERROR_F(FUN_LINE + P(ret));
	}
	return FALSE;
}
CString CAdb::GetOutput()
{
	return  m_strOutput;
}

 

/*通过管道操作adb*/
#pragma once
#include <WinDef.h>
#include <atlstr.h>

class CAdb
{
public:
	CAdb(void);
	~CAdb(void);
public:		
	BOOL RunCmd(CString strCmdline);	//这里输入的是在adb 下的批处理命令,\n结束
	BOOL WaitResponse(CString strPass, CString strFail, DWORD dwMilliseconds);
	CString GetOutput();

public:
	BOOL Read();
	BOOL Stop();//关闭进程
private:
	BOOL Write(CString strCmdline);
	BOOL Start();//启动adb,如果成功,可以使用RunCmd执行命令
	CString m_strOutput;
	HANDLE hParentRead, hParentWrite, hChildRead, hChildWrite; //创建4个句柄
	HANDLE m_hEvent;
	HANDLE subProcess;
};

extern CAdb adb;

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小黄人软件

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值