ReadFileEx和WriteFileEx功能提供了另一种形式的重叠I / O。与使用事件对象来表示完成的重叠ReadFile和WriteFile函数不同,扩展函数指定完成例程。完成例程是在读或写操作完成时排队执行的功能。直到调用ReadFileEx和WriteFileEx的线程通过调用其中一个扩展等待功能WaitForSingleObjectEx,WaitForMultipleObjectsEx或SleepEx进入可警告的等待时,才执行完成例程。这些功能与其他等待功能类似,当指定的对象处于信号状态或超时间隔过去时,它们返回。但是,这些功能也可以执行一个可警告的等待,当他们的【的fAlertable】参数设置为TRUE时发生。在等待警报时,当ReadFileEx或WriteFileEx完成例程排队执行时,函数也返回。服务器进程可以使用扩展功能为连接到它的每个客户端执行一系列读写操作。序列中的每个读取或写入操作指定完成例程,并且每个完成例程启动序列中的下一个步骤。
像前面的例子一样,这个例子是一个单线程的服务器进程,它创建一个消息类型的管道并使用重叠的操作。服务器进程的不同之处在于它使用扩展功能ReadFileEx和WriteFileEx来执行重叠的I / O。与重叠的ReadFile和WriteFile功能不同,它们在完成后发出事件对象的信号,扩展功能指定完成例程,该操作在操作完成时排队等待执行。服务器进程使用WaitForSingleObjectEx函数,该函数在完成例程准备好执行时执行一个可警告的等待,返回。当事件对象发出信号时,等待功能也返回,在此示例中,表示重叠的ConnectNamedPipe操作已完成(新客户端已连接)。
最初,服务器进程创建管道的单个实例,并启动重叠ConnectNamedPipe操作。当客户端连接时,服务器分配一个结构来为该管道实例提供存储,然后调用ReadFileEx函数来启动一系列I / O操作,以处理与客户端的通信。每个操作指定一个完成例程,执行序列中的下一个操作。当客户端断开并且管道实例关闭时,序列将终止。在为新客户端启动操作序列后,服务器创建另一个管道实例,并等待下一个客户端进行连接。
ReadFileEx和WriteFileEx函数的参数指定完成例程和指向OVERLAPPED结构的指针。该指针后来传递给其【lpOverLap】参数中的完成例程。因为OVERLAPPED结构指向为每个管道实例分配的结构中的第一个成员,完成例程可以使用其【lpOverLap】参数访问管道实例的结构。
为避免重复,不显示ConnectToNewClient子例程的列表;它与上一节中重叠的服务器进程使用的相同。
#include < windows.h >
typedef结构en
{
OVERLAPPED oOverlap;
HANDLE hPipeInst;
CHAR chBuf[BUFSIZE];
DWORD cbToWrite;
} PIPEINST, *LPPIPEINST;
BOOL CreateAndConnectInstance();
BOOL ConnectToNewClient(HANDLE, LPOVERLAPPED);
VOID GetDataToWriteToClient(LPPIPEINST);
VOID DisconnectAndClose(LPPIPEINST);
VOID WINAPI CompletedWriteRoutine(DWORD, DWORD, LPOVERLAPPED);
VOID WINAPI CompletedReadRoutine(DWORD, DWORD, LPOVERLAPPED);
HANDLE hPipe;
DWORD主(VOID)
{
HANDLE hConnectEvent;
OVERLAPPED oConnect;
LPPIPEINST lpPipeInst;
DWORD dwWait, cbBytes;
BOOL fSuccess, fPendingIO;
//为连接操作创建一个事件对象。
hConnectEvent = CreateEvent(
NULL, //没有安全属性
TRUE, //手动复位事件
TRUE, //初始状态=发信号
NULL); //未命名的事件对象
if (hConnectEvent == NULL)
MyErrExit("CreateEvent");
oConnect.hEvent = hConnectEvent;
//调用一个子例程来创建一个实例,然后等待
//客户端连接。
fPendingIO = CreateAndConnectInstance(&oConnect);
而(1)
{
//等待客户端连接,或者进行读取或写入
//操作完成,导致完成
//要排队执行的例程。
dwWait = WaitForSingleObjectEx(
hConnectEvent, //事件对象等待
INFINITE, //无限期地等待
TRUE); switch (dwWait)
开关(dwWait)
{
//通过完成的连接操作满足等待。
case 0:
//如果操作正在等待,请获取该结果
//连接操作。
如果(fPendingIO)
{
fSuccess = GetOverlappedResult(
hPipe, //管道手柄
&oConnect, // OVERLAPPED结构
&cbBytes, //传送字节
FALSE); //不等待
如果(!fSuccess)
MyErrExit("ConnectNamedPipe");
}
//为此实例分配存储。
lpPipeInst = (LPPIPEINST) GlobalAlloc(
GPTR, sizeof(PIPEINST));
if (lpPipeInst == NULL)
MyErrExit("GlobalAlloc lpPipeInst");
lpPipeInst->hPipeInst = hPipe;
//启动此客户端的读取操作。
//注意,这个相同的例程后来被用作一个
//写操作完成例程。
lpPipeInst->cbToWrite = 0;
CompletedWriteRoutine(0, 0, (LPOVERLAPPED) lpPipeInst);
//为下一个客户端创建新的管道实例。
fPendingIO = CreateAndConnectInstance(
&oConnect);
break;
//完成的读取或写入等待满足
//操作。这允许系统执行
//完成例程。
case WAIT_IO_COMPLETION:
break;
//等待功能发生错误。
默认:
MyErrExit("WaitForSingleObjectEx");
}
}
return 0;
}
// CompletedWriteRoutine(DWORD,DWORD,LPOVERLAPPED)
//这个例程在写入后称为完成例程
//管道,或新客户端连接到管道实例时。It
//开始另一个读操作。
VOID WINAPI CompletedWriteRoutine(DWORD dwErr,DWORD cbWritten,
LPOVERLAPPED lpOverLap)
{
LPPIPEINST lpPipeInst;
BOOL fRead = FALSE;
// lpOverlap指向此实例的存储。
lpPipeInst = (LPPIPEINST) lpOverLap;
//写操作完成,所以读下一个请求(如果
//没有错误)。
if ((dwErr == 0) && (cbWritten == lpPipeInst->cbToWrite))
fRead = ReadFileEx(
lpPipeInst->hPipeInst,
lpPipeInst->chBuf,
BUFSIZE,
(LPOVERLAPPED)lpPipeInst,
(LPOVERLAPPED_COMPLETION_ROUTINE) CompletedReadRoutine);
//如果发生错误,请断开连接。
如果(!fRead)
DisconnectAndClose(lpPipeInst);
}
// CompletedReadRoutine(DWORD,DWORD,LPOVERLAPPED)
//读取之后,该例程被称为I / O完成例程
//客户端请求它获取数据并将其写入管道。
VOID WINAPI CompletedReadRoutine(DWORD dwErr,DWORD cbBytesRead,
LPOVERLAPPED lpOverLap)
{
LPPIPEINST lpPipeInst;
BOOL fWrite = FALSE;
// lpOverlap指向此实例的存储。
lpPipeInst = (LPPIPEINST) lpOverLap;
//读操作完成,所以写一个响应(如果没有)
// 发生了错误)。
if ((dwErr == 0) && (cbBytesRead != 0))
{
GetDataToWriteToClient(lpPipeInst);
fWrite = WriteFileEx(
lpPipeInst->hPipeInst,
lpPipeInst->chBuf,
lpPipeInst->cbToWrite,
(LPOVERLAPPED)lpPipeInst,
(LPOVERLAPPED_COMPLETION_ROUTINE) CompletedWriteRoutine);
}
//如果发生错误,请断开连接。
如果(!fWrite)
DisconnectAndClose(lpPipeInst);
}
// DisconnectAndClose(LPPIPEINST)
//发生错误或客户端关闭时调用此例程
//它的管道的手柄。
VOID DisconnectAndClose(LPPIPEINST lpPipeInst)
{
//断开管道实例。
if(!DisconnectNamedPipe(lpPipeInst- > hPipeInst))
MyErrExit("DisconnectNamedPipe");
//关闭管道实例的句柄。
CloseHandle(lpPipeInst->hPipeInst);
//释放管道实例的存储。
if (lpPipeInst != NULL)
GlobalFree(lpPipeInst);
}
// CreateAndConnectInstance(LPOVERLAPPED)
//此函数创建管道实例并连接到客户机。
//如果连接操作正在等待,则返回TRUE,否则返回FALSE
//连接已经完成。
BOOL CreateAndConnectInstance(LPOVERLAPPED lpoOverlap)
{
LPTSTR lpszPipename = "\\\\.\\pipe\\mynamedpipe";
hPipe = CreateNamedPipe(
lpszPipename, //管道名称
PIPE_ACCESS_DUPLEX | //读/写访问
FILE_FLAG_OVERLAPPED, //重叠模式
PIPE_TYPE_MESSAGE | // message-type pipe
PIPE_READMODE_MESSAGE | //消息读取模式
PIPE_WAIT, //阻止模式
PIPE_UNLIMITED_INSTANCES, //无限制的实例
BUFSIZE, //输出缓冲区大小
BUFSIZE, //输入缓冲区大小
PIPE_TIMEOUT, //客户端超时
NULL); //没有安全属性
if (hPipe == INVALID_HANDLE_VALUE)
MyErrExit("CreatePipe");
//调用子程序连接到新的客户端。
return ConnectToNewClient(hPipe, lpoOverlap);
}