MFC匿名管道原理详解、函数总结、调用实例(用MFC的匿名管道读取CMD输出内容)(C++语言)

本博客主要总结MFC中匿名管道的原理和具体调用实例,以及调用匿名管道三个核心函数各个参数用法详解,具体的如下所述。

博主在做项目时,遇到一个问题。用程序调用一个进程,然后读取进程输出信息。但是,博主用Qt的QProcess无法读取标准输出,所以只能考虑管道技术。

由于博主的开发环境是Windows10 64位,Qt的QProcess并没有找到类似的功能(可能博主对Qt研究不够深入,希望知道的大神告知一下),所以打算用MFC的匿名管道。

经过资料的查找和验证,博主发现,MFC的匿名管道技术可以实现读取CMD进程输出的内容,所以特定将MFC匿名管道用法记录下来,学习和总结。

 

匿名管道概念解释:

匿名管道主要用于进程间通信,进程的关系为父进程和子进程。具体原理如下图所示:

父进程和子进程都有读端口和写端口。通信方式可以从父进程写数据,子进程读数据。或者子进程写数据,父进程读数据。

其中,本文下面的例子是用图二的从子进程写数据,从父进程读取数据。即子进程通过cmd命令执行程序,然后程序输入的内容通过写句柄hWritePipe,写入内核(直接调用CrateProcess()函数,设置对应参数就可以写数据到内核。而不用调用WriteFile()函数)。父进程根据子进程管道的读句柄hReadPipe,调用ReadFile()函数读取内核数据。

 

一、MFC创建管道主要步骤

用MFC编写匿名管道的核心函数有三个,分别是CreatePipe(),CreateProcess(),ReadFile()三个核心函数。其中,函数CreatePipe()主要功能是创建一个管道通信,函数CreateProcess()主要功能是创建一个进程,函数ReadFile()读取进程输出的内容。下面是对创建管道通信步骤的总结:

 

1.1先创建调用函数CreatePipe()创建一个管道通信。关键代码为:

CreatePipe(&hReadPipe, &hWritePipe, &safety, 0);

 

1.2调用函数CreateProcess()创建一个进程,该进程输出的内容作为管道的写端口,向管道写数据。关键代码为:(其中写的句柄hWritePipe在结构体startInfo里的参数设置)

CreateProcess(NULL, cmdStr, NULL, NULL, TRUE, NULL, NULL, NULL, &startupInfo, &pinfo);

 

1.3调用函数ReadFile()作为管道的读端口,读取进程写入管道的内容。关键代码为:(其中读取内存存在缓冲区buffer)

ReadFile(hReadPipe, buffer, 4095, &byteRead, NULL);

 

1.4下面是博主封装的一个函数。该函数的功能是输入cmd命令,返回cmd输出内容。

具体代码如下所示:

CString executeCmd(CString command)
{
	//创建匿名管道
	HANDLE hReadPipe, hWritePipe;
	SECURITY_ATTRIBUTES safety;						//安全属性	
	safety.nLength = sizeof(SECURITY_ATTRIBUTES);	//结构体大小
	safety.lpSecurityDescriptor = NULL;				//安全描述符,NULL;使用默认的
	safety.bInheritHandle = TRUE;					//安全描述符的对象能否被子进程继承
	if (!CreatePipe(&hReadPipe, &hWritePipe, &safety, 0))
	{
		//创建管道错误
		return _T("创建管道错误!");
	}

	//创建进程
	TCHAR *cmdStr = StringToChar(command);
	STARTUPINFO startupInfo = { sizeof(startupInfo) };					//进程信息	
	startupInfo.hStdError = hWritePipe;									//标志控制台窗口缓存
	startupInfo.hStdOutput = hWritePipe;								//标志控制台窗口缓存
	startupInfo.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;	//使用wSHOWWIndows成员
	startupInfo.wShowWindow = SW_HIDE;
	PROCESS_INFORMATION pinfo;
	if (!CreateProcess(NULL, cmdStr, NULL, NULL, TRUE, NULL, NULL, NULL, &startupInfo, &pinfo))
	{
		//创建进程错误
		return _T("创建进程错误!");
	}
	CloseHandle(hWritePipe);

	//获取管道信息
	char buffer[4096];
	memset(buffer, 0, 4096);
	DWORD byteRead;
	CString output;									//返回值
	while (true)
	{
		if (ReadFile(hReadPipe, buffer, 4095, &byteRead, NULL) == NULL)
		{
			break;
		}
		output += buffer;
	}
	CloseHandle(hReadPipe);

	return output;
}
TCHAR* StringToChar(CString& str)
{
	int len = str.GetLength();
	TCHAR* tr = str.GetBuffer(len);
	str.ReleaseBuffer();
	return tr;
}

 

