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

#define BUFSIZE 4096
 
DWORD WINAPI InstanceThread(LPVOID);
VOID GetAnswerToRequest(LPTSTR, LPTSTR, LPDWORD);
 
int main(int argc, char **argv) 
{ 
   BOOL fConnected;
   DWORD dwThreadId;
   HANDLE hPipe, hThread;
   LPTSTR lpszPipename = L"\\\\.\\pipe\\jim";
   
   // 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 
   printf("Pipe Server: Awaiting initial client connection on %S\n", lpszPipename);

   for (;;) 
   { 
      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) 
      {
          printf("CreateNamedPipe() failed, error code %d.\n", GetLastError());
          return -1;
      }
	  else
		  printf("CreateNamedPipe() should be fine!\n");
 
      // Wait for the client to connect; if it succeeds,
      // the function returns a nonzero value. If the function
      // returns zero, GetLastError returns ERROR_PIPE_CONNECTED 
      fConnected = ConnectNamedPipe(hPipe, NULL) ? TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
 
      if (fConnected)
      {
         printf("Client connected, creating a processing thread...\n");
      
         // Create a thread for this client
         hThread = CreateThread(
            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)
         {
           printf("CreateThread() failed, error code %d.\n", GetLastError());
		   return -1;
         }
		 else
		 {
			 printf("CreateThread() should be fine!\n");
			 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();
   CHAR* pchRequest = (CHAR*)HeapAlloc(hHeap, 0, BUFSIZE*sizeof(CHAR));
   CHAR* pchReply   = (CHAR*)HeapAlloc(hHeap, 0, BUFSIZE*sizeof(CHAR));

   DWORD cbBytesRead = 0, cbReplyBytes = 0, cbWritten = 0;
   BOOL fSuccess = FALSE;
   HANDLE hPipe  = NULL;
   
   // Do some extra error checking in a compound statement, this could be
   // made more specific for different recovery routines if more robust
   // recovery is needed. For this example we simply quit
   if (lpvParam == NULL || pchRequest == NULL || pchReply == NULL)
   {
       printf( "\nERROR - Pipe Server Failure: InstanceThread() has an unexpected NULL value.\n");
       return 0;
   }
   
   // 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
	   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)
         break;
      GetAnswerToRequest((LPTSTR)pchRequest, (LPTSTR)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) 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);
   DisconnectNamedPipe(hPipe);
   CloseHandle(hPipe);

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

   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
{
    printf("Client Request String:\"%S\"\n", pchRequest);
    StringCchCopy(pchReply, BUFSIZE, L"\"Default answer from server\"");
    *pchBytes = (lstrlen(pchReply)+1)*sizeof(CHAR);
}
