Windows进程间通信(上)

一、管道

管道(pipe)是用于进程间通信的共享内存区域。创建管道的进程称为管道服务器,而连接到这个管道的进程称为管道客户端。一个进程向管道写入信息,而另外一个进程从管道读取信息。
异步管道是基于字符和半双工的(即单向),一般用于程序输入输出的重定向;命名管道则强大地多,它们是面向消息和全双工的,同时还允许网络通信,用于创建客户端/服务器系统

  • 异步管道(匿名管道):

管道(Pipe)是一种具有两个端点的通信通道:有一端句柄的进 程可以和有另一端句柄的进程通信。管道可以是单向-一端是只读的,另一端点是只写的(匿名管道);也可以是双向的一管道的两端点既可读也可写。 匿名管道(Anonymous Pipe)是在父进程和子进程之间,或同一父进程的两个子进程之间传输数据的无名字的单向管道。通常由父进程创建管道, 然后由要通信的子进程继承通道的读端点句柄或写 端点句柄,然后实现通信。父进程还可以建立两个或更多个继承匿名管道读和写句柄的子进程。这些子进程 可以使用管道直接通信,不需要通过父进程。 匿名管道是单机上实现子进程标准I/O重定向的有效方法,它不能在网上使用,也不能用于两个不相关的进程之间。

实验流程图:

异步管道实现的流程图说明:
1)父进程是我们需要实现的,其中需要创建管道A,管道B,和子进程,整个实现流程分为4个操作。
2)管道A:输入管道
3)管道B:输出管道
4)操作A:把输入文件sample.in的数据写入输入管道(管道A)
5)操作B:子进程从输入管道中读取数据,作为该进程的加工原料。通常,程序的输入数据由标准的输入设备输入,这里实现输入重定向,即把输入管道作为输入设备。
6)操作C:子进程把加工后的成品(输出数据)输出到输出管道。通常,程序的输出数据会输出到标准的输出设备,一般为屏幕,这里实现输出重定向,即把输出管道作为输出设备。
7)操作D:把输出管道的数据写入输出文件

需要注意的是,管道的本质只是一个共享的内存区域。这个实验中,管道区域处于父进程的地址空间中,父进程的作用是提供环境和资源,并协调子进程进行加工。

GetStdHandle:返回标准的输入、输出或错误的设备的句柄,也就是获得输入、输出/错误的屏幕缓冲区的句柄。

CreatePipe:创建一个匿名管道,并从中得到读、写管道的句柄。

SetStdHandle:为标准的输入、输出或错误的设备设置句柄。和GetStdHandle配合使用其实就是用来使其它的窗口作为标准输入、输出、错误窗口。

子进程sample.exe内容:

#include <iostream>
using namespace std;
int main()
{
    int a, b;
    while (cin >> a >> b && (a || b))
        cout << a + b << endl;
    return 0;
}

父进程内容:

#include <windows.h> 
#include <iostream>
#include <strsafe.h>

const int BUFSIZE = 4096;

HANDLE    hChildStdinRd, hChildStdinWr, hChildStdinWrDup,
hChildStdoutRd, hChildStdoutWr, hChildStdoutRdDup,
hSaveStdin, hSaveStdout;

BOOL CreateChildProcess(LPTSTR);
VOID WriteToPipe(LPTSTR);
VOID ReadFromPipe(LPTSTR);
VOID ErrorExit(LPTSTR);
VOID ErrMsg(LPTSTR, BOOL);

