# 通过匿名管道获取CMD命令框输出内容
# 背景
以前有个人咨询我说,要怎么编程实现获取控制台窗口或者是CMD窗口输出的数据内容。当时水平有限,很多知识还不是很了解。但是凭借着有一点基础,而且之前在网络上浏览过相关的技术实现,还有些印象,便回答ta说,可以用匿名管道的方式来实现。
> 管道是一种用于在进程间共享数据的机制,其实质是一段共享内存。Windows系统为这段共享的内存设计采用数据流I/0的方式来访问。由一个进程读、另一个进程写,类似于一个管道两端,因此这种进程间的通信方式称作“管道”。
>
> 管道分为匿名管道和命名管道。匿名管道只能在父子进程间进行通信,不能在网络间通信,而且数据传输是单向的,只能一端写,另一端读。命令管道可以在任意进程间通信,通信是双向的,任意一端都可读可写,但是在同一时间只能有一端读、一端写。
后来,自己想起这件事,开始自己动手实现。没想到真的可以通过创建匿名管道的方式来实现。现在,我就把程序实现的过程和原理整理成文档,分享给大家。
# 函数介绍
## CreatePipe 函数
> 创建一个匿名管道,并从中得到读写管道的句柄。
>
> 函数声明
>
> ```c++
> BOOL WINAPI CreatePipe(
> _Out_PHANDLE hReadPipe,
> _Out_PHANDLE hWritePipe,
> _In_opt_LPSECURITY_ATTRIBUTES lpPipeAttributes,
> _In_DWORD nSize
> );
> ```
>
> 参数
>
> - hReadPipe[out]
> 返回一个可用于读管道数据的文件句柄。
> - hWritePipe[out]
> 返回一个可用于写管道数据的文件句柄。
> - lpPipeAttributes[in, optional]
> 传入一个SECURITY_ATTRIBUTES结构的指针,该结构用于决定该函数返回的句柄是否可被子进程继承。如果传NULL,则返回的句柄是不可继承的。
> 该结构的lpSecurityDescriptor成员用于设定管道的安全属性,如果传NULL,那么该管道将获得一个默认的安全属性,该属性与创建该管道的用户账户权限ACLs的安全令牌(token)相同。
> - nSize[in]
> 管道的缓冲区大小。但是这仅仅只是一个理想值,系统根据这个值创建大小相近的缓冲区。如果传入0 ,那么系统将使用一个默认的缓冲区大小。
>
> 返回值
>
> - 如果函数成功,则返回值不为零。
> - 如果函数失败,返回值为零。 要获取扩展错误信息,请调用GetLastError。
## CreateProcess 函数
> 创建一个新进程及其主要线程。 新进程在调用进程的安全上下文中运行。
>
> 函数声明
>
> ```c++
> BOOL WINAPI CreateProcess(
> _In_opt_ LPCTSTR lpApplicationName,
> _Inout_opt_ LPTSTR lpCommandLine,
> _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
> _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
> _In_ BOOL bInheritHandles,
> _In_ DWORD dwCreationFlags,
> _In_opt_ LPVOID lpEnvironment,
> _In_opt_ LPCTSTR lpCurrentDirectory,
> _In_ LPSTARTUPINFO lpStartupInfo,
> _Out_ LPPROCESS_INFORMATION lpProcessInformation
> );
> ```
>
> 参数
>
> - lpApplicationName
> 指向一个NULL结尾的、用来指定可执行模块的字符串。这个参数可以被设为NULL,在这种情况下,可执行模块的名字必须处于 lpCommandLine 参数最前面并由空格符与后面的字符分开。
> - lpCommandLine
> 指向一个以NULL结尾的字符串,该字符串指定要执行的命令行。这个参数可以为空,那么函数将使用lpApplicationName参数指定的字符串当做要运行的程序的命令行。如果lpApplicationName和lpCommandLine参数都不为空,那么lpApplicationName参数指定将要被运行的模块,lpCommandLine参数指定将被运行的模块的命令行。新运行的进程可以使用GetCommandLine函数获得整个命令行。C语言程序可以使用argc和argv参数。
> - lpProcessAttributes
> 指向一个SECURITY_ATTRIBUTES结构体,这个结构体决定是否返回的句柄可以被子进程继承。
> - lpThreadAttributes
> 同lpProcessAttribute,不过这个参数决定的是线程是否被继承,通常置为NULL。
> - bInheritHandles
> 指示新进程是否从调用进程处继承了句柄。如果参数的值为真,调用进程中的每一个可继承的打开句柄都将被子进程继承。被继承的句柄与原进程拥有完全相同的值和访问权限。
> - dwCreationFlags
> 指定附加的、用来控制优先类和进程的创建的标志。
> - lpEnvironment
> 指向一个新进程的环境块。如果此参数为空,新进程使用调用进程的环境。
> 一个环境块存在于一个由以NULL结尾的字符串组成的块中,这个块也是以NULL结尾的。
> - lpCurrentDirectory
> 指向一个以NULL结尾的字符串,这个字符串用来指定子进程的工作路径。这个字符串必须是一个包含驱动器名的绝对路径。如果这个参数为空,新进程将使用与调用进程相同的驱动器和目录。这个选项是一个需要启动应用程序并指定它们的驱动器和工作目录的外壳程序的主要条件。
> - lpStartupInfo
> 指向一个用于决定新进程的主窗体如何显示的STARTUPINFO结构体。
> - lpProcessInformation
> 指向一个用来接收新进程的识别信息的PROCESS_INFORMATION结构体。
>
> 返回值
>
> - 如果函数执行成功,返回非零值。
> - 如果函数执行失败,返回零,可以使用GetLastError函数获得错误的附加信息。
## STARTUPINFO 结构体
> STARTUPINFO用于指定新进程的主窗口特性的一个结构。
>
> ```c++
> typedef struct _STARTUPINFO {
> DWORD cb;
> LPTSTR lpReserved;
> LPTSTR lpDesktop;
> LPTSTR lpTitle;
> DWORD dwX;
> DWORD dwY;
> DWORD dwXSize;
> DWORD dwYSize;
> DWORD dwXCountChars;
> DWORD dwYCountChars;
> DWORD dwFillAttribute;
> DWORD dwFlags;
> WORD wShowWindow;
> WORD cbReserved2;
> LPBYTE lpReserved2;
> HANDLE hStdInput;
> HANDLE hStdOutput;
> HANDLE hStdError;
> } STARTUPINFO, *LPSTARTUPINFO;
> ```
>
> 部分成员
>
> - cb表示包含STARTUPINFO结构中的字节数,应用程序必须将cb初始化为sizeof(STARTUPINFO)。
> - dwFlags表示结构体启用哪些成员,其中STARTF_USESHOWWINDOW表示使用结构体成员wShowWindow;STARTF_USESTDHANDLES表示使用结构体成员hStdInput、hStdOutput 和 hStdError。
> - wShowWindow用于窗口显示方式,SW_HIDE表示隐藏窗口。
> - hStdOutput 和 hStdError用于标识控制台窗口的缓存。
# 实现原理
创建命名管道的方式,来获取CMD执行结果的输出内容。具体可以分成以下 5 个步骤:
- 初始化匿名管道的安全属性结构体SECURITY_ATTRIBUTES,使用匿名管道默认的缓冲区大小,并调用函数 CreatePipe 创建匿名管道,获取管道数据读取句柄和管道数据写入句柄
- 对即将创建的进程结构体STARTUPINFO进行初始化,设置进程窗口隐藏,并把上面管道数据写入句柄赋值给新进程控制台窗口的缓存句柄,这样,新进程会把窗口缓存的输出数据写入到匿名管道中
- 开始调用 CreateProcess 函数创建新进程,执行 CMD 命令,并调用函数 WaitForSingleObject 等待命令执行完毕
- 命令执行完毕后,便调用 ReadFile 函数根据匿名管道的数据读取句柄从匿名管道的缓冲区中读取缓冲区的数据,这个数据就是新进程执行命令返回的结果数据
- 这样,经过上面 4 步操作,CMD 的执行结果就成功获取了。这是就可以关闭句柄,释放资源了
# 编码实现
```c++
// 执行 cmd 命令, 并获取执行结果数据
BOOL PipeC
精选_通过匿名管道获取CMD命令框输出内容_源码打包
版权申诉
5 浏览量
2022-03-09
13:46:52
上传
评论
收藏 189KB ZIP 举报
工具盒子
- 粉丝: 58
- 资源: 1313