孙鑫 第十七课进程间通信之二 匿名管道

来源:互联网 发布:php二次开发 编辑:程序博客网 时间:2024/06/05 20:21

说明

匿名管道用在本地机器的父子进程之间,父进程创建一个匿名管道,并且生成一个子进程(子进程对应的程序已经存在)。

匿名管道和子进程创建完毕后,父子进程就可以利用该管道进行通信,由于该管道是匿名的,因此子进程要从父进程继承读写句柄,

另外还有标准错误句柄。

父进程在创建子进程的时候指定子进程的标准输入 / 输出 / 错误句柄。

在进行通信的时候用函数ReadFile和WriteFile,不必先创建文件,ReadFile / WriteFile对应的文件句柄为该匿名管道的读写句柄。



相关函数和数据结构

①创建匿名管道

BOOL  CreatePipe(

PHANDLE  hReadPipe,                                  //[out] 读句柄指针

PHANDLE  hWritePipe,                                  //[out] 写句柄指针

LPSECURITY_ATTRIBUTES  lpPipeAttributes,  //[in] 安全属性,因为子进程要继承父进程的句柄,因此此参数不能为NULL

DWORD  nSize                                            //[in] 管道大小,字节单位,如果为0系统使用默认大小

);

失败返回0,成功非0



//安全属性结构体

typedef  struct  _SECURITY_ATTRIBUTES{

DWORD  nLength;                  //该结构体大小

LPVOID   lpSecurityDescriptor; //安全描述符,为NULL即可,使用默认

BOOL     bInheritHandle;         //返回的句柄是否可以被新的进程继承

}SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES;



eg.

HANDLE  hRead = NULL;

HANDLE  hWrite = NULL;

SECURITY_ATTRIBUTES  sa;                           //安全属性结构体

sa.nLength  = sizeof(SECURITY_ATTRIBUTES);  //大小

sa.lpSecurityDescriptor = NULL;                       //默认安全描述符

sa.bInheritHandle = TRUE;                               //允许继承

CreatePipe(&hRead, &hWrite, &sa, 0);               //第四个参数为0表示使用默认管道大小



②创建子线程

BOOL   CreateProcess(

LPCTSTR  lpApplicationName,                            //应用程序名,包括扩展名

LPTSTR    lpCommandLine,                               //命令行参数,没有为NULL

LPSECURITY_ATTRIBUTES  lpProcessAttributes, //返回的新进程对象的句柄是否能被子进程所继承,为NULL即可

LPSECURITY_ATTRIBUTES  lpThreadAttributes,  //返回的新线程对象的句柄是否能被子进程所继承,为NULL即可

BOOL  bInheritHandles,                                    //父进程打开的句柄是否被子进程继承

DWORD  dwCreationFlags,                               //创建标识,为0即可

LPVOID  lpEnvironment,                                  //环境块指针,为NULL即可,表示和父进程使用它相同环境块

LPCTSTR  lpCurrentDirectory,                          //新进程的驱动器和目录,绝对路径,为NULL表示和父进程有相同驱动器和目录

LPSTARTUPINFO  lpStartupInfo,                      //[in] STARTUPINFO结构体指针,下面将说明

LPPROCESS_INFORMATION  lpProcessInformation //[out] PROCESS_INFORMATION结构体指针

);

失败返回0,成功非0



typedef  struct  _STARTUPINFO{

DWORD  cb;                   //结构体大小

//略

DWORD  dwFlags;           //标识,当使用标识STARTF_USESTDHANDLES时,只需要使用这里列出的3+1个成员

HANDLE  hStdInput;       //标准输入句柄

HANDLE  hStdOutput;     //标准输出句柄

HANDLE  hStdError;        //标准出错句柄

}STARTUPINFO, *PSTARTUPINFO;


 

typedef   struct  _PROCESS_INFORMATION{

HANDLE  hProcess; //新创建的进程句柄

HANDLE  HThread; //新进程的主线程句柄

DWORD  dwProcessId; //新的进程ID

DWORD  dwThreadId;  //新的进程的主线程的ID

}PROCESS_INFORMATION;


eg.