void main(int argc, char *argv[])//DataRedirect sample sample.in sample.out
{
    // 处理输入参数
    if (argc != 4)
        return;
    TCHAR lpProgram[0x100] = { 0 };
    TCHAR lpInputFile[0x100] = { 0 };
    TCHAR lpOutputFile[0x100] = { 0 };
    DWORD cchDest = 0x100;
    
    StringCchCopy(lpProgram, cchDest, argv[1]);
    StringCchCopy(lpInputFile, cchDest, argv[2]);
    StringCchCopy(lpOutputFile, cchDest, argv[3]);

    SECURITY_ATTRIBUTES saAttr;
    saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
    saAttr.bInheritHandle = TRUE;
    saAttr.lpSecurityDescriptor = NULL;

    
    //重定向标准输出hChildStdoutWr            
    hSaveStdout = GetStdHandle(STD_OUTPUT_HANDLE);//获得标准输出

    //创建管道B,hChildStdoutRd就是管道B的读取方,hChildStdoutWr是向管道B输出,后边用作自己进程的标准输出
    if (!CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0))//创建匿名管道,获得读、写管道的句柄
        ErrorExit("Stdout pipe creation failed\n");

    if (!SetStdHandle(STD_OUTPUT_HANDLE, hChildStdoutWr))//更改标准输出为hChildStdoutWr,则子线程的cout会输出到hChildStdoutWr
        ErrorExit("Redirecting STDOUT failed");

    //DuplicateHandle获得一个进程句柄表中的一个记录项,然后在另一个进程的句柄表中创建这个记录项的一个副本。
    BOOL fSuccess = DuplicateHandle(//Duplicates an object handle.
        GetCurrentProcess(),//A handle to the process with the handle to be duplicated. 
        hChildStdoutRd,//The handle to be duplicated
        GetCurrentProcess(),//A handle to the process that is to receive the duplicated handle. 
        &hChildStdoutRdDup,//A pointer to a variable that receives the duplicate handle
        0,
        FALSE,
        DUPLICATE_SAME_ACCESS);
    if (!fSuccess)
        ErrorExit("DuplicateHandle failed");
    CloseHandle(hChildStdoutRd);

    //重定向标准输入为 hChildStdinRd
    hSaveStdin = GetStdHandle(STD_INPUT_HANDLE);

    if (!CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0))//子进程从hChildStdinRd读入
        ErrorExit("Stdin pipe creation failed\n");

    if (!SetStdHandle(STD_INPUT_HANDLE, hChildStdinRd))
        ErrorExit("Redirecting Stdin failed");

    fSuccess = DuplicateHandle(
        GetCurrentProcess(),
        hChildStdinWr,
        GetCurrentProcess(),
        &hChildStdinWrDup,
        0,
        FALSE,
        DUPLICATE_SAME_ACCESS);
    if (!fSuccess)
        ErrorExit("DuplicateHandle failed");
    CloseHandle(hChildStdinWr);

    
    //创建子进程(即启动SAMPLE.EXE)
    fSuccess = CreateChildProcess(lpProgram);
    if (!fSuccess)
        ErrorExit("Create process failed");

    // 父进程输入输出流的还原设置
    if (!SetStdHandle(STD_INPUT_HANDLE, hSaveStdin))
        ErrorExit("Re-redirecting Stdin failed\n");
    if (!SetStdHandle(STD_OUTPUT_HANDLE, hSaveStdout))
        ErrorExit("Re-redirecting Stdout failed\n");

    WriteToPipe(lpInputFile);//操作A
    ReadFromPipe(lpOutputFile);//操作B
}

BOOL CreateChildProcess(LPTSTR lpProgram)
{
    PROCESS_INFORMATION piProcInfo;
    STARTUPINFO siStartInfo;
    BOOL bFuncRetn = FALSE;

    ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
    ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
    siStartInfo.cb = sizeof(STARTUPINFO);

    bFuncRetn = CreateProcess(NULL, lpProgram, NULL, NULL, TRUE, \
        0, NULL, NULL, &siStartInfo, &piProcInfo);
    if (bFuncRetn == 0)
    {
        ErrorExit("CreateProcess failed\n");
        return 0;
    }
    else
    {
        CloseHandle(piProcInfo.hProcess);
        CloseHandle(piProcInfo.hThread);
        return bFuncRetn;
    }
}

