17.2匿名管道
17.2.1基础知识
//匿名管道是一个未命名的、单向管道,通常用在一个父进程和一个子进程之间传输数据。匿名管道只能实现本地机器上两个进程间的通信,而不能实现跨网络的通信。为创建匿名管道,需要调用CreatePipe函数:
BOOL CreatePipe(
PHANDLE hReadPipe, // 返回管道读取的句柄
PHANDLE hWritePipe, // 接收管道写入的句柄
LPSECURITY_ATTRIBUTES lpPipeAttributes, // 检测返回的句柄能否被子进程继承,如果此参数为NULL,则句柄不能继承
DWORD nSize // 指定管道缓冲区大小,为0表示使用默认缓冲区大小
);
//因为匿名管道只能在父子进程之间通信,当子进程从其父进程继承命名管道的句柄后,这两个进程之间就可以通过该句柄进行通信了。
typedef struct _SECURITY_ATTRIBUTES { // sa
DWORD nLength; //指定结构体大小
LPVOID lpSecurityDescriptor; //指向安全描述符的指针
BOOL bInheritHandle; //TRUE表示返回的句柄能被新进程继承
} SECURITY_ATTRIBUTES;
17.2.2进程的创建
//为了启动一个进程,可以调用CreateProcess。
BOOL CreateProcess(
LPCTSTR lpApplicationNam,//指定可执行程序的名称,一定加上扩展名
LPTSTR lpCommandLine, //指定传递给新进程的命令行字符串
LPSECURITY_ATTRIBUTES lpProcessAttributes,//新进程对象安全性
LPSECURITY_ATTRIBUTES lpThreadAttributes,//新线程对象的安全性
BOOL bInheritHandles,//为TRUE,让子进程继承父进程创建的管道的读写句柄。
DWORD dwCreationFlags, //进程创建的附加标记
LPVOID lpEnvironment, //指向环境快的指针
LPCTSTR lpCurrentDirectory,//指定子进程当前路径
LPSTARTUPINFO lpStartupInfo, //指定新进程的窗口如何显示
LPPROCESS_INFORMATION lpProcessInformation //接收新进程的标识信息
);
7.2.3父进程的实现
//新建一个单文档的MFC应用程序,取名为Parent,为该工程增加一个子菜单,名称为匿名管道。接着为这个子菜单添加三个菜单项,并分别为他们添加相应的命令响应函数,由视类接收这些命令响应函数。
//接下来为视类增加两个私有成员变量,即两个句柄,他们将分别作为匿名管道的读写句柄来使用,并在视类的构造函数中将他们都初始化为NULL。
private:
HANDLE hWrite;
HANDLE hRead;
//然后再视类的析构函数中判断这两个变量是否有值,则调用CloseHandle函数关闭这两个变量
//1、创建匿名管道
//在OnPipeCreate函数中调用CreatePipe创建匿名管道,返回管道的读写句柄
void CParentView::OnPipeCreate()
{
// TODO: Add your command handler code here
SECURITY_ATTRIBUTES sa;
sa.bInheritHandle=TRUE;
sa.lpSecurityDescriptor=NULL;
sa.nLength=sizeof(SECURITY_ATTRIBUTES);
if(!CreatePipe(&hRead,&hWrite,&sa,0))
{
MessageBox("创建匿名管道失败!");
return;
}
STARTUPINFO sui;
PROCESS_INFORMATION pi;
//将sui变量中的所有成员设置为0
ZeroMemory(&sui,sizeof(STARTUPINFO));
//指定新进程的窗口如何显示
sui.cb=sizeof(STARTUPINFO);
sui.dwFlags=STARTF_USESTDHANDLES;
sui.hStdInput=hRead;
sui.hStdOutput=hWrite;
sui.hStdError=GetStdHandle(STD_ERROR_HANDLE);
if(!CreateProcess("..\\Child\\Debug\\Child.exe",NULL,NULL,NULL,TRUE,0,NULL,NULL,&sui,&pi))
{
CloseHandle(hRead);
CloseHandle(hWrite);
hRead=NULL;
hWrite=NULL;
MessageBox("创建子进程失败!");
return;
}
else
{
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
}
//CreateProcess函数第一个参数设置为子进程的应用程序文件名,这里先指定为…\Child\Debug\Child.exe,下面编写子进程程序的目录与本程序所在的目录保持平级。当子进程启动之后,父子进程间就可以通过创建的匿名管道来读取和写入数据
//2、读取数据
//在OnPipeRead函数中实现匿名管道的读取操作
void CParentView::OnPipeRead()
{
// TODO: Add your command handler code here
char buf[200];
DWORD dwRead;
if(!ReadFile(hRead,buf,100,&dwRead,NULL))
{
MessageBox("读取数据失败!");
return;
}
MessageBox(buf);
}
//3、写入数据:在OnPipeWrite函数中实现匿名管道的写入操作
void CParentView::OnPipeWrite()
{
// TODO: Add your command handler code here
char buf[]="vc++";
//保存实际写入的字节数
DWORD dwWrite;
if(!WriteFile(hWrite,buf,strlen(buf)+1,&dwWrite,NULL))
{
MessageBox("写入数据失败!");
return;
}
}
//运行就可以生成Parent程序。
17.2.4子进程的实现
//新建一个MFC应用程序,工程取名为Child,并将该工程添加到Parent工程中,并且该工程所在目录与Parent工程平级。然后为Child增加一个子菜单,名称为匿名管道,再增加两个菜单项,添加相应的视类的命令消息响应函数。
//同样地为CChildView类添加两个私有成员变量,即两个句柄,将他们分别作为匿名管道的读取和写入句柄。
private:
HANDLE hWrite;
HANDLE hRead;
//并在CChildView类构造函数中将它们初始化为NULL。
//CChildView类的析构函数中,判断两个变量是否有值,则调用CloseHandle函数关闭这两个变量:
CChildView::~CChildView()
{
if(hRead)
{
CloseHandle(hRead);
}
if(hWrite)
{
CloseHandle(hWrite);
}
}
//1、获得管道读取和写入句柄
//为了利用父进程创建的匿名管道进行通信,首先就得获得子进程的标准输入输出句柄,这可以在视类窗口创建完毕后去获取。我们可以为CChildView类增加虚函数:OnInitialUpdate函数中调用GetStdHandle函数获取子进程的标准输入输出句柄:
void CChildView::OnInitialUpdate()
{
CView::OnInitialUpdate();
// TODO: Add your specialized code here and/or call the base class
hRead=GetStdHandle(STD_INPUT_HANDLE);
hWrite=GetStdHandle(STD_OUTPUT_HANDLE);
}
//2、读取数据
void CChildView::OnPipeRead()
{
// TODO: Add your command handler code here
char buf[100];
DWORD dwRead;
if(!ReadFile(hRead,buf,100,&dwRead,NULL))
{
MessageBox("读取数据失败!");
return;
}
MessageBox(buf);
}
//3、写入数据
void CChildView::OnPipeWrite()
{
// TODO: Add your command handler code here
char buf[]="深入详解";
//保存实际写入的字节数
DWORD dwWrite;
if(!WriteFile(hWrite,buf,strlen(buf)+1,&dwWrite,NULL))
{
MessageBox("写入数据失败!");
return;
}
}
//运行生成子进程,运行结果如图。注意先运行父进程,父进程运行同时会运行子进程。父进程中调用CreateProcess函数创建子进程时,将管道的读写句柄传递给子进程了。