孙鑫 第十七课进程间通信之二 匿名管道
来源:互联网 发布: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;
}
}
- 孙鑫 第十七课进程间通信之二 匿名管道
- 孙鑫VC学习笔记:第十七讲 (二) 用匿名管道实现进程间的通信
- 孙鑫 第十七课进程间通信之三 命名管道
- 孙鑫MFC第十七讲 进程间通信的方法:剪切板,管道,匿名管道,邮槽
- 进程间通信之匿名管道通信
- 孙鑫VC学习笔记:第十七讲 用匿名管道实现进程间的通信
- 进程通信之二 管道技术第二篇 匿名管道
- 进程通信之二 管道技术第二篇 匿名管道
- 进程通信之二 管道技术第二篇 匿名管道
- 进程通信之二 管道技术第二篇 匿名管道
- 进程通信之二 管道技术第二篇 匿名管道
- linux进程间通信之匿名管道
- Python进程间通信之匿名管道
- Linux进程间通信之匿名管道
- 进程间通信之匿名管道
- 进程间通信之--匿名管道
- 进程间通信之匿名管道
- 进程间通信之管道通信(匿名管道)
- 基本概念:同步、异步、阻塞和非阻塞(转载)
- 孙鑫 第十五/十六课之五 基于消息异步套接字编程
- 孙鑫 第十五/十六课之六 基于消息异步UDP套接字编程实例
- gethostbyname实例 / gethostbyadddr实例
- 孙鑫 第十七课进程间通信之一 剪贴板
- 孙鑫 第十七课进程间通信之二 匿名管道
- 孙鑫 第十七课进程间通信之三 命名管道
- 孙鑫 第十七课进程间通信之四 邮槽
- Java 集合类的一点总结
- 颜色16进制值
- 孙鑫 第十八课ActiveX控件
- 孙鑫 第十九课 动态链接库DLL
- Android Fragement学习笔记(三)----PreferenceFragment的使用
- 孙鑫 第二十课HOOK