VOID WriteToPipe(LPTSTR lpInputFile)
{
    HANDLE hInputFile = CreateFile(lpInputFile, GENERIC_READ, 0, NULL,
        OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
    if (hInputFile == INVALID_HANDLE_VALUE)
        return;

    BOOL fSuccess;
    DWORD dwRead, dwWritten;
    CHAR chBuf[BUFSIZE] = { 0 };

    for (;;)
    {
        //操作A //写到管道A
        fSuccess = ReadFile(hInputFile, chBuf, BUFSIZE, &dwRead, NULL);
        if (!fSuccess || dwRead == 0)
            break;
        //父进程往这个管道A里写,子进程就从这个管道A里读
        fSuccess = WriteFile(hChildStdinWrDup, chBuf, dwRead, &dwWritten, NULL);
        if (!fSuccess)
            break;
    }

    if (!CloseHandle(hChildStdinWrDup))
        ErrorExit("Close pipe failed\n");

    CloseHandle(hInputFile);
}

VOID ReadFromPipe(LPTSTR lpOutputFile)
{
    HANDLE hOutputFile = CreateFile(lpOutputFile, GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hOutputFile == INVALID_HANDLE_VALUE)
        return;

    BOOL fSuccess;
    DWORD dwRead, dwWritten;
    CHAR chBuf[BUFSIZE] = { 0 };

    if (!CloseHandle(hChildStdoutWr))
        ErrorExit("Closing handle failed");

    for (;;)
    {
        //从管道B读,读完写到sample.out
        //父进程从管道B读,就是子进程往管道B里写
        fSuccess = ReadFile(hChildStdoutRdDup, chBuf, BUFSIZE, &dwRead, NULL);
        if (!fSuccess || dwRead == 0)
        {
            break;
        }

        fSuccess = WriteFile(hOutputFile, chBuf, dwRead, &dwWritten, NULL);
        if (!fSuccess)
            break;
    }

    CloseHandle(hOutputFile);
}

VOID ErrorExit(LPTSTR lpszMessage)
{
    MessageBox(0, lpszMessage, 0, 0);
}

读入的数据:

输出结果:

异步管道,就是创建一个管道,管道有两个句柄,有一个句柄专门用来读,另一个句柄专门用来写。

参考:http://bbs.pediy.com/showthread.php?t=26252

  • 命名管道:

命名管道具有以下几个特征:
(1)命名管道是双向的,所以两个进程可以通过同一管道进行交互。
(2)命名管道不但可以面向字节流,还可以面向消息,所以读取进程可以读取写进程发送的不同长度的消息。
(3)多个独立的管道实例可以用一个名称来命名。例如几个客户端可以使用名称相同的管道与同一个服务器进行并发通信。
(4)命名管道可以用于网络间两个进程的通信,而其实现的过程与本地进程通信完全一致。

命名管道是通过网络来完成进程之间的通信的,命名管道依赖于底层网络接口,其中包括有 DNS 服务,TCP/IP 协议等等机制,但是其屏蔽了底层的网络协议细节,对于匿名管道而言,其只能实现在父进程和子进程之间进行通信,而对于命名管道而言,其不仅可以在本地机器上实现两个进程之间的通信,还可以跨越网络实现两个进程之间的通信。命名管道使用了 Windows 安全机制,因而命名管道的服务端可以控制哪些客户有权与其建立连接,而哪些客户端是不能够与这个命名管道建立连接的。利用命名管道机制实现不同机器上的进程之间相互进行通信时,可以将命名管道作为一种网络编程方案时,也就是看做是 Socket 就可以了,它实际上是建立了一个客户机/服务器通信体系,并在其中可靠的传输数据。命名管道的通信是以连接的方式来进行的,服务器创建一个命名管道对象,然后在此对象上等待连接请求,一旦客户连接过来,则两者都可以通过命名管道读或者写数据。          命名管道提供了两种通信模式:字节模式和消息模式。在字节模式下,数据以一个连续的字节流的形式在客户机和服务器之间流动,而在消息模式下,客户机和服务器则通过一系列的不连续的数据单位,进行数据的收发,每次在管道上发出一个消息后,它必须作为一个完整的消息读入。

CreateNamedPipe:Creates an instance of a named pipe and returns a handle for subsequent pipe operations. A named pipe server process uses this function either to create the first instance of a specific named pipe and establish its basic attributes or to create a new instance of an existing named pipe.

HANDLE WINAPI CreateNamedPipe(
  _In_     LPCTSTR               lpName,//命名管道的名字
  _In_     DWORD                 dwOpenMode,//指示命名管道创建好后,它的传输方向、I/O控制以及安全模式
  _In_     DWORD                 dwPipeMode,//指定了命名管道的读、写以及等待模式。
  _In_     DWORD                 nMaxInstances,//最多可以接受多少个从客户机到服务器的连接
  _In_     DWORD                 nOutBufferSize,
  _In_     DWORD                 nInBufferSize,//nOutBufferSize和nInBufferSize分别指定了为内部输入及输出缓冲区长度保留的字节数。
  _In_     DWORD                 nDefaultTimeOut,//客户机等待同一个命令管道建立连接的最长时间,单位毫秒
  _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes//允许应用程序为命名管道指定一个安全描述符,并决定一个子进程是否能够继承新建的句柄
);

ConnectNamedPipe:Enables a named pipe server process to wait for a client process to connect to an instance of a named pipe. A client process connects by calling either the CreateFile or CallNamedPipe function.

BOOL WINAPI ConnectNamedPipe(
  _In_        HANDLE       hNamedPipe,
  _Inout_opt_ LPOVERLAPPED lpOverlapped//在使用FILE_FLAG_OVERLAPPED标志创建命名管道的情况下,可以使用这个参数让这个连接以异步方式工作,如果这个参数为NULL,则以锁定模式工作
);

一个命名管道客户机成功建立了与服务器的连接之后,ConnectNamedPipe的调用便会结束。随后,服务器可用WriteFile函数,向客户机自由地发送数据;或者使用ReadFile函数,从客户机那里接收数据。服务器完成了与一个客户机的通信之后,便应调用DisconnctNamedPipe函数,以关闭此次通信会话。

先用MSDN上的例子说明:https://msdn.microsoft.com/en-us/library/aa365588%28v=vs.85%29.aspx

The following example is a multithreaded pipe server. It has a main thread with a loop that creates a pipe instance and waits for a pipe client to connect. When a pipe client connects, the pipe server creates a thread to service that client and then continues to execute the loop in the main thread. It is possible for a pipe client to connect successfully to the pipe instance in the interval between calls to the CreateNamedPipe and ConnectNamedPipe functions. If this happens, ConnectNamedPipe returns zero, and GetLastError returns ERROR_PIPE_CONNECTED.

The thread created to service each pipe instance reads requests from the pipe and writes replies to the pipe until the pipe client closes its handle. When this happens, the thread flushes the pipe, disconnects, closes its pipe handle, and terminates. The main thread will run until an error occurs or the process is ended.

Multithreaded pipe server:

#include <windows.h> 
#include <stdio.h> 
#include <tchar.h>
#include <strsafe.h>

#define BUFSIZE 512

DWORD WINAPI InstanceThread(LPVOID);
VOID GetAnswerToRequest(LPTSTR, LPTSTR, LPDWORD);

int _tmain(VOID)
{
    BOOL   fConnected = FALSE;
    DWORD  dwThreadId = 0;
    HANDLE hPipe = INVALID_HANDLE_VALUE, hThread = NULL;
    LPTSTR lpszPipename = TEXT("\\\\.\\pipe\\mynamedpipe");

    // The main loop creates an instance of the named pipe and 
    // then waits for a client to connect to it. When the client 
    // connects, a thread is created to handle communications 
    // with that client, and this loop is free to wait for the
    // next client connect request. It is an infinite loop.

    for (;;)//waits for a pipe client to connect
    {
        _tprintf(TEXT("\nPipe Server: Main thread awaiting client connection on %s\n"), lpszPipename);
        hPipe = CreateNamedPipe(
            lpszPipename,             // pipe name 
            PIPE_ACCESS_DUPLEX,       // read/write access 
            PIPE_TYPE_MESSAGE |       // message type pipe 
            PIPE_READMODE_MESSAGE |   // message-read mode 
            PIPE_WAIT,                // blocking mode 
            PIPE_UNLIMITED_INSTANCES, // max. instances  
            BUFSIZE,                  // output buffer size 
            BUFSIZE,                  // input buffer size 
            0,                        // client time-out 
            NULL);                    // default security attribute 

        if (hPipe == INVALID_HANDLE_VALUE)
        {
            _tprintf(TEXT("CreateNamedPipe failed, GLE=%d.\n"), GetLastError());
            return -1;
        }

        // Wait for the client to connect 
        fConnected = ConnectNamedPipe(hPipe, NULL) ?    //enables the namepipe
        TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);

        if (fConnected)
        {
            printf("Client connected, creating a processing thread.\n");

            // Create a thread for this client. 
            hThread = CreateThread(//创建线程用来处理client的请求
                NULL,              // no security attribute 
                0,                 // default stack size 
                InstanceThread,    // thread proc
                (LPVOID)hPipe,    // thread parameter 
                0,                 // not suspended 
                &dwThreadId);      // returns thread ID 

            if (hThread == NULL)
            {
                _tprintf(TEXT("CreateThread failed, GLE=%d.\n"), GetLastError());
                return -1;
            }
            else CloseHandle(hThread);
        }
        else
            // The client could not connect, so close the pipe. 
            CloseHandle(hPipe);
    }

    return 0;
}

DWORD WINAPI InstanceThread(LPVOID lpvParam)
// This routine is a thread processing function to read from and reply to a client
// via the open pipe connection passed from the main loop. Note this allows
// the main loop to continue executing, potentially creating more threads of
// of this procedure to run concurrently, depending on the number of incoming
// client connections.
{
    HANDLE hHeap = GetProcessHeap();
    TCHAR* pchRequest = (TCHAR*)HeapAlloc(hHeap, 0, BUFSIZE*sizeof(TCHAR));
    TCHAR* pchReply = (TCHAR*)HeapAlloc(hHeap, 0, BUFSIZE*sizeof(TCHAR));

    DWORD cbBytesRead = 0, cbReplyBytes = 0, cbWritten = 0;
    BOOL fSuccess = FALSE;
    HANDLE hPipe = NULL;

    // Do some extra error checking since the app will keep running even if this
    // thread fails.

    if (lpvParam == NULL)
    {
        printf("\nERROR - Pipe Server Failure:\n");
        printf("   InstanceThread got an unexpected NULL value in lpvParam.\n");
        printf("   InstanceThread exitting.\n");
        if (pchReply != NULL) HeapFree(hHeap, 0, pchReply);
        if (pchRequest != NULL) HeapFree(hHeap, 0, pchRequest);
        return (DWORD)-1;
    }

    if (pchRequest == NULL)
    {
        printf("\nERROR - Pipe Server Failure:\n");
        printf("   InstanceThread got an unexpected NULL heap allocation.\n");
        printf("   InstanceThread exitting.\n");
        if (pchReply != NULL) HeapFree(hHeap, 0, pchReply);
        return (DWORD)-1;
    }

    if (pchReply == NULL)
    {
        printf("\nERROR - Pipe Server Failure:\n");
        printf("   InstanceThread got an unexpected NULL heap allocation.\n");
        printf("   InstanceThread exitting.\n");
        if (pchRequest != NULL) HeapFree(hHeap, 0, pchRequest);
        return (DWORD)-1;
    }

    // Print verbose messages. In production code, this should be for debugging only.
    printf("InstanceThread created, receiving and processing messages.\n");

    // The thread's parameter is a handle to a pipe object instance. 

    hPipe = (HANDLE)lpvParam;

    // Loop until done reading
    while (1)
    {
        // Read client requests from the pipe. This simplistic code only allows messages
        // up to BUFSIZE characters in length.
        fSuccess = ReadFile(
            hPipe,        // handle to pipe 
            pchRequest,    // buffer to receive data 
            BUFSIZE*sizeof(TCHAR), // size of buffer 
            &cbBytesRead, // number of bytes read 
            NULL);        // not overlapped I/O 

        if (!fSuccess || cbBytesRead == 0)
        {
            if (GetLastError() == ERROR_BROKEN_PIPE)
            {
                _tprintf(TEXT("InstanceThread: client disconnected.\n"), GetLastError());
            }
            else
            {
                _tprintf(TEXT("InstanceThread ReadFile failed, GLE=%d.\n"), GetLastError());
            }
            break;
        }

        // Process the incoming message.
        GetAnswerToRequest(pchRequest, pchReply, &cbReplyBytes);

        // Write the reply to the pipe. 
        fSuccess = WriteFile(
            hPipe,        // handle to pipe 
            pchReply,     // buffer to write from 
            cbReplyBytes, // number of bytes to write 
            &cbWritten,   // number of bytes written 
            NULL);        // not overlapped I/O 

        if (!fSuccess || cbReplyBytes != cbWritten)
        {
            _tprintf(TEXT("InstanceThread WriteFile failed, GLE=%d.\n"), GetLastError());
            break;
        }
    }

    // Flush the pipe to allow the client to read the pipe's contents 
    // before disconnecting. Then disconnect the pipe, and close the 
    // handle to this pipe instance. 

    FlushFileBuffers(hPipe);//调用FlushFileBuffers在断开连接之前允许客户端读取管道内容。然后断开客户端连接
    DisconnectNamedPipe(hPipe);
    CloseHandle(hPipe);

    HeapFree(hHeap, 0, pchRequest);
    HeapFree(hHeap, 0, pchReply);

    printf("InstanceThread exitting.\n");
    return 1;
}

VOID GetAnswerToRequest(LPTSTR pchRequest,
    LPTSTR pchReply,
    LPDWORD pchBytes)
    // This routine is a simple function to print the client request to the console
    // and populate the reply buffer with a default data string. This is where you
    // would put the actual client request processing code that runs in the context
    // of an instance thread. Keep in mind the main thread will continue to wait for
    // and receive other client connections while the instance thread is working.
{
    _tprintf(TEXT("Client Request String:\"%s\"\n"), pchRequest);

    // Check the outgoing message to make sure it's not too long for the buffer.
    if (FAILED(StringCchCopy(pchReply, BUFSIZE, TEXT("default answer from server"))))
    {
        *pchBytes = 0;
        pchReply[0] = 0;
        printf("StringCchCopy failed, no outgoing message.\n");
        return;
    }
    *pchBytes = (lstrlen(pchReply) + 1)*sizeof(TCHAR);
}

Named pipe client:

#include <windows.h> 
#include <stdio.h>
#include <conio.h>
#include <tchar.h>

#define BUFSIZE 512

int _tmain(int argc, TCHAR *argv[])
{
    HANDLE hPipe;
    LPTSTR lpvMessage = TEXT("Default message from client.");
    TCHAR  chBuf[BUFSIZE];
    BOOL   fSuccess = FALSE;
    DWORD  cbRead, cbToWrite, cbWritten, dwMode;
    LPTSTR lpszPipename = TEXT("\\\\.\\pipe\\mynamedpipe");

    if (argc > 1)
        lpvMessage = argv[1];

    // Try to open a named pipe; wait for it, if necessary. 

    while (1)
    {
        hPipe = CreateFile(
            lpszPipename,   // pipe name 
            GENERIC_READ |  // read and write access 
            GENERIC_WRITE,
            0,              // no sharing 
            NULL,           // default security attributes
            OPEN_EXISTING,  // opens existing pipe 
            0,              // default attributes 
            NULL);          // no template file 

        // Break if the pipe handle is valid. 

        if (hPipe != INVALID_HANDLE_VALUE)
            break;

        // Exit if an error other than ERROR_PIPE_BUSY occurs. 

        if (GetLastError() != ERROR_PIPE_BUSY)
        {
            _tprintf(TEXT("Could not open pipe. GLE=%d\n"), GetLastError());
            return -1;
        }

        // All pipe instances are busy, so wait for 20 seconds. 

        if (!WaitNamedPipe(lpszPipename, 20000))
        {
            printf("Could not open pipe: 20 second wait timed out.");
            return -1;
        }
    }

    // The pipe connected; change to message-read mode. 

    dwMode = PIPE_READMODE_MESSAGE;
    fSuccess = SetNamedPipeHandleState(
        hPipe,    // pipe handle 
        &dwMode,  // new pipe mode 
        NULL,     // don't set maximum bytes 
        NULL);    // don't set maximum time 
    if (!fSuccess)
    {
        _tprintf(TEXT("SetNamedPipeHandleState failed. GLE=%d\n"), GetLastError());
        return -1;
    }

    // Send a message to the pipe server. 

    cbToWrite = (lstrlen(lpvMessage) + 1)*sizeof(TCHAR);
    _tprintf(TEXT("Sending %d byte message: \"%s\"\n"), cbToWrite, lpvMessage);

    fSuccess = WriteFile(
        hPipe,                  // pipe handle 
        lpvMessage,             // message 
        cbToWrite,              // message length 
        &cbWritten,             // bytes written 
        NULL);                  // not overlapped 

    if (!fSuccess)
    {
        _tprintf(TEXT("WriteFile to pipe failed. GLE=%d\n"), GetLastError());
        return -1;
    }

    printf("\nMessage sent to server, receiving reply as follows:\n");

    do
    {
        // Read from the pipe. 

        fSuccess = ReadFile(
            hPipe,    // pipe handle 
            chBuf,    // buffer to receive reply 
            BUFSIZE*sizeof(TCHAR),  // size of buffer 
            &cbRead,  // number of bytes read 
            NULL);    // not overlapped 

        if (!fSuccess && GetLastError() != ERROR_MORE_DATA)
            break;

        _tprintf(TEXT("\"%s\"\n"), chBuf);
    } while (!fSuccess);  // repeat loop if ERROR_MORE_DATA 

    if (!fSuccess)
    {
        _tprintf(TEXT("ReadFile from pipe failed. GLE=%d\n"), GetLastError());
        return -1;
    }

    printf("\n<End of message, press ENTER to terminate connection and exit>");
    _getch();

    CloseHandle(hPipe);

    return 0;
}

举一个简单的例子来说明下:

#include <iostream>
#include <windows.h>
int main(void)
{
    HANDLE hPipe;
    char buffer[1024];
    DWORD dwRead;

    hPipe = CreateNamedPipe(TEXT("\\\\.\\pipe\\Wang"),
        PIPE_ACCESS_DUPLEX | PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,   // FILE_FLAG_FIRST_PIPE_INSTANCE is not needed but forces CreateNamedPipe(..) to fail if the pipe already exists...
        PIPE_WAIT,
        1,
        1024 * 16,
        1024 * 16,
        NMPWAIT_USE_DEFAULT_WAIT,
        NULL);

    while (hPipe != INVALID_HANDLE_VALUE)
    {
        if (ConnectNamedPipe(hPipe, NULL) != FALSE)   // wait for someone to connect to the pipe//没有等到client就一直返回false
        {
            while (ReadFile(hPipe, buffer, sizeof(buffer) - 1, &dwRead, NULL) != FALSE)
            {
                /* add terminating zero */
                buffer[dwRead] = '\0';

                /* do something with data in buffer */
                printf("%s", buffer);
            }
        }

        DisconnectNamedPipe(hPipe);
    }
    system("pause");
    return 0;
}

运行起这个服务端,由于管道中黑没有任何数据,所以什么都读不到,是一块黑屏:

当我们运行客户端:

#include<iostream>
#include<windows.h>

int main(void)
{
    HANDLE hPipe;
    DWORD dwWritten;


    hPipe = CreateFile(TEXT("\\\\.\\pipe\\Wang"),
        GENERIC_READ | GENERIC_WRITE,
        0,
        NULL,
        OPEN_EXISTING,
        0,
        NULL);
    if (hPipe != INVALID_HANDLE_VALUE)
    {
        WriteFile(hPipe,
            "Hello Pipe\n",
            12,   // = length of string + terminating '\0' !!!
            &dwWritten,
            NULL);

        CloseHandle(hPipe);
    }

    return (0);
}

服务端从管道中读到了数据:

再运行,又读到一次数据:

然后,服务端就一直处在循环等待的状态。

参考:

http://www.cplusplus.com/forum/general/139656/

http://stackoverflow.com/questions/26561604/create-named-pipe-c-windows

posted @ 2016-01-19 20:42  _No.47  阅读(1105)  评论(0编辑  收藏  举报