天天看点

17.2匿名管道

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函数创建子进程时,将管道的读写句柄传递给子进程了。

17.2匿名管道
17.2匿名管道