匿名管道
匿名管道是一种未命名的、单向管道,通常用来在一个父进程和一个子进程之间传输数据。匿名的管道只能实现本地机器上两个进程间的通信,而不能实现跨网络的通信。
1.1匿名管道
匿名管道是一种未命名的、单向管道。通常用来在父进程和子进程之间传输数据。匿名管道总是本地的,不能在网络之间传递数据。
1.1.1匿名管道操作
CreatePipe函数创建一个匿名管道,并且返回两个句柄:一个读管道的句柄和一个写管道的句柄。读句柄具有管道的只读权限,写句柄具有管道的只写权限。为了利用管道交换数据,管道服务端必须把管道句柄传给另一个进程。通常情况下,这是通过继承实现的(参见1.1.2);就是说,父进程允许子进程继承这个句柄。进程也可以使用DuplicateHandle函数复制一个管道句柄,再通过一些进程间通信机制,比如DDE或者共享内存,把它发送给另一个不相关的进程。
管道服务端可以给管道服务端发送读句柄或者写句柄,这取决于客户端要用这个管道发送数据还是获取数据。要从管道读取数据,以管道的读句柄为参数调用ReadFile函数。当另一个进程向管道写入数据是,ReadFile函数返回。如果管道的所有写句柄被关闭,或者读取数据时有错误发生,ReadFile函数也会返回。
要向管道写入数据,以管道的写句柄问参数,调用WriteFile函数。数据被完全写入管道,或者出错,WriteFile将会返回。如果管道的缓存已满,且还有尚未写完的数据,直到另一个进程从管道读取数据前,WriteFile函数都不会返回。缓存的大小是在管道服务端调用CreatePipe函数时指定的。
匿名管道不支持异步读写。这意味着不能使用ReadFileEx和WriteFileEx函数读写匿名管道。另外,使用匿名管道时,ReadFile和WriteFile函数的lpOverlapped参数也会被忽略。
匿名管道会一直存在,直到所有的读写句柄全部被关闭。进程可以调用CloseHandle函数关闭管道句柄。进程终止时,所有的管道句柄也会被自动关闭。
1.1.2管道句柄的继承
通过一下几个方法,管道服务端控制管道句柄是否可以被继承:
一、调用CreatePipe时,将SECURITY_ATTRIBUTES参数的成员bInheritHandle设为TRUE,那么这个CreatePipe创建的管道就可以被继承。
二、管道服务端可以利用DuplicateHandle函数改变管道句柄的继承特性。管道服务到可以从可继承的管道句柄复制出不可继承的句柄,也可以从不可继承的管道句柄复制出可继承的句柄。
三、CreateProcess函数使得管道服务端可以决定子进程是否继承自己的所有句柄。
在同一个目录下面建立两个不同的工程Parent和Child:
·@父进程的实现
1.添加两个私有成员变量
// Attributes
private:
HANDLE hWrite;
HANDLE hRead;
初始化为NULL
CParentView::CParentView()
{
// TODO: add construction code here
hWrite = NULL;
hRead = NULL;
}
在析构函数中关闭这两个变量。
CParentView::~CParentView()
{
if(hRead)
CloseHandle(hRead);
if(hWrite)
CloseHandle(hWrite);
}
2.匿名管道的创建
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;
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,
if(!CreateProcess("..//Parent//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);
}
}
3.匿名管道的读取操作
void CParentView::OnPipeRead()
{
// TODO: Add your command handler code here
char buf[100];
DWORD dwRead;
if(!ReadFile(hRead,buf,100,&dwRead,NULL))
{
MessageBox("读取数据失败!");
return ;
}
MessageBox(buf);
}
4.匿名管道的写入操作
void CParentView::OnPipeWrite()
{
// TODO: Add your command handler code here
char buf[] = "http://www.sina.com.cn";
DWORD dwWrite;
if(!WriteFile(hWrite,buf,strlen(buf) + 1,&dwWrite,NULL))
{
MessageBox("写入数据失败!");
return ;
}
}
·@子进程的实现
1. // Attributes
private:
HANDLE hWrite;
HANDLE hRead;
CChildView::CChildView()
{
// TODO: add construction code here
hRead = NULL;
hWrite = NULL;
}
CChildView::~CChildView()
{
if(hRead)
CloseHandle(hRead);
if(hWrite)
CloseHandle(hWrite);
}
2.获取管道的读取和写入句柄
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);
}
3.读取数据
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);
}
4.写入数据
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 ;
}
}