< Name Pipes DACL, Threads, Overlapped, Client, Server | Name Pipes Main | Multithreaded & Overlapped Server Examples >


 

 

Named Pipes 15 Part 3

 

 

What do we have in this chapter 15 part 3?

  1. Other API Calls

  2. Program Examples

  3. Name Pipe Client Example 1

  4. Name Pipe Client Example 2

 

Other API Calls

 

There are several additional named pipe functions that we haven't touched on yet. The first set of these API functions, CallNamedPipe() and TransactNamedPipe(), is designed to reduce coding complexity in an application. Both functions perform a write and read operation in one call. The CallNamedPipe() function allows a client application to connect to a message-type pipe (and waits if an instance of the pipe is not available), writes to and reads from the pipe, and then closes the pipe. This is practically an entire client application written in one call. CallNamedPipe() is defined as follows:

 

BOOL CallNamedPipe(

  LPCTSTR lpNamedPipeName,

  LPVOID lpInBuffer,

  DWORD nInBufferSize,

  LPVOID lpOutBuffer,

  DWORD nOutBufferSize,

  LPDWORD lpBytesRead,

  DWORD nTimeOut

);

 

The lpNamedPipeName parameter is a string that represents the named pipe in UNC form. The lpInBuffer and nInBufferSize parameters represent the address and the size of the buffer that the application uses to write data to the server. The lpOutBuffer and nOutBufferSize parameters represent the address and the size of the buffer that the application uses to retrieve data from the server. The lpBytesRead parameter receives the number of bytes read from the pipe. The nTimeOut parameter specifies how many milliseconds to wait for the named pipe to be available.

The TransactNamedPipe() function can be used in both a client and a server application. It is designed to combine read and write operations in one API call, thus optimizing network I/O by reducing send and receive transactions in the MSNP redirector. TransactNamedPipe() is defined as follows:

 

BOOL TransactNamedPipe(

  HANDLE hNamedPipe,

  LPVOID lpInBuffer,

  DWORD nInBufferSize,

  LPVOID lpOutBuffer,

  DWORD nOutBufferSize,

  LPDWORD lpBytesRead,

  LPOVERLAPPED lpOverlapped

);

 

The hNamedPipe parameter identifies the named pipe returned by the CreateNamedPipe() or CreateFile() API functions. The lpInBuffer and nInBufferSize parameters represent the address and the size of the buffer that the application uses to write data to the pipe. The lpOutBuffer and nOutBufferSize parameters represent the address and the size of the buffer that the application uses to retrieve data from the pipe. The lpBytesRead parameter receives the number of bytes read from the pipe. The lpOverlapped parameter allows this TransactNamedPipe to operate asynchronously using overlapped I/O.

The next set of functions - GetNamedPipeHandleState(), SetNamedPipeHandleState(), and GetNamedPipeInfo() - are designed to make named pipe client and server communication more flexible at run time. For example, you can use these functions to change the operating mode of a pipe at run time from message mode to byte mode and vice versa. GetNamedPipeHandleState() retrieves information such as the operating mode (message mode and byte mode), pipe instance count, and buffer caching information about a specified named pipe. The information that GetNamedPipeHandleState() returns can vary during the lifetime of an instance of the named pipe. GetNamedPipeHandleState() is defined as follows:

 

BOOL GetNamedPipeHandleState(

    HANDLE hNamedPipe,

    LPDWORD lpState,

    LPDWORD lpCurInstances,

    LPDWORD lpMaxCollectionCount,

    LPDWORD lpCollectDataTimeout,

    LPTSTR lpUserName,

    DWORD nMaxUserNameSize

);

 

The hNamedPipe parameter identifies the named pipe returned by the CreateNamedPipe() or CreateFile() function. The lpState parameter is a pointer to a variable that receives the current operating mode of the pipe handle. The lpState parameter can return the value PIPE_NOWAIT or the value PIPE_READMODE_MESSAGE. The lpCurInstances parameter is a pointer to a variable that receives the number of current pipe instances. The lpMaxCollectionCount parameter receives the maximum number of bytes to be collected on the client's computer before transmission to the server. The lpCollectDataTimeout parameter receives the maximum time in milliseconds that can pass before a remote named pipe transfers information over a network. The lpUserName and nMaxUserNameSize parameters represent a buffer that receives a null-terminated string containing the user name string of the client application. The SetNamedPipeHandleState() function allows you to change the pipe characteristics retrieved with GetNamedPipeHandleState(). SetNamedPipeHandleState() is defined as follows:

 

BOOL SetNamedPipeHandleState(

    HANDLE hNamedPipe,

    LPDWORD lpMode,

    LPDWORD lpMaxCollectionCount,

    LPDWORD lpCollectDataTimeout

);

 

The hNamedPipe parameter identifies the named pipe returned by CreateNamedPipe() or CreateFile(). The lpMode parameter sets the operating mode of a pipe. The lpMaxCollectionCount parameter specifies the maximum number of bytes collected on the client computer before data is transmitted to the server. The lpCollectDataTimeout parameter specifies the maximum time in milliseconds that can pass before a remote named pipe client transfers information over the network.

The GetNamedPipeInfo() API function is used to retrieve buffer size and maximum pipe instance information. GetNamedPipeInfo is defined as follows:

 

BOOL GetNamedPipeInfo(

  HANDLE hNamedPipe,

  LPDWORD lpFlags,

  LPDWORD lpOutBufferSize,

  LPDWORD lpInBufferSize,

  LPDWORD lpMaxInstances

);

 

The hNamedPipe parameter identifies the named pipe returned by CreateNamedPipe() or CreateFile(). The lpFlags parameter retrieves the type of the named pipe and determines whether it is a server or a client and whether the pipe is in byte mode or message mode. The lpOutBufferSize parameter determines the size in bytes of the internal buffer for outgoing data. The lpInBufferSize parameter receives the size of the internal buffer for incoming data. The lpMaxInstance parameter receives the maximum number of pipe instances that can be created.

The final API function, PeekNamedPipe(), allows an application to look at the data in a named pipe without removing it from the pipe's internal buffer. This function is useful if an application wants to poll for incoming data to avoid blocking on the ReadFile() API call. The function can also be useful for applications that need to examine data before they actually receive it. For example, an application might want to adjust its application buffers based on the size of incoming messages. PeekNamedPipe() is defined as follows:

 

BOOL PeekNamedPipe(

  HANDLE hNamedPipe,

  LPVOID lpBuffer,

  DWORD nBufferSize,

  LPDWORD lpBytesRead,

  LPDWORD lpTotalBytesAvail,

  LPDWORD lpBytesLeftThisMessage

);

 

The hNamedPipe parameter identifies the named pipe returned by CreateNamedPipe() or CreateFile(). The lpBuffer and nBufferSize parameters represent the receiving buffer along with the receiving buffer size to retrieve data from the pipe. The lpBytesRead parameter receives the number of bytes read from the pipe into the lpBuffer parameter. The lpTotalBytesAvail parameter receives the total number of bytes that are available to be read from the pipe. The lpBytesLeftThisMessage parameter receives the number of bytes remaining in a message if a pipe is opened in message mode. If a message cannot fit in the lpBuffer parameter, the remaining bytes in a message are returned. This parameter always returns 0 for byte-mode named pipes.

 

Program Examples

 

Name Pipe Client Example 1

 

Create a new empty Win32 console mode application and add the project/solution name.

 

Winsock: Name Pipe Client Program Example 1

 

Add the following source code.

 

// Purpose:

//

//     This program is a simple named pipe client that demonstrates

//     the API calls needed to successfully develop a basic named

//     pipe client application. When this application successfully

//     connects to a named pipe, the message "This is a test string" is written to the server.

//

//     There are four basic steps needed to implement a client:

//

//     1. Wait for a Named Pipe instance to become available using the WaitNamedPipe() API function.

//     2. Connect to the Named Pipe using the CreateFile() API function.

//     3. Send data to or receive data from the server using the WriteFile() and ReadFile() API functions.

//     4. Close the Named Pipe session using the CloseHandle() API functions.

//

// Command line options: None

#include <windows.h>

#include <stdio.h>

 

#define PIPE_NAME "\\\\.\\Pipe\\Jim"

 

void main(int argc, char **argv)

{

            HANDLE PipeHandle;

            DWORD BytesWritten;

 

            if (WaitNamedPipe((LPCWSTR)PIPE_NAME, NMPWAIT_WAIT_FOREVER) == 0)

            {

                        printf("WaitNamedPipe() failed with error code %d\n", GetLastError());

                        return;

            }

            else

                        printf("WaitNamedPipe() is OK!\n");

 

            // Create the named pipe file handle

            if ((PipeHandle = CreateFile((LPCWSTR)PIPE_NAME,

                        GENERIC_READ | GENERIC_WRITE, 0,

                        (LPSECURITY_ATTRIBUTES) NULL, OPEN_EXISTING,

                        FILE_ATTRIBUTE_NORMAL,

                        (HANDLE) NULL)) == INVALID_HANDLE_VALUE)

            {

                        printf("CreateFile() failed with error code %d\n", GetLastError());

                        return;

            }

            else

                        printf("CreateFile() is OK!\n");

 

            if (WriteFile(PipeHandle, "This is a test string", 21, &BytesWritten,  NULL) == 0)

            {

                        printf("WriteFile() failed with error code %d\n", GetLastError());

                        printf("Closing the handle...\n");

                        CloseHandle(PipeHandle);

                        return;

            }

            else

                        printf("WriteFile() is fine!\n");

            printf("Wrote %d bytes", BytesWritten);

            printf("Closing the handle...\n");

            CloseHandle(PipeHandle);

}

 

Build and run the project.

 

-------------------------------------------------

Winsock: Name Pipe Client Program Example 1 console sample output

 

Name Pipe Client Example 2

 

Create a new empty Win32 console mode application and add the project/solution name.

 

Winsock: Name Pipe Client Program Example 2 VC++ .NET project

 

Add the following source code.

 

#include <windows.h>

#include <stdio.h>

#include <conio.h>

#include <tchar.h>

 

#define BUFSIZE 1024

 

int main(int argc, char *argv[])

{

   HANDLE hPipe;

   LPTSTR lpvMessage= L"Default message from client!";

   TCHAR chBuf[BUFSIZE];

   BOOL fSuccess;

   DWORD cbRead, cbWritten, dwMode;

   LPTSTR lpszPipename = L"\\\\.\\pipe\\jim";

 

   if(argc > 1)

      lpvMessage = (LPTSTR)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)

      {

         printf("CreateFile(): Could not open pipe! Error code %d\n", GetLastError());

         return 0;

      }

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

      if (!WaitNamedPipe(lpszPipename, 20000))

      {

         printf("CreateFile(): Could not open pipe! They are busy, I do some waiting...\n");

         return 0;

      }

         printf("CreateFile() should be fine!\n");

   }

 

   // 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)

   {

      printf("SetNamedPipeHandleState() failed! %d\n", GetLastError());

      return 0;

   }

   else

      printf("SetNamedPipeHandleState() is OK!\n");

 

   // Send a message to the pipe server

   fSuccess = WriteFile(

      hPipe,                           // pipe handle

      lpvMessage,                // message

      (lstrlen(lpvMessage)+1)*sizeof(CHAR), // message length

      &cbWritten,                  // bytes written

      NULL);                          // not overlapped

   if (!fSuccess)

   {

      printf("WriteFile() failed! Error code %d", GetLastError());

      return 0;

   }

   else

      printf("WriteFile() is OK!\n");

 

   do

   {

         // Read from the pipe

         fSuccess = ReadFile(

         hPipe,                                  // pipe handle

         chBuf,                                  // buffer to receive reply

         BUFSIZE*sizeof(CHAR),  // size of buffer

         &cbRead,                            // number of bytes read

         NULL);                                 // not overlapped

 

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

      {

            printf("No more data!\n");

            break;

      }

      printf("%S\n", chBuf );

   } while (!fSuccess);  // repeat loop if ERROR_MORE_DATA

 

   printf("Closing the pipe handle...\n");

   CloseHandle(hPipe);

   return 0;

}

 

Build and run the project.

 

Winsock: Name Pipe Client Program Example 2 sample console output

 

 

 


< Name Pipes DACL, Threads, Overlapped, Client, Server | Name Pipes Main | Multithreaded & Overlapped Server Examples >