#include "StdAfx.h"
#include "NPServer.h"
#include <process.h>

CNPServer::CNPServer(LPCTSTR lpszPipename):m_hConnectEvent(NULL)
{
	ZeroMemory(&m_ovpConnect, sizeof(OVERLAPPED));
	if ( lpszPipename != NULL)
		_tcscpy_s(m_szPipename, lpszPipename);
}

CNPServer::~CNPServer(void)
{
}

BOOL CNPServer::InitNPServer()
{
	if ( m_hConnectEvent != NULL )
		return TRUE;
	
	// 创建连接操作的响应事件对象;
	m_hConnectEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
	if (m_hConnectEvent == NULL)
		return FALSE;

	m_ovpConnect.hEvent = m_hConnectEvent;
	// 创建接收客户端的线程;
	HANDLE hThread = NULL;
	unsigned int dwThreadId = 0;
	hThread = (HANDLE)_beginthreadex(NULL, 0, AcceptThread, this, NULL, &dwThreadId);
	if ( hThread == NULL)
		return FALSE;

	CloseHandle(hThread);

	return TRUE;
}

unsigned CNPServer::AcceptThread(LPVOID lpParam)
{
	CNPServer *pthis = (CNPServer*)lpParam;
	HANDLE hPipe = NULL; 
	LPPIPEINST lpPipeInst;
	DWORD dwWait, cbRet;
	BOOL fSuccess, fPendingIO;

	fPendingIO = pthis->CreateAndConnectInstance(&pthis->m_ovpConnect);

	while (1)
	{
		// 等待客户端连接或读写操作完成;
		dwWait = WaitForSingleObjectEx(pthis->m_hConnectEvent, INFINITE,TRUE); 
		switch (dwWait)
		{
			// 连接完成;
		case WAIT_OBJECT_0:
			// If an operation is pending, get the result of the 
			// connect operation. 

			if (fPendingIO)
			{
				fSuccess = GetOverlappedResult(
					hPipe,     // pipe handle 
					&pthis->m_ovpConnect, // OVERLAPPED structure 
					&cbRet,    // bytes transferred 
					FALSE);    // does not wait 
				if (!fSuccess)
				{
					printf("ConnectNamedPipe (%d)\n", GetLastError());
					OutputDebugString(_T("ConnectNamedPipe GetOverlappedResult"));
					return 0;
				}
			}

			// Allocate storage for this instance. 

			lpPipeInst = (LPPIPEINST)GlobalAlloc(GPTR, sizeof(PIPEINST));
			if (lpPipeInst == NULL)
			{
				printf("GlobalAlloc failed (%d)\n", GetLastError());
				return 0;
			}

			lpPipeInst->hPipeInst = hPipe;

			// Start the read operation for this client. 
			// Note that this same routine is later used as a 
			// completion routine after a write operation. 

			lpPipeInst->cbToWrite = 0;
			CompletedWriteRoutine(pthis, 0, 0, (LPOVERLAPPED)lpPipeInst);

			// Create new pipe instance for the next client. 

			fPendingIO = pthis->CreateAndConnectInstance(&pthis->m_ovpConnect);
			break;

			// The wait is satisfied by a completed read or write 
			// operation. This allows the system to execute the 
			// completion routine. 

		case WAIT_IO_COMPLETION:
			break;

			// An error occurred in the wait function. 

		default:
		{
			printf("WaitForSingleObjectEx (%d)\n", GetLastError());
			OutputDebugString(_T("WaitForSingleObjectEx\n"));
			return 0;
		}
		}
	}

	return 0;
}

BOOL CNPServer::CreateAndConnectInstance(LPOVERLAPPED lpoOverlap)
{
	HANDLE hPipe = CreateNamedPipe( 
		m_szPipename,             // pipe name 
		PIPE_ACCESS_DUPLEX |      // read/write access 
		FILE_FLAG_OVERLAPPED,     // overlapped mode 
		PIPE_TYPE_MESSAGE |       // message-type pipe 
		PIPE_READMODE_MESSAGE |   // message read mode 
		PIPE_WAIT,                // blocking mode 
		PIPE_UNLIMITED_INSTANCES, // unlimited instances 
		BUFSIZE*sizeof(TCHAR),    // output buffer size 
		BUFSIZE*sizeof(TCHAR),    // input buffer size 
		PIPE_TIMEOUT,             // client time-out 
		NULL);                    // default security attributes
	if (hPipe == INVALID_HANDLE_VALUE) 
	{
		printf("CreateNamedPipe failed with %d.\n", GetLastError()); 
		OutputDebugString(_T("CreateNamedPipe failed"));
		return 0;
	}

	// Call a subroutine to connect to the new client. 

	return ConnectToNewClient(hPipe, lpoOverlap); 
}

