connectivity/com.nokia.tcf/native/TCFNative/TCFClient/ClientManager.cpp
changeset 60 9d2210c8eed2
child 505 6de8d9cfdda1
child 916 6743933eec70
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/connectivity/com.nokia.tcf/native/TCFNative/TCFClient/ClientManager.cpp	Mon Apr 06 15:18:48 2009 -0500
@@ -0,0 +1,836 @@
+/*
+* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of the License "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description: 
+*
+*/
+// ClientManager.cpp: implementation of the CClientManager class.
+//
+//////////////////////////////////////////////////////////////////////
+
+#include "stdafx.h"
+#include "ClientManager.h"
+#include "TCErrorConstants.h"
+#include "resource.h"
+#include <stdio.h>
+
+#ifdef _DEBUG
+extern BOOL gDoLogging;
+extern char TCDebugMsg[100];
+#define TCDEBUGOPEN() if (gDoLogging) m_DebugLog->WaitForAccess();
+#define TCDEBUGLOGS(s) if (gDoLogging) sprintf(TCDebugMsg,"%s", s); if (gDoLogging) m_DebugLog->log(TCDebugMsg);
+#define TCDEBUGLOGA1(s, a1) if (gDoLogging) sprintf(TCDebugMsg, s, a1); if (gDoLogging) m_DebugLog->log(TCDebugMsg);
+#define TCDEBUGLOGA2(s, a1, a2) if (gDoLogging) sprintf(TCDebugMsg, s, a1, a2); if (gDoLogging) m_DebugLog->log(TCDebugMsg);
+#define TCDEBUGLOGA3(s, a1, a2, a3) if (gDoLogging) sprintf(TCDebugMsg, s, a1, a2, a3); if (gDoLogging) m_DebugLog->log(TCDebugMsg);
+#define TCDEBUGCLOSE() if (gDoLogging) m_DebugLog->ReleaseAccess();
+#else
+#define TCDEBUGOPEN()
+#define TCDEBUGLOGS(s)
+#define TCDEBUGLOGA1(s, a1)
+#define TCDEBUGLOGA2(s, a1, a2)
+#define TCDEBUGLOGA3(s, a1, a2, a3)
+#define TCDEBUGCLOSE()
+#endif
+
+#ifdef _DEBUG
+static char* GetErrorText(DWORD inError);
+#endif
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+CClientManager::CClientManager()
+{
+	m_Server = NULL;
+	m_StreamList = NULL;
+	m_ErrorMonitorList = NULL;
+	m_DebugLog = NULL;
+	m_DllLocation = NULL;
+	m_hServer = NULL;
+	m_hServerThread = NULL;
+	m_Version[0] = NULL;
+	m_ServerLockFile = NULL;
+	m_ServerExeFile = NULL;
+}
+CClientManager::CClientManager(HINSTANCE hinstDLL)
+{
+#ifdef _DEBUG
+	if (gDoLogging)
+		m_DebugLog = new TCDebugLog("TCF_ClientLog", ::GetCurrentProcessId());
+	else
+		m_DebugLog = NULL;
+
+#else
+	m_DebugLog = NULL;
+#endif
+	TCDEBUGOPEN();
+	TCDEBUGLOGS("CClientManager::CClientManager\n");
+
+	m_Server = new CServerCommand();
+
+	// lock server access (it might be running)
+	m_Server->WaitforServerPipeAccess();
+
+	m_StreamList = new InputStreamList();
+	m_StreamList->clear();
+
+	m_ErrorMonitorList = new ErrorMonitorList();
+	m_ErrorMonitorList->clear();
+
+	m_DllLocation = new char[MAX_DLLPATHNAME];
+	::GetModuleFileName(hinstDLL, m_DllLocation, MAX_DLLPATHNAME);
+
+	char exeDirectory[MAX_DLLPATHNAME] = {0};
+	strncpy(exeDirectory, m_DllLocation, MAX_DLLPATHNAME);
+	size_t len = strlen(exeDirectory);
+	// remove file
+	for (int i = len-1; i > 0; i--)
+	{
+		if (exeDirectory[i] == PATH_DELIMITER)
+			break;
+	}
+	exeDirectory[i] = NULL;
+
+	m_ServerExeFile = new char[MAX_DLLPATHNAME];
+	sprintf(m_ServerExeFile, "\"%s%c%s\"", exeDirectory, PATH_DELIMITER, SERVER_PROCESS_NAME);
+
+	m_ServerLockFile = new char[MAX_DLLPATHNAME];
+	sprintf(m_ServerLockFile, "%s%c%s", exeDirectory, PATH_DELIMITER, SERVER_LOCKFILE_NAME);
+
+	char name[100];
+	sprintf(name, "%s%ld", ERRORMONITORLIST_MUTEX_BASENAME, ::GetCurrentProcessId());
+	m_ErrorMonitorListMutex.Open(name, ERRORMONITORLIST_MUTEX_TIMEOUT);
+
+	sprintf(name, "%s%ld", INPUTSTREAMLIST_MUTEX_BASENAME, ::GetCurrentProcessId());
+	m_StreamListMutex.Open(name, INPUTSTREAMLIST_MUTEX_TIMEOUT);
+
+	m_hServer = NULL;
+	m_hServerThread = NULL;
+
+	// release server access
+	m_Server->ReleaseServerPipeAccess();
+	int ret = ::LoadString(hinstDLL, IDS_VERSION, m_Version, MAX_VERSION_STRING);
+
+	TCDEBUGCLOSE();
+}
+
+CClientManager::~CClientManager()
+{
+	TCDEBUGOPEN();
+	TCDEBUGLOGS("CClientManager::~CClientManager\n");
+	pServerProcessData pData = m_Server->GetProcessPtr();
+
+	if (m_Server)
+	{
+		delete m_Server;
+		m_Server = NULL;
+	}
+
+	WaitForStreamListAccess();
+	TCDEBUGLOGA1("CClientManager::~CClientManager: erasing stream list size=%d\n", InputStreamListSize());
+	if (m_StreamList)
+	{
+		InputStreamList::iterator iter;
+		for (iter = m_StreamList->begin(); iter != m_StreamList->end(); iter++)
+		{
+			TCDEBUGLOGS("CClientManager::~CClientManager: erasing stream list next 1\n");
+//			m_StreamList->erase(iter);
+			delete *iter;
+//			TCDEBUGLOGS("CClientManager::~CClientManager: erasing stream list next 2\n");
+		}
+		m_StreamList->clear();
+		TCDEBUGLOGS("CClientManager::~CClientManager: erasing stream list done 1\n");
+		delete m_StreamList;
+		TCDEBUGLOGS("CClientManager::~CClientManager: erasing stream list done 2\n");
+	}
+	ReleaseStreamListAccess();
+	m_StreamListMutex.Close();
+
+	WaitForErrorMonitorListAccess();
+	TCDEBUGLOGA1("CClientManager::~CClientManager: erasing monitor list size=%d\n", ErrorMonitorListSize());
+	if (m_ErrorMonitorList)
+	{
+		ErrorMonitorList::iterator iter;
+		for (iter = m_ErrorMonitorList->begin(); iter != m_ErrorMonitorList->end(); iter++)
+		{
+			TCDEBUGLOGS("CClientManager::~CClientManager: erasing monitor list next 1\n");
+//			m_ErrorMonitorList->erase(iter);
+			delete *iter;
+//			TCDEBUGLOGS("CClientManager::~CClientManager: erasing monitor list next 1\n");
+		}
+		m_ErrorMonitorList->clear();
+		TCDEBUGLOGS("CClientManager::~CClientManager: erasing monitor list done 1\n");
+		delete m_ErrorMonitorList;
+		TCDEBUGLOGS("CClientManager::~CClientManager: erasing monitor list done 2\n");
+	}
+	ReleaseErrorMonitorListAccess();
+	m_ErrorMonitorListMutex.Close();
+
+	if (m_DllLocation)
+	{
+		delete[] m_DllLocation;
+		m_DllLocation = NULL;
+	}
+
+	if (m_ServerLockFile)
+	{
+		delete[] m_ServerLockFile;
+		m_ServerLockFile = NULL;
+	}
+
+	if (m_ServerExeFile)
+	{
+		delete[] m_ServerExeFile;
+		m_ServerExeFile = NULL;
+	}
+	TCDEBUGLOGS("CClientManager::~CClientManager: closing log\n");
+	TCDEBUGCLOSE();
+	if (m_DebugLog)
+	{
+		delete m_DebugLog;
+		m_DebugLog = NULL;
+	}
+}
+CErrorMonitor*
+CClientManager::FindErrorMonitor(long inClientId)
+{
+	CErrorMonitor* errorMonitor = NULL;
+	ErrorMonitorList::iterator iter;
+
+	for (iter = m_ErrorMonitorList->begin(); iter != m_ErrorMonitorList->end(); iter++)
+	{
+		if ((*iter)->IsThisClient(inClientId))
+		{
+			errorMonitor = *iter;
+			break;
+		}
+	}
+	return errorMonitor;
+}
+long CClientManager::ErrorMonitorListSize()
+{
+	long size = m_ErrorMonitorList->size();
+
+	return size;
+}
+void CClientManager::AddErrorMonitor(CErrorMonitor* monitor)
+{
+	m_ErrorMonitorList->push_back(monitor);
+}
+void CClientManager::RemoveErrorMonitor(CErrorMonitor* monitor)
+{
+	ErrorMonitorList::iterator iter;
+
+	for (iter = m_ErrorMonitorList->begin(); iter != m_ErrorMonitorList->end(); iter++)
+	{
+		if ((*iter)->IsThisClient(monitor->GetClientId()))
+		{
+			m_ErrorMonitorList->erase(iter);
+			break;
+		}
+	}
+}
+
+CInputStream*
+CClientManager::FindInputStream(long inClientId)
+{
+	CInputStream* inputStream = NULL;
+	InputStreamList::iterator iter;
+
+	for (iter = m_StreamList->begin(); iter != m_StreamList->end(); iter++)
+	{
+		if ((*iter)->IsThisClient(inClientId))
+//		if ((*iter).IsThisClient(inClientId))
+		{
+			inputStream = *iter;
+//			inputStream = iter;
+			break;
+		}
+	}
+	return inputStream;
+}
+long CClientManager::InputStreamListSize()
+{
+	long size = m_StreamList->size();
+
+	return size;
+}
+void CClientManager::AddInputStream(CInputStream* stream)
+{
+	m_StreamList->push_back(stream);
+//	m_StreamList->push_back(*stream);
+
+}
+void CClientManager::RemoveInputStream(CInputStream* stream)
+{
+	InputStreamList::iterator iter;
+
+	for (iter = m_StreamList->begin(); iter != m_StreamList->end(); iter++)
+	{
+		if ((*iter)->IsThisClient(stream->GetClientId()))
+//		if ((*iter).IsThisClient(stream->GetClientId()))
+		{
+			m_StreamList->erase(iter);
+			break;
+		}
+	}
+}
+
+BOOL CClientManager::StartServer(pServerProcessData pData)
+{
+	TCDEBUGLOGA1("CClientManager::StartServer numRefs = %d\n",pData->numRefs);
+
+	BOOL serverStarted = FALSE;
+	// server is ref counted
+	// refcount = 0 => server is not running
+	// refcount > 0 => server already started by some other process
+	if (pData->numRefs == 0)
+	{
+		// server not running
+		// get exe location
+		char exeLocation[MAX_DLLPATHNAME] = {0};
+		strncpy(exeLocation, m_DllLocation, MAX_DLLPATHNAME);
+		size_t len = strlen(exeLocation);
+		// remove file
+		for (int i = len-1; i > 0; i--)
+		{
+			if (exeLocation[i] == PATH_DELIMITER)
+				break;
+		}
+		exeLocation[i] = NULL;
+		char quotedLocation[MAX_DLLPATHNAME] = {0};
+		sprintf(quotedLocation, "\"%s%c%s\"", exeLocation, PATH_DELIMITER, SERVER_PROCESS_NAME);
+
+		TCDEBUGLOGA1("  exeLocation=%s\n", quotedLocation);
+
+		// create process
+		STARTUPINFO si;
+		memset(&si,0,sizeof(si));
+		si.cb = sizeof(si);
+		memset(&pData->serverProcess, 0, sizeof(pData->serverProcess));
+		pData->serverProcess.hProcess = NULL;
+		if (!::CreateProcess(
+			NULL,			// module location
+			quotedLocation,	// command line
+			NULL,			// process attributes
+			NULL,			// thread attributes
+			FALSE,			// inherit our handles
+			CREATE_NO_WINDOW,	// no window
+			NULL,			// use our environment
+			NULL,			// user our current directory
+			&si,			// startup info
+			&pData->serverProcess)) // process info
+		{
+			// TODO: error creating process
+		}
+		else
+		{
+			// we are the creator so save handles for later
+			m_hServer = pData->serverProcess.hProcess;
+			m_hServerThread = pData->serverProcess.hThread;
+			// add a refcount
+			pData->numRefs++;
+			serverStarted = TRUE;
+		}
+	}
+	else
+	{
+		// already running
+		// add a refcount and open our process handle to it
+		pData->numRefs++;
+		m_hServer = ::OpenProcess(0, FALSE, pData->serverProcess.dwProcessId);
+		serverStarted = TRUE;
+	}
+	TCDEBUGLOGA1("CClientManager::StartServer serverStarted=%d\n", serverStarted);
+	return serverStarted;
+}
+
+BOOL CClientManager::StopServer(pServerProcessData pData)
+{
+	TCDEBUGLOGS("CClientManager::StopServer\n");
+
+	BOOL serverStopped = FALSE;
+
+	if (pData->serverProcess.hProcess == NULL || pData->numRefs <= 0)
+	{
+		serverStopped = TRUE;
+	}
+	else
+	{
+		TCDEBUGLOGA1(" numRefs = %d\n",pData->numRefs); 
+
+		// substract ref count
+		pData->numRefs--;
+		// if refcount == 0 then really stop the server process
+		if (pData->numRefs <= 0)
+		{
+			// last client process is closing
+			// tell server to exit
+			ServerCommandData cmdrsp;
+			cmdrsp.command = eCmdExit;
+		TCDEBUGLOGS(" SendCommand eCmdExit\n");
+			m_Server->SendCommand(&cmdrsp);
+		TCDEBUGLOGS(" GetResponse eExit\n");
+			m_Server->GetResponse(&cmdrsp);
+			// wait for process to exit
+		TCDEBUGLOGS(" WaitForSingleObject start\n");
+			WaitForSingleObject(m_hServer, 10000L /*INFINITE*/);
+		TCDEBUGLOGS(" WaitForSingleObject found\n");
+
+			if (m_hServer != NULL)
+				CloseHandle(m_hServer);
+
+			if (m_hServerThread != NULL)
+				CloseHandle(m_hServerThread);
+		}
+		else
+		{
+			// just close our handle to server process
+			if (m_hServer != NULL)
+				CloseHandle(m_hServer);
+
+			if (m_hServerThread != NULL)
+				CloseHandle(m_hServerThread);
+		}
+	}
+
+	TCDEBUGLOGS("CClientManager::StopServer end\n");
+	return TRUE;
+}
+long CClientManager::StartServer()
+{
+	long ret = TCAPI_ERR_NONE;
+	pServerProcessData pData = m_Server->GetProcessPtr();
+
+	TCDEBUGLOGA3("CClientManager::StartServer this = %x m_hServer = %x numRefs = %d\n", this, m_hServer, pData->numRefs);
+//	TCDEBUGLOGA1("  mgrRefs = %d\n", m_MgrServerRef);
+
+	BOOL serverStarted = FALSE;
+	// server is ref counted
+	// refcount = 0 => server is not running
+	// refcount > 0 => server already started by some other process
+
+	// terminate the TCFServer if it is already running
+	TerminateServerThroughLockFile(pData);
+
+	if (pData->numRefs == 0)
+	{
+		// server not running
+		TCDEBUGLOGA1("  TCFServer exe =%s\n", m_ServerExeFile);
+		TCDEBUGLOGA1("  TCFServer lock=%s\n", m_ServerLockFile);
+
+
+		// create process
+		STARTUPINFO si;
+		memset(&si,0,sizeof(si));
+		si.cb = sizeof(si);
+		memset(&pData->serverProcess, 0, sizeof(pData->serverProcess));
+		pData->serverProcess.hProcess = NULL;
+		if (!::CreateProcess(
+			NULL,			// module location
+			m_ServerExeFile,	// command line
+			NULL,			// process attributes
+			NULL,			// thread attributes
+			FALSE,			// inherit our handles
+			CREATE_NO_WINDOW,	// no window
+			NULL,			// use our environment
+			NULL,			// user our current directory
+			&si,			// startup info
+			&pData->serverProcess)) // process info
+		{
+			// TODO: error creating process
+		}
+		else
+		{
+			// we are the creator so save handles for later
+			m_hServer = pData->serverProcess.hProcess;
+			m_hServerThread = pData->serverProcess.hThread;
+			// add a refcount
+			pData->numRefs++;
+			serverStarted = TRUE;
+			TCDEBUGLOGA3("CClientManager::StartServer created m_hServer = %x processId = %d numRefs = %d\n", m_hServer, pData->serverProcess.dwProcessId, pData->numRefs);
+
+			// create lock file and save process ID
+			TCDEBUGLOGS("CClientManager::StartServer CreateLockFile\n");
+			CreateLockFile(pData->serverProcess.dwProcessId);
+		}
+	}
+	else
+	{
+		// already running
+		// add a refcount and open our process handle to it only if we haven't opened it already
+		pData->numRefs++;
+		if (m_hServer == NULL)
+			m_hServer = ::OpenProcess(SYNCHRONIZE|PROCESS_TERMINATE, FALSE, pData->serverProcess.dwProcessId);
+		if (m_hServer == 0)
+		{
+			TCDEBUGLOGA1("CClientManager::StartServer opened m_hServer null error=%d\n", ::GetLastError());
+		}
+		m_hServerThread = NULL;		// only creator gets real thread handle
+		serverStarted = TRUE;
+		TCDEBUGLOGA3("CClientManager::StartServer opened m_hServer = %x processId = %d numRefs = %d\n", m_hServer, pData->serverProcess.dwProcessId, pData->numRefs);
+		// save our process id to lock file
+		AppendToLockFile(pData->serverProcess.dwProcessId);
+	}
+	if (serverStarted)
+		m_ServerRunning = TRUE;
+
+	TCDEBUGLOGA1("CClientManager::StartServer end numRefs = %d\n", pData->numRefs);
+	return ret;
+}
+
+long CClientManager::StopServer()
+{
+	long ret = TCAPI_ERR_NONE;
+	pServerProcessData pData = m_Server->GetProcessPtr();
+
+	TCDEBUGLOGA3("CClientManager::StopServer this = %x m_hServer = %x numRefs = %d\n", this, m_hServer, pData->numRefs);
+
+	BOOL serverStopped = FALSE;
+
+	if (pData->serverProcess.hProcess == NULL || pData->numRefs <= 0)
+	{
+		TCDEBUGLOGS("CClientManager::StopServer hProcess NULL or numRefs <= 0\n");
+		serverStopped = TRUE;
+	}
+#if (0)
+	else if (m_hServer == NULL)
+	{
+		// we've already closed our handle to server
+		// don't close it again
+		TCDEBUGLOGS("CClientManager::StopServer m_hServer null\n");
+	}
+#endif
+	else
+	{
+		// substract ref count
+		pData->numRefs--;
+		if (pData->numRefs < 0) pData->numRefs = 0;
+		// if refcount == 0 then really stop the server process
+		if (pData->numRefs == 0)
+		{
+			// last client process is closing
+			// tell server to exit
+			ServerCommandData cmdrsp;
+			cmdrsp.command = eCmdExit;
+			
+			TCDEBUGLOGS(" SendCommand eCmdExit\n");
+			m_Server->SendCommand(&cmdrsp);
+			TCDEBUGLOGS(" GetResponse eExit\n");
+			m_Server->GetResponse(&cmdrsp);
+			
+			// wait for process to exit
+			TCDEBUGLOGS(" WaitForSingleObject start\n");
+			DWORD waitErr = ::WaitForSingleObject(m_hServer, 10000L /*INFINITE*/);
+			TCDEBUGLOGA1("CClientManager::StopServer WaitForSingleObject = %d\n", waitErr);
+
+			// now close our handle to server process
+			if (m_hServer != NULL)
+			{
+				CloseHandle(m_hServer);
+				m_hServer = NULL;
+			}
+
+			if (m_hServerThread != NULL)
+			{
+				CloseHandle(m_hServerThread);
+				m_hServerThread = NULL;
+			}
+			serverStopped = TRUE;
+
+			// delete lock file
+			TCDEBUGLOGS("CClientManager::StopServer DeleteLockFile\n");
+			DeleteLockFile();
+		}
+		else
+		{
+			// just close our handle to server process
+
+			if (m_hServer != NULL)
+			{
+				CloseHandle(m_hServer);
+				m_hServer = NULL;
+			}
+
+			if (m_hServerThread != NULL)
+			{
+				CloseHandle(m_hServerThread);
+				m_hServerThread = NULL;
+			}
+			DeleteFromLockFile(pData->serverProcess.dwProcessId);
+		}
+	}
+	if (serverStopped)
+		m_ServerRunning = FALSE;
+
+	TCDEBUGLOGA1("CClientManager::StopServer end numRefs = %d\n", pData->numRefs);
+	return ret;
+}
+
+BOOL CClientManager::IsServerRunning()
+{
+	pServerProcessData pData = m_Server->GetProcessPtr();
+	if (pData->serverProcess.hProcess != NULL)
+		return TRUE;
+	else
+		return FALSE;
+
+}
+
+void CClientManager::CreateLockFile(DWORD processId)
+{
+	if (m_ServerLockFile != NULL)
+	{
+		FILE* f = fopen(m_ServerLockFile, "wt");
+		TCDEBUGLOGA1("CClientManager::CreateLockFile f=%x\n", f);
+
+		if (f)
+		{
+			DWORD callingProcessId = ::GetCurrentProcessId();
+			TCDEBUGLOGA2("CClientManager::CreateLockFile callingProcessId=%d processId=%d\n", callingProcessId, processId);
+			fprintf(f, "%ld %ld\n", callingProcessId, processId);
+			fclose(f);
+		}
+		else
+		{
+			DWORD err = ::GetLastError();
+			TCDEBUGLOGA2("CClientManager::CreateLockFile fopenErr=%d:%s\n", err, GetErrorText(err));
+		}
+	}
+}
+void CClientManager::AppendToLockFile(DWORD processId)
+{
+	if (m_ServerLockFile != NULL)
+	{
+		FILE* f = fopen(m_ServerLockFile, "at");
+		TCDEBUGLOGA1("CClientManager::AppendToLockFile f=%x\n", f);
+
+		if (f)
+		{
+			DWORD callingProcessId = ::GetCurrentProcessId();
+			TCDEBUGLOGA2("CClientManager::AppendToLockFile callingProcessId=%d processId=%d\n", callingProcessId, processId);
+			fprintf(f, "%ld %ld\n", callingProcessId, processId);
+			fclose(f);
+		}
+		else
+		{
+			DWORD err = ::GetLastError();
+			TCDEBUGLOGA2("CClientManager::AppendToLockFile fopenErr=%d:%s\n", err, GetErrorText(err));
+		}
+	}
+}
+void CClientManager::DeleteLockFile()
+{
+	if (m_ServerLockFile != NULL)
+	{
+		TCDEBUGLOGS("CClientManager::DeleteLockFile\n");
+		::remove(m_ServerLockFile);
+	}
+}
+
+void CClientManager::DeleteFromLockFile(DWORD serverProcessId)
+{
+	DWORD callingId[10];
+	DWORD serverId[10];
+	int numIds = 0;
+
+	DWORD ourProcessId = ::GetCurrentProcessId();
+
+	if (m_ServerLockFile != NULL)
+	{
+		DWORD attr = ::GetFileAttributes(m_ServerLockFile);
+		TCDEBUGLOGA1("CClientManager::DeleteFromLockFile attr=%x\n", attr);
+
+		if (attr != 0xffffffff) // error
+		{
+			// file exists
+			// read the process Ids from it 
+
+			FILE *f = fopen(m_ServerLockFile, "rt");
+			TCDEBUGLOGA1("CClientManager::DeleteFromLockFile f=%x\n", f);
+			if (f)
+			{
+				BOOL done = FALSE;
+				while (!done)
+				{
+					DWORD cId = 0xffffffff;
+					DWORD sId = 0xffffffff;
+					int n = fscanf(f, "%ld %ld\n", &cId, &sId);
+					if (n == 2)
+					{
+						TCDEBUGLOGA3("CClientManager::DeleteFromLockFile numIds=%d sId=%d pId=%d\n", numIds, cId, sId);
+						if (cId != ourProcessId || sId != serverProcessId)
+						{
+							callingId[numIds] = cId;
+							serverId[numIds] = sId;
+							numIds++;
+							if (numIds > 9)
+								done = TRUE;
+						}
+					}
+					else
+					{
+						done = TRUE;
+					}
+				}
+				fclose(f);
+			}
+
+			// now rewrite lock file without us
+			::remove(m_ServerLockFile);
+			if (numIds > 0)
+			{
+				f = fopen(m_ServerLockFile, "wt");
+				if (f)
+				{
+					for (int i = 0; i < numIds; i++)
+					{
+						fprintf(f, "%ld %ld\n", callingId[i], serverId[i]);
+					}
+					fclose(f);
+				}
+			}
+		}
+	}
+}
+
+// Currently assumes there is only ONE TCFServer, but multiple client processes (that use that server)
+// we should not have more than a few Carbide processes connecting to the same TCFServer
+void CClientManager::TerminateServerThroughLockFile(pServerProcessData pData)
+{
+	DWORD callingId[10];
+	DWORD serverId[10];
+	BOOL liveCaller[10];
+	int numIds = 0;
+	if (m_ServerLockFile != NULL)
+	{
+		DWORD attr = ::GetFileAttributes(m_ServerLockFile);
+		TCDEBUGLOGA1("CClientManager::TerminateServerThroughLockFile attr=%x\n", attr);
+
+		if (attr != 0xffffffff) // error
+		{
+			// file exists
+			// read the process Ids from it 
+
+			FILE *f = fopen(m_ServerLockFile, "rt");
+			TCDEBUGLOGA1("CClientManager::TerminateServerThroughLockFile f=%x\n", f);
+			if (f)
+			{
+				BOOL done = FALSE;
+				while (!done)
+				{
+					DWORD cId = 0xffffffff;
+					DWORD sId = 0xffffffff;
+					int n = fscanf(f, "%ld %ld\n", &cId, &sId);
+					if (n == 2)
+					{
+						TCDEBUGLOGA3("CClientManager::TerminateServerThroughLockFile n=%d sId=%d pId=%d\n", n, cId, sId);
+						callingId[numIds] = cId;
+						serverId[numIds] = sId;
+						numIds++;
+						if (numIds > 9)
+							done = TRUE;
+					}
+					else
+					{
+						done = TRUE;
+					}
+				}
+				fclose(f);
+
+				int numDeadCallers = 0;
+				for (int i = 0; i < numIds; i++)
+				{
+					HANDLE h = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, callingId[i]);
+					if (h)
+					{
+						// calling process is still alive
+						liveCaller[i] = TRUE;
+						::CloseHandle(h);
+						TCDEBUGLOGA1("CClientManager::TerminateServerThroughLockFile %d alive\n", callingId[i]);
+					}
+					else
+					{
+						liveCaller[i] = FALSE;
+						numDeadCallers++;
+						DWORD err = ::GetLastError();
+						TCDEBUGLOGA3("CClientManager::TerminateServerThroughLockFile %d dead err=%d:%s\n", callingId[i], err, GetErrorText(err));
+					}
+				}
+				if (numDeadCallers == numIds)
+				{
+					// terminate the TCFServer, and delete lock file
+					pData->numRefs = 0;
+					::remove(m_ServerLockFile);
+					HANDLE h = ::OpenProcess(SYNCHRONIZE|PROCESS_TERMINATE, FALSE, serverId[0]);
+					if (h)
+					{
+						BOOL ret = ::TerminateProcess(h, -1);
+						if (ret == 0)
+						{
+							DWORD err = ::GetLastError();
+							TCDEBUGLOGA2("CClientManager::TerminateServerThroughLockFile TerminateProcess=%d:%s\n", err, GetErrorText(err));
+						}
+						::CloseHandle(h);
+					}
+				}
+				else
+				{
+					// leave TCFServer running, recreate lock file and save live callers
+					::remove(m_ServerLockFile);
+					f = fopen(m_ServerLockFile, "wt");
+					if (f)
+					{
+						for (int i = 0; i < numIds; i++)
+						{
+							if (liveCaller[i])
+							{
+								fprintf(f, "%ld %ld\n", callingId[i], serverId[i]);
+							}
+						}
+						fclose(f);
+					}
+					pData->numRefs -= numDeadCallers;
+					if (pData->numRefs < 0) pData->numRefs = 0;
+				}
+			}
+			else
+			{
+				// error opening lock file
+				DWORD err = ::GetLastError();
+				TCDEBUGLOGA2("CClientManager::TerminateServerThroughLockFile fopenErr=%d:%s\n", err, GetErrorText(err));
+			}
+		}
+	}
+}
+#ifdef _DEBUG
+static char* GetErrorText(DWORD inError)
+{
+	static char msg[256];
+	FormatMessage(
+		FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+		NULL,
+		inError,
+		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
+		(LPTSTR) &msg,
+		sizeof(msg) - 1,
+		NULL);
+
+	return msg;
+}
+#else
+static char* GetErrorText(DWORD inError)
+{
+	return NULL;
+}
+#endif