管道(Pipe)实际是用于进程间通信的一段共享内存,创建管道的进程称为管道服务器,连接到一个管道的进程为管道客户机。一个进程在向管道写入数据后,另一进程就可以从管道的另一端将其读取出来。匿名管道(Anonymous Pipes)是在父进程和子进程间单向传输数据的一种未命名的管道,只能在本地计算机中使用,而不可用于网络间的通信。
匿名管道由CreatePipe()函数创建,该函数在创建匿名管道的同时返回两个句柄:管道读句柄和管道写句柄。CreatePipe()的函数原型为: BOOL CreatePipe(PHANDLE hReadPipe, // 指向读句柄的指针 PHANDLE hWritePipe, // 指向写句柄的指针 LPSECURITY_ATTRIBUTES lpPipeAttributes, // 指向安全属性的指针 DWORD nSize // 管道大小 ); //四个参数
通过
hReadPipe
和
hWritePipe
所指向的句柄可分别以只读、只写的方式去访问管道。在使用匿名管道通信时,服务器进程必须将其中的一个句柄传送给客户机进程。句柄的传递多通过继承来完成,服务器进程也允许这些句柄为子进程所继承。除此之外,进程也可以通过诸如
DDE
或共享内存等形式的进程间通信将句柄发送给与其不相关联的进程。
在调用
CreatePipe
()函数时,如果管道服务器将
lpPipeAttributes
指向的
SECURITY_ATTRIBUTES
数据结构的数据成员
bInheritHandle
设置为
TRUE
,那么
CreatePipe
()创建的管道读、写句柄将会被继承。管道服务器可调用
DuplicateHandle
()函数改变管道句柄的继承。管道服务器可以为一个可继承的管道句柄创建一个不可继承的副本或是为一个不可继承的管道句柄创建一个可继承的副本。
CreateProcess
()函数还可以使管道服务器有能力决定子进程对其可继承句柄是全部继承还是不继承。
在生成子进程之前,父进程首先调用
Win32 API SetStdHandle
()使子进程、父进程可共用标准输入、标准输出和标准错误句柄。当父进程向子进程发送数据时,用
SetStdHandle
()将管道的读句柄赋予标准输入句柄;在从子进程接收数据时,则用
SetStdHandle
()将管道的写句柄赋予标准输出(或标准错误)句柄。然后,父进程可以调用进程创建函数
CreateProcess
()生成子进程。如果父进程要发送数据到子进程,父进程可调用
WriteFile
()将数据写入到管道(传递管道写句柄给函数),子进程则调用
GetStdHandle
()取得管道的读句柄,将该句柄传入
ReadFile
()后从管道读取数据。
如果是父进程从子进程读取数据,那么由子进程调用
GetStdHandle
()取得管道的写入句柄,并调用
WriteFile
()将数据写入到管道。然后,父进程调用
ReadFile
()从管道读取出数据(传递管道读句柄给函数)。
在用
WriteFile
()函数向管道写入数据时,只有在向管道写完指定字节的数据后或是在有错误发生时函数才会返回。如管道缓冲已满而数据还没有写完,
WriteFile
()将要等到另一进程对管道中数据读取以释放出更多可用空间后才能够返回。管道服务器在调用
CreatePipe
()创建管道时以参数
nSize
对管道的缓冲大小作了设定。
匿名管道并不支持异步读、写操作,这也就意味着不能在匿名管道中使用
ReadFileEx
()和
WriteFileEx
(),而且
ReadFile
()和
WriteFile
()中的
lpOverLapped
参数也将被忽略。匿名管道将在读、写句柄都被关闭后退出,也可以在进程中调用
CloseHandle
()函数来关闭此句柄。
。在下面将要给出的程序示例中,将由父进程(管道服务器)创建一个子进程(管道客户机),子进程回见个其全部的标准输出发送到匿名管道中,父进程再从管道读取数据,一直到子进程关闭管道的写句柄。其中,匿名管道服务器程序的实现清单如下:
(不知道为什么我的这段程序调试没通过,所以以下的有问题,请大家注意)
#include<stdio.h> #include<windows.h> #include<Winbase.h> int main() { STARTUPINFO si; PROCESS_INFORMATION pi; char ReadBuf[100]; DWORD ReadNum; HANDLE hRead,hWrite; BOOL bRet=CreatePipe(&hRead, &hWrite, NULL, 0); if(bRet==TRUE) printf("成功创建匿名管道/n"); else printf("创建匿名管道失败,错误代码:%d/n",GetLastError()); HANDLE hTemp = GetStdHandle(STD_OUTPUT_HANDLE); SetStdHandle(STD_OUTPUT_HANDLE, hWrite); GetStartupInfo(&si); bRet=CreateProcess("ChildPro.exe", NULL, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
SetStdHandle(STD_OUTPUT_HANDLE, hTemp); if (bRet == TRUE) // 输入信息 printf("成功创建子进程!/n"); else printf("failure,:%d/n",GetLastError()); return 1; }
在本示例中,将当前进程的标准输出设置为使用匿名管道,再创建子进程,子进程将继承父进程的标准输出,然后再将父进程的标准输出恢复为其初始状态。于是父进程便可从管道读取数据,直到有错误发生或关闭管道写入端的所有句柄。创建的子进程只是向标准输出和标准错误发送一些文本信息,其中发送给标准输出的文本将重定向输出到管道,发送给标准错误的文本将不改变输出。下面给出子进程的实现代码:
int main(int argc, char* argv[])
{
for (int i = 0; i
<
100; i++) //
发送一些数据到标准输出和标准错误
{
printf("i = %d/n", i); //
打印提示
cout
<<
"
标准输出
:"
<<
i
<<
endl; //
打印到标准输出
cerr
<<
"
标准错误
:"
<<
i
<<
endl; //
打印到标准错误
}
return 0;
}