一、管 道
管 道(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;