Windows进程通信之一看就懂的匿名管道通信
进程通信之一看就懂的匿名管道通信
一丶匿名管道
1.1何为匿名管道
匿名管道是用来父进程跟子进程通信的.还有一种是命名管道.不需要父子进程就可以进行通信的.今天先说匿名管道的.
匿名管道. 就是父进程创建子进程. 读取子进程数据.或者给子进程发送数据.当然子进程也可以给父进程发送数据.以及读取父进程发送过来的数据.
1.2创建匿名管道需要注意的事项
创建匿名管道,首先你要明白什么是管道. 管道你可以想象成一个管子.
我们通过这个管子发送数据.
如下图所示:
通过上图,我们就知道其实创建了两个管道. 分别是父进程读取的管道.以及
子进程读取的管道.相应的.子进程也可以对父进程读取的管道进行传输数据.父进程就可以读取了.
这段话可能难以理解.你可以这样想.
我父进程读取子进程使用第一个管道. 那么反正子进程写的话也是使用第一个管道.因为子进程写.我们父进程才能读.
1.3 创建匿名管道需要的步骤
首先你需要了解创建匿名管道的API
WINBASEAPI
BOOL
WINAPI
CreatePipe(
_Out_ PHANDLE hReadPipe,
_Out_ PHANDLE hWritePipe,
_In_opt_ LPSECURITY_ATTRIBUTES lpPipeAttributes,
_In_ DWORD nSize
);
关于这个API很简单.相比你过来看匿名管道.说明你已经知道这个API了.
只不过不知道管道概念.(我猜的);
这个API很简单.就是创建管道. 两个句柄.读取句柄跟写入句柄.
我们需要创建两个管道.
还需要注意的是,父进程创建子进程,必须让父进程句柄可继承.这个就是第三个属性.安全属性描述符中给即可.
那么说一下创建步骤吧.
1.创建一个安全属性描述符,设置句柄可继承
2.创建两个管道. 父读->子写 子读->父写的
3.重定向输出,将子进程的读 以及子进程的写重定向.
4.创建子进程
5.读取\写入数据给子进程.
1.4代码例子
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
int main()
{
HANDLE hParentRead, hParentWrite, hChildRead, hChildWrite; //创建4个句柄
STARTUPINFO si = { 0 }; //启动信息结构体
si.cb = sizeof(si);
PROCESS_INFORMATION pi = { 0 }; //进程信息结构体
DWORD dwWritedBytes = 0;
DWORD dwReadedBytes = 0;
DWORD dwBytesRead = 0;
DWORD dwTotalBytesAvail = 0;
DWORD dwBytesLeftThisMessage = 0;
SECURITY_ATTRIBUTES sa = { 0 }; //安全属性描述符
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = TRUE; //设置句柄可继承
//创建管道1. 父进程读 -> 子进程写入
BOOL bRet = CreatePipe(&hParentRead,
&hChildWrite,
&sa,
0);
//创建管道2. 子进程读->父进程写.
bRet = CreatePipe(&hChildRead,
&hParentWrite,
&sa,
0);
//这里将子进程写重定向到 stdout中. 子进程读取重定向到stdinput中
si.hStdInput = hChildRead;
si.hStdOutput = hChildWrite;
si.dwFlags = STARTF_USESTDHANDLES; //设置窗口隐藏启动
bRet = CreateProcess(NULL,
"cmd.exe", //创建cmd进程.默认寻找cmd进程.
NULL,
NULL,
TRUE,
CREATE_NO_WINDOW,
NULL,
NULL,
&si,
&pi);
char szBuffer[15] = "calc \n";
WriteFile(hParentWrite, szBuffer, 15, NULL, 0);//使用writeFile操作管道,给cmd发送数据命令.
return 0;
}
1.5代码运行截图
二丶匿名管道读取写入问题
2.1 简介
匿名管道是阻塞的.一个命令的输入那么ReadFile才能读取. 且还会阻塞. 所以要使用API PeekNamedPipe 来查看管道数据.
而且要写死循环去读. 但是我们不知道读取多大.所以建立一个一分钟的线程来读取.
2.2 C++代码
.h
#pragma once
#include "IUtileTools.h"
//IUtileTools 是工具类 包含了字符串大小写转换 获取路径等自己用的.可以删除.
class CPipeOpt : public IUtileTools
{
public:
CPipeOpt();
~CPipeOpt();
public:
void CreateChildProcess();
void WriteToPipe(char* szCommand, DWORD commandsize);
string ReadFromPipe();
private:
HANDLE g_hChildStd_IN_Rd = NULL;
HANDLE g_hChildStd_IN_Wr = NULL;
HANDLE g_hChildStd_OUT_Rd = NULL;
HANDLE g_hChildStd_OUT_Wr = NULL;
PROCESS_INFORMATION piProcInfo;
};
.cpp
#include "CPipeOpt.h"
#define BUFSIZE 4096
CPipeOpt::CPipeOpt()
{
SECURITY_ATTRIBUTES saAttr;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0))
return;
// Ensure the read handle to the pipe for STDOUT is not inherited.
if (!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0))
return;
// Create a pipe for the child process\'s STDIN.
if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0))
return;
// Ensure the write handle to the pipe for STDIN is not inherited.
if (!SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0))
return;
// Create the child process.
CreateChildProcess();
}
CPipeOpt::~CPipeOpt()
{
CloseHandle(piProcInfo.hProcess);
CloseHandle(piProcInfo.hThread);
CloseHandle(g_hChildStd_OUT_Wr);
CloseHandle(g_hChildStd_IN_Rd);
TerminateProcess(piProcInfo.hProcess, 0);
}
void CPipeOpt::CreateChildProcess()
// Create a child process that uses the previously created pipes for STDIN and STDOUT.
{
TCHAR szCmdline[] = TEXT("cmd.exe");
STARTUPINFO siStartInfo;
BOOL bSuccess = FALSE;
// Set up members of the PROCESS_INFORMATION structure.
ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
// Set up members of the STARTUPINFO structure.
// This structure specifies the STDIN and STDOUT handles for redirection.
ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = g_hChildStd_OUT_Wr;
siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
siStartInfo.hStdInput = g_hChildStd_IN_Rd;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
siStartInfo.dwFlags |= STARTF_USESHOWWINDOW;
//siStartInfo.dwFlags = STARTF_USESTDHANDLES;
// Create the child process.
bSuccess = CreateProcess(NULL,
szCmdline, // command line
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // handles are inherited
NULL, // creation flags
NULL, // use parent\'s environment
NULL, // use parent\'s current directory
&siStartInfo, // STARTUPINFO pointer
&piProcInfo); // receives PROCESS_INFORMATION
}
void CPipeOpt::WriteToPipe(char *szCommand,DWORD commandsize)
// Read from a file and write its contents to the pipe for the child\'s STDIN.
// Stop when there is no more data.
{
DWORD dwWritten = 0;
BOOL bSuccess = FALSE;
WriteFile(g_hChildStd_IN_Wr, szCommand, commandsize, &dwWritten, NULL);
WaitForSingleObject(g_hChildStd_IN_Wr, INFINITE);
}
DWORD WINAPI WaitThread(LPVOID)
{
Sleep(1 * 60 * 1000);
return 0;
}
string CPipeOpt::ReadFromPipe()
{
DWORD dwRead = 0;
DWORD dwAvail = 4096;
char* start = nullptr; //记录起始地址
char cbBuf[BUFSIZE] = { 0 };
BOOL bSuccess = FALSE;
string strRes = "";
HANDLE hThread = CreateThread(nullptr, 0, WaitThread, nullptr, 0, 0);
while (TRUE)
{
DWORD dwByteRecv;
DWORD dwTotalBytesAvail;
while (PeekNamedPipe(
g_hChildStd_OUT_Rd,
cbBuf,
BUFSIZE,
&dwByteRecv,
&dwTotalBytesAvail,
nullptr))
{
if (dwByteRecv <= 0) {
break;
}
DWORD dwAllocSize(dwTotalBytesAvail + 1);
PBYTE lpReadBuffer = new BYTE[dwAllocSize];
ReadFile(g_hChildStd_OUT_Rd, lpReadBuffer,
dwTotalBytesAvail, &dwByteRecv, nullptr);
lpReadBuffer[dwByteRecv] = \'\0\';
strRes += reinterpret_cast<LPCSTR>(lpReadBuffer);
delete[] lpReadBuffer;
lpReadBuffer = nullptr;
}
if ( WAIT_OBJECT_0 == WaitForSingleObject(piProcInfo.hProcess, 0)
|| WAIT_OBJECT_0 == WaitForSingleObject(hThread, 0))
{
break;
}
}
return strRes;
}