1.5函数调用的一个实例

    CString cmdString = _T("ipconfig.exe /?");
    CString output = executeCmd(cmdString);

    AfxMessageBox(output);

 

1.6运行结果如下图所示:

 

 

二、关键函数参数讲解

2.1函数CreatePipe()各个参数如下所示:

BOOL CreatePipe(
	_Out_ PHANDLE hReadPipe,	//管道读端口句柄
	_Out_ PHANDLE hWritePipe,	//管道写端口句柄
	_In_opt_ LPSECURITY_ATTRIBUTES lpPipeAttributes,	//安全描述符,通过设置参数,可以设置能否被支线程继承
	_In_ DWORD nSize    //0表示管道缓冲设置为系统默认值
	);

 

2.2函数CreateProcess()各个参数如下所示:

BOOL CreateProcess(
_In_opt_ LPCWSTR lpApplicationName,					//指向要调用的程序路径
_Inout_opt_ LPWSTR lpCommandLine,					//输入的命令行TCHAR*字符串
_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,	//结构体SECURTY_ATTRIBUTE,指向进程安全描述符
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,	//结构体SECURITY_ATTRIBUTE,指向线程安全描述符
_In_ BOOL bInheritHandles,							//新进程是否从调用进程处继承了句柄
_In_ DWORD dwCreationFlags,							//附加的、用来控制优先类和进程的创建标志。设置为CREATE_NEW_CONSOLE可显示子窗口。
_In_opt_ LPVOID lpEnvironment,						//指向使用父类的环境
_In_opt_ LPCWSTR lpCurrentDirectory,				//使用父类的当前目录
_In_ LPSTARTUPINFOW lpStartupInfo,					//结构体STARTUPINFO,指向一个用于决定新进程的主窗体如何显示
_Out_ LPPROCESS_INFORMATION lpProcessInformation	//结构体PROCESS_INFORMATION,指向一个用来接收新进程的识别信息
);

 

2.3函数ReadFile()各个参数如下所示:

BOOL ReadFile(
_In_ HANDLE hFile,						//文件句柄
_Out_writes_bytes_to_opt_(nNumberOfBytesToRead, *lpNumberOfBytesRead) __out_data_source(FILE) LPVOID lpBuffer,	//读缓冲区
_In_ DWORD nNumberOfBytesToRead,		//要读取的字节数
_Out_opt_ LPDWORD lpNumberOfBytesRead,	//实际读取到的字节数
_Inout_opt_ LPOVERLAPPED lpOverlapped	//指向结构体OVERLAPPED,一般设为NULL
);

 

 

参考内容:

https://blog.csdn.net/it2153534/article/details/79064643(参考:重点函数各个参数讲解)

https://blog.csdn.net/qq61394323/article/details/39829631(参考:MFC读取CMD输出内容代码)

https://blog.csdn.net/qq61394323/article/details/39253193?utm_source=blogxgwz2(参考:匿名管道读写句柄用法)

https://blog.csdn.net/whynottrythis/article/details/39828395(参考:STARTUPINFO结构体各个参数讲解)

https://blog.csdn.net/jeanphorn/article/details/44982273(参考:函数ReadFile()参数讲解和调用实例,主要参考)

https://blog.csdn.net/virtualdesk/article/details/4379965(参考:函数ReadFile()参数讲解和调用实例)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章