本文共 4439 字,大约阅读时间需要 14 分钟。
本博客主要总结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]; DWORD byteRead; CString output; //返回值 while (true) { memset(buffer, 0, 4096); //要放在循环里面,否则接收数据错乱 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()参数讲解和调用实例)
原文:https://blog.csdn.net/naibozhuan3744/article/details/83142860