STARTUPINFO   startupInfo;  //STARTUPINFO结构体成员

ZeroMemory(&startupInfo, sizeof(STARTUPINFO));//成员全置0

startupInfo.cb = sizeof(STARTUPINFO); //大小

startupInfo.dwFlags = STARTF_USESTDHANDLES; //标识

startupInfo.hStdInput = hRead; //子进程的标准输入句柄为继承自父进程的管道读句柄

startupInfo.hStdOutput = hWrite;//子进程的标准写句柄为继承自父进程的管道写句柄

startupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);//标准错误句柄


PROCESS_INFORMATION  proInfo;

ZeroMemory(&proInfo, sizeof(PROCESS_INFORMATION));


CreateProcess(

"..\\MyProcessChild\\Debug\\MyProcessChild.exe", 

NULL, 

NULL, 

NULL, 

TRUE, 

0, 

NULL,

NULL, 

&sa, 

&proInfo

);



③写管道

BOOL  WriteFile(

HANDLE  hFile,                                   //文件句柄,匿名管道下为管道写句柄

LPCVOID  lpBuffer,                             //要写的数据地址

DWORD  nNumberOfBytesToWrite,      //要写的数据字节数

LPDWORD  lpNumberOfBytesWritten,  //[out] 实际写的字节数

LPVOERLAPPED  lpOverlapped             //重叠操作结构体指针

);


失败返回0, 成功非0

说明:最后一个参数lpOverlapped为一个OVERLAPPED结构体指针,当CreateFile中指定了FILE_FLAG_OVERLAPPED标识时,

这个参数不能为NULL,否则会错误的报告写操作完成。

如果指定了FILE_FLAG_OVERLAPPED标记并且这个参数也不为NULL,那么这个函数会立即返回而不用等到写操作完成。这个时候韩会返回FALSE并且调用GetLastError时返回ERROR_IO_PENDING,此时允许调用线程继续处理(不算失败)。  写操作完成时,OVERLAPPED结构体中的事件event被置为有信号。

如果没有指定FILE_FLAG_OVERLAPPED标记,那么这个参数为NULL即可,此时写操作完成时函数才会返回,否则一直停在此处。


eg.

ZeroMemory(&lo, sizeof(OVERLAPPED));

char* pSend = "hello world!";

DWORD length = 0;


WriteFile(

hWrite,

pSend,

strlen(pSend) + 1,

&length,

NULL

);



④读管道

BOOL  ReadFile(

HANDLE  hFile,                               //文件句柄,这里为匿名管道读句柄

LPVOID   lpBuffer,                          //数据读入的地址

DWORD  nNumberOfBytesToRead,  //要读的字节数

LPDWORD  lpNumberOfBytesRead,  //实际读入的字节数

LPOVERLAPPED  lpOverlapped         //OVERLAPPED结构体指针

);

失败返回0, 成功非0

说明:关于最后一个参数lpOverlapped和写管道类似,当没有指定FILE_FLAG_OVERLAPPED标记时,这个参数为NULL即可,

        此时,函数会停在此直到读操作完成。

        在实际实验时,有这样一种情况:一个按钮函数的作用是读管道,如果管道中没有数据的话,程序会阻塞,因此读操作没有

        完成,程序会停在此处,造成假死现象, 这一点要注意!!!    

        相比写操作,读操作可能需要更长的时间。


 eg.

char   readBuffer[200];

memset(readBuffer, '\0', sizeof(readBuffer));

DWORD  length = 0;


ReadFile(

hRead,

readBuffer,

sizeof(readBuffer),

&length;

NULL

);





实例

//1 父进程

//////////////////////////////////////////////创建管道/////////////////////////////////////////////////

void CMy79AnonymousPipeDlg::OnBtnCreatepipe() 

{

  //创建匿名管道

  BOOL ret = FALSE;

 

  SECURITY_ATTRIBUTES sa;

  sa.nLength = sizeof(SECURITY_ATTRIBUTES);  

  sa.lpSecurityDescriptor = NULL;               //默认安全描述符

  sa.bInheritHandle = TRUE;                      //允许子进程继承句柄

 

  ret = CreatePipe(&m_hRead, &m_hWrite, &sa, 0);

  if (!ret)

  {

    MessageBox(_T("创建匿名管道失败!"));

    CloseHandle(m_hRead);

    CloseHandle(m_hWrite);

    m_hRead = NULL;

    m_hWrite = NULL;

    return;

  }

 

  STARTUPINFO startupInfo;

  ZeroMemory(&startupInfo, sizeof(STARTUPINFO));

  startupInfo.cb = sizeof(STARTUPINFO);

  startupInfo.dwFlags = STARTF_USESTDHANDLES;

  startupInfo.hStdInput = m_hRead;

  startupInfo.hStdOutput = m_hWrite;

  startupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);

 

  PROCESS_INFORMATION proInfo;

  ZeroMemory(&proInfo, sizeof(PROCESS_INFORMATION));

 

  ret = CreateProcess("..\\79AnonymousChild\\Debug\\79AnonymousChild.exe", NULL, NULL, NULL, TRUE,

  0, NULL, NULL, &startupInfo, &proInfo);

  if (!ret)

  {

    MessageBox(_T("创建子进程失败!"));

    CloseHandle(m_hRead);

    CloseHandle(m_hWrite);

    CloseHandle(proInfo.hProcess);

    CloseHandle(proInfo.hThread);

    m_hWrite = NULL;

    m_hRead = NULL;

    return;

  }

  else

  {

    CloseHandle(proInfo.hProcess);

    CloseHandle(proInfo.hThread);

  }

}

 

 

/////////////////////////////////写管道////////////////////////////////////////////////////////////////////////

void CMy79AnonymousPipeDlg::OnBtnWrite() 

{

  // TODO: Add your control notification handler code here 

  char* pSend = "hello world!";

  DWORD length = 0;

  BOOL ret = FALSE;

 

  ret = WriteFile(m_hWrite, pSend, strlen(pSend) + 1, &length, NULL);

  if (!ret)

  {

    MessageBox(_T("写管道失败"));

    CloseHandle(m_hWrite);

    m_hWrite = NULL;

    return;

  }

}

 

 

///////////////////////////////////////读管道//////////////////////////////////////////////////////////////////////////// 

void CMy79AnonymousPipeDlg::OnBtnRead() 

{

  // TODO: Add your control notification handler code here

  char readBuffer[200];

  memset(readBuffer, '\0', sizeof(readBuffer));

  BOOL ret = FALSE;

  DWORD length = 0;

  ret = ReadFile(m_hRead, readBuffer, sizeof(readBuffer), &length, NULL);

  if (!ret)

  {

    MessageBox(_T("读管道失败!"));

    CloseHandle(m_hRead);

    m_hRead = NULL;

    return;

  }

  MessageBox(readBuffer);

}




//2 子进程

///////////////////////////////////////////写管道//////////////////////////////////////////////////////

void CMy79AnonymousChildDlg::OnBtnRead() 

{

  // TODO: Add your control notification handler code here

  BOOL ret = FALSE;

  DWORD length = 0;

  char readBuffer[200];

  memset(readBuffer, '\0', sizeof(readBuffer));

 

  ret = ReadFile(m_hRead, readBuffer, sizeof(readBuffer), &length, NULL);

  if (!ret)

  {

    MessageBox(_T("读管道失败!"));

    CloseHandle(m_hRead);

    m_hRead = NULL;

    return;

  }

  MessageBox(readBuffer);

}

 

////////////////////////////////////读管道/////////////////////////////////////////////////////////////

void CMy79AnonymousChildDlg::OnBtnWrite() 

{

  // TODO: Add your control notification handler code here

  DWORD length = 0;

  BOOL ret = FALSE;

 

  GetDlgItemText(IDC_EDIT_WRITE, str);

  char* pSend = "hello!";

 

  ret = WriteFile(m_hWrite, pSend, strlen(pSend) + 1, &length, NULL);

  if (!ret)

  {

    MessageBox(_T("写管道失败!"));

    CloseHandle(m_hWrite);

    m_hWrite = NULL;

    return;

  }

}


孙鑫 第十七课进程间通信之二  匿名管道 - 大灰狼 - 大灰狼 的博客