
/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples. 
*       Copyright (C) 1993-1995 Microsoft Corporation.
*       All rights reserved. 
*       This source code is only intended as a supplement to 
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the 
*       Microsoft samples programs.
\******************************************************************************/

#include <stdio.h>
#include <windows.h>
#include <stdlib.h>
#include <process.h>

/* Error checking macro. If bSuccess == TRUE, print error info */
#define PERR(bSuccess, szApi) {if (!(bSuccess)) printf("%s: Error %d from %s \
    on line %d\n", __FILE__, GetLastError(), szApi, __LINE__);}

#define DEFNAME "\\\\.\\pipe\\nmpipe" /* Default pipe name */
/* Define OVERLAPPED_IO to TRUE to use Overlapped IO. Otherwise define as
   FALSE */
#define OVERLAPPED_IO TRUE
//#define OVERLAPPED_IO FALSE

/* structure to pass into the writer thread */
typedef struct {
  HANDLE hPipe;     /* pipe write handle */
  char *szData;     /* data to repeatedly write */
  int iDataSize;    /* size of buffer pointed to by szData */
} WRITER_PARAMS;

/*
 ** BOOL pipeCheck(HANDLE h)
 *
 *  PARAMETERS: HANDLE hPipe: pipe handle to close if an error condition exists
 *
 *  DESCRIPTION: if GetLastError() returns a common error generated by a
 *               broken pipe, close the pipe handle. Call this right after a
 *               pipe operation.
 *
 *  RETURNS: FALSE if the pipe is broken, TRUE if not.
 *
 */
BOOL pipeCheck(HANDLE hPipe)
{
  DWORD dwLastError = GetLastError();

  if (dwLastError == ERROR_BROKEN_PIPE || dwLastError == ERROR_NO_DATA)
    {
    puts("\n* pipe broken, closing...");
    CloseHandle(hPipe);
    return(FALSE);
    }
  if (dwLastError == ERROR_INVALID_HANDLE) /* is handle already closed? */
    return(FALSE);
  return(TRUE);
} /* pipeCheck() */

/*
 ** void reader(HANDLE hPipe)
 *
 *  PARAMETERS: HANDLE hPipe: pipe handle to read from
 *
 *  DESCRIPTION: read from the handle and dump data to the console.
 *
 *  RETURNS: none
 *
 */
void reader(HANDLE hPipe)
{
  char buf[64];
  DWORD dwRead;
  BOOL bSuccess;
  OVERLAPPED ol;

  if (OVERLAPPED_IO) /* if overlapped, prepare OVERLAPPED structure */
  {
    memset(&ol, 0, sizeof(ol));
    ol.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
    PERR(ol.hEvent, "CreateEvent");
  }
  while (1) 
    {
    bSuccess = ReadFile(hPipe, buf, sizeof(buf), &dwRead,
        OVERLAPPED_IO ? &ol : NULL);
    if (!bSuccess && GetLastError() == ERROR_IO_PENDING)
      bSuccess = GetOverlappedResult(hPipe, &ol, &dwRead, TRUE);
    /* If ReadFile or GetOverlappedResult fails, check pipe. If pipe is
       broken, fall out of loop */
    if (!bSuccess && !pipeCheck(hPipe))
      break;
    /* else check for any other kinds of errors that may have occurred */
    PERR(bSuccess, "ReadFile");
    printf("%.*s", dwRead, buf); /* print only number of chars read */
    } /* while */
  CloseHandle(ol.hEvent);
  _endthread();
} /* reader() */

/*
 ** void writer(WRITER_PARAMS *writer_params)
 *
 *  PARAMETERS: WRITER_PARAMS *writer_params: misc info needed for writing to
 *              the pipe
 *
 *  DESCRIPTION: write data from WRITER_PARAMS to the pipe
 *
 *  RETURNS: none
 *
 */
void writer(WRITER_PARAMS *writer_params)
{
  DWORD dwWritten;
  BOOL bSuccess;
  OVERLAPPED ol;

  if (OVERLAPPED_IO)
  {
    memset(&ol, 0, sizeof(ol));
    ol.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
    PERR(ol.hEvent, "CreateEvent");
  }
  while(1) 
  {
    bSuccess = WriteFile(writer_params->hPipe, writer_params->szData,
        writer_params->iDataSize, &dwWritten, OVERLAPPED_IO ? &ol : NULL);
    if (!bSuccess && GetLastError() == ERROR_IO_PENDING)
      bSuccess = GetOverlappedResult(writer_params->hPipe, &ol, &dwWritten,
          TRUE);
    /* If ReadFile or GetOverlappedResult fails, check pipe. If pipe is
       broken, fall out of loop */
    if (!bSuccess && !pipeCheck(writer_params->hPipe))
      break;
    /* else check for any other kinds of errors that may have occurred */
    PERR(bSuccess, "WriteFile");
    } /* while */
  free(writer_params);
  CloseHandle(ol.hEvent);
  _endthread();
} /* writer() */

/*
 ** main()
 *
 *  DESCRIPTION: Connect pipe instances to clients. Start a reader thread for
 *               each client. If OVERLAPPED_IO is defined, also start a
 *               writer thread for full duplex pipe I/O testing. If this is
 *               a client, connect to the server pipe and start a writer
 *               thread. If OVERLAPPED_IO, also start a reader thread.
 *
 */

void main(int argc, char *argv[])
{
  HANDLE hPipe;
  BOOL bSuccess;
  BOOL bNotConnected;
  char *szPname;
  WRITER_PARAMS *writer_params;
  DWORD dwClients;
  SECURITY_ATTRIBUTES saPipe;
  OVERLAPPED ol;
  DWORD dwRead;

  if (argc < 3)
    {
    puts("nmpipe [/s|/c] <char> <pipename>");
    puts("'/s' to start as server, '/c' to start as client");
    puts("<char>: single char to write to the pipe");
    puts("<pipename>: full UNC name of pipe (optional)");
    return;
    }
  szPname = (argc > 3) ? argv[3] : DEFNAME;
  if (tolower(argv[1][1]) == 's')
    {
    if (OVERLAPPED_IO)
    {
      memset(&ol, 0, sizeof(ol));
      ol.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
      PERR(ol.hEvent, "CreateEvent");
    }
    SetConsoleTitle("SERVER: nmpipe sample");
    /* set up a NULL DACL in our pipe security descriptor to allow anyone to
       connect to the pipe server */
    saPipe.lpSecurityDescriptor =
        (PSECURITY_DESCRIPTOR) malloc(SECURITY_DESCRIPTOR_MIN_LENGTH);
    InitializeSecurityDescriptor(saPipe.lpSecurityDescriptor,
        SECURITY_DESCRIPTOR_REVISION);
    SetSecurityDescriptorDacl(saPipe.lpSecurityDescriptor, TRUE, (PACL) NULL,
        FALSE);
    saPipe.nLength = sizeof(saPipe);
    saPipe.bInheritHandle = TRUE;
    while(1)
      {
      /* Create a named pipe: duplex, type byte, readmode byte, unlimited
         instances, default timeout of 60s */
      hPipe = CreateNamedPipe(szPname, PIPE_ACCESS_DUPLEX |
          (OVERLAPPED_IO ? FILE_FLAG_OVERLAPPED : 0), PIPE_TYPE_BYTE |
          PIPE_READMODE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 0, 0,
          60000, &saPipe);
      PERR(hPipe != INVALID_HANDLE_VALUE, "CreateNamedPipe");
      puts("\n* pipe created, waiting for connection...");
      bSuccess = ConnectNamedPipe(hPipe, OVERLAPPED_IO ? &ol : NULL);
      if (!bSuccess && GetLastError() == ERROR_IO_PENDING)
        bSuccess = GetOverlappedResult(hPipe, &ol, &dwRead, TRUE);
      /* check return from either ConnectNamedPipe or GetOverlappedResult.
         If a client managed to connect between the CreateNamedPipe and
         ConnectNamedPipe calls, ERROR_PIPE_CONNECTED will result */
      if (!bSuccess && GetLastError() != ERROR_PIPE_CONNECTED)
        {
        /* something went wrong, report error, close instance and try again */
        PERR(bSuccess, "ConnectNamedPipe");
        CloseHandle(hPipe);
        continue;
        }
      /* find out how many pipe instances there currently are */
      bSuccess = GetNamedPipeHandleState(hPipe, NULL, &dwClients, NULL, NULL,
          NULL, 0);
      PERR(bSuccess, "GetNamedPipeHandleState"); 
      printf("\n* %d clients connected", dwClients);
      _beginthread(reader, 0, hPipe);
      writer_params = malloc(sizeof(WRITER_PARAMS));
      writer_params->hPipe = hPipe;
      writer_params->szData = argv[2];
      writer_params->iDataSize = strlen(argv[2]);
      /* WARNING! reading and writing at the same time to a non-overlapped
         pipe is a no-no! If not overlapped, server only reads and client
         only writes */
      if (OVERLAPPED_IO)
        _beginthread(writer, 0, writer_params);
      } /* while */
    CloseHandle(ol.hEvent);
    CloseHandle(hPipe);
    }
  else /* no '/s', assume it's a client */
    {
    char szTemp[64];

    sprintf(szTemp, "Client %s", argv[2]);
    SetConsoleTitle(szTemp);
    bNotConnected = TRUE;
    while (bNotConnected)
      {
      /* attempt to connect to pipe instance */
      hPipe = CreateFile(szPname, GENERIC_READ | GENERIC_WRITE, 0, NULL,
          OPEN_EXISTING, OVERLAPPED_IO ? FILE_FLAG_OVERLAPPED : 0, NULL);
      if (GetLastError() == ERROR_PIPE_BUSY)
        {
        puts("Pipe busy, waiting for a pipe instance...");
        bSuccess = WaitNamedPipe(szPname, NMPWAIT_USE_DEFAULT_WAIT);
        PERR(bSuccess, "WaitNamedPipe");
        }
      else
        {
        PERR(hPipe != INVALID_HANDLE_VALUE, "CreateFile");
        bNotConnected = (hPipe == INVALID_HANDLE_VALUE);
        }
      } /* while */
    puts("Connected to pipe...");
    if (OVERLAPPED_IO) /* if overlapped, start reader thread */
      _beginthread(reader, 0, hPipe);
    writer_params = malloc(sizeof(WRITER_PARAMS));
    writer_params->hPipe = hPipe;
    writer_params->szData = argv[2];
    writer_params->iDataSize = strlen(argv[2]);
    writer(writer_params);
    } /* else */
  free(saPipe.lpSecurityDescriptor);
  CloseHandle(hPipe);
  CloseHandle(ol.hEvent);
  return;
}
