NMPIPE.C


/******************************************************************************\
* This is a part of the Microsoft Source Code Samples.
* Copyright 1993 - 1998 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] <string> <pipename>");
puts("'/s' to start as server, '/c' to start as client");
puts("<string>: string to write to the pipe");
puts("<pipename>: full UNC name of pipe (optional)");
puts("example: nmpipe /s \"hello from server \" \\\\.\\pipe\\pipetst");
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;
}