BOOL CNPServer::ConnectToNewClient(HANDLE hPipe, LPOVERLAPPED lpo)
{
	TCHAR szMsg[MAX_PATH] = {0};
	BOOL fConnected, fPendingIO = FALSE; 

	// Start an overlapped connection for this pipe instance. 
	fConnected = ConnectNamedPipe(hPipe, lpo); 

	// Overlapped ConnectNamedPipe should return zero. 
	if (fConnected) 
	{
		_stprintf_s(szMsg, _T("ConnectNamedPipe failed with %d.\n"), GetLastError());
		//printf("ConnectNamedPipe failed with %d.\n", GetLastError()); 
		OutputDebugString(szMsg);
		return 0;
	}

	switch (GetLastError()) 
	{ 
		// The overlapped connection in progress. 
	case ERROR_IO_PENDING: 
		//OutputDebugString(_T("The overlapped connection in progress\n"));
		fPendingIO = TRUE; 
		break; 

		// Client is already connected, so signal an event. 

	case ERROR_PIPE_CONNECTED: 
		OutputDebugString(_T("Client is already connected, so signal an event\n"));
		if (SetEvent(lpo->hEvent)) 
			break; 

		// If an error occurs during the connect operation... 
	default: 
		{
			_stprintf_s(szMsg, _T("ConnectNamedPipe failed with %d.\n"), GetLastError());
			OutputDebugString(szMsg);
			//printf("ConnectNamedPipe failed with %d.\n", GetLastError());
			return 0;
		}
	} 
	return fPendingIO; 
}

void CNPServer::DisconnectAndClose(LPPIPEINST lpPipeInst)
{
	// Disconnect the pipe instance. 

	if (! DisconnectNamedPipe(lpPipeInst->hPipeInst) ) 
	{
		printf("DisconnectNamedPipe failed with %d.\n", GetLastError());
	}

	// Close the handle to the pipe instance. 

	CloseHandle(lpPipeInst->hPipeInst); 

	// Release the storage for the pipe instance. 

	if (lpPipeInst != NULL) 
		GlobalFree(lpPipeInst); 
}

void WINAPI CNPServer::CompletedWriteRoutine(CNPServer *pInstance, DWORD dwErr, DWORD cbWritten, LPOVERLAPPED lpOverLap)
{
	LPPIPEINST lpPipeInst; 
	BOOL fRead = FALSE; 

	// lpOverlap points to storage for this instance. 

	lpPipeInst = (LPPIPEINST) lpOverLap; 

	// The write operation has finished, so read the next request (if 
	// there is no error). 

	if ((dwErr == 0) && (cbWritten == lpPipeInst->cbToWrite)) 
		fRead = ReadFileEx( 
		lpPipeInst->hPipeInst, 
		lpPipeInst->chRequest, 
		BUFSIZE*sizeof(TCHAR), 
		(LPOVERLAPPED) lpPipeInst, 
		(LPOVERLAPPED_COMPLETION_ROUTINE) CompletedReadRoutine); 

	// Disconnect if an error occurred. 

	if (! fRead) 
		pInstance->DisconnectAndClose(lpPipeInst);
}

void WINAPI CNPServer::CompletedReadRoutine(CNPServer *pInstance, DWORD dwErr, DWORD cbBytesRead, LPOVERLAPPED lpOverLap)
{
	LPPIPEINST lpPipeInst; 
	BOOL fWrite = FALSE; 

	// lpOverlap points to storage for this instance. 

	lpPipeInst = (LPPIPEINST) lpOverLap; 

	// The read operation has finished, so write a response (if no 
	// error occurred). 

	if ((dwErr == 0) && (cbBytesRead != 0)) 
	{ 
		pInstance->GetAnswerToRequest(lpPipeInst);

		fWrite = WriteFileEx( 
			lpPipeInst->hPipeInst, 
			lpPipeInst->chReply, 
			lpPipeInst->cbToWrite, 
			(LPOVERLAPPED) lpPipeInst, 
			(LPOVERLAPPED_COMPLETION_ROUTINE) CompletedWriteRoutine); 
	} 

	// Disconnect if an error occurred. 

	if (! fWrite) 
		pInstance->DisconnectAndClose(lpPipeInst);
}

void CNPServer::GetAnswerToRequest(LPPIPEINST pipe)
{
	_tprintf( TEXT("[%d] %s\n"), pipe->hPipeInst, pipe->chRequest);
	_tcscpy_s( pipe->chReply, BUFSIZE, TEXT("Default answer from server") );
	pipe->cbToWrite = (lstrlen(pipe->chReply)+1)*sizeof(TCHAR);
}