/*****************************************************************************
* Copyright (c) 1990-98 Digi International Inc., All Rights Reserved
* 
* This software contains proprietary and confidential information of Digi
* International Inc.  By accepting transfer of this copy, Recipient agrees
* to retain this software in confidence, to prevent disclosure to others,
* and to make no use of this software other than that for which it was
* delivered.  This is an unpublished copyrighted work of Digi International
* Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly
* prohibited.
* 
* Restricted Rights Legend
*
* Use, duplication, or disclosure by the Government is subject to
* restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in
* Technical Data and Computer Software clause at DFARS 252.227-7031 or
* subparagraphs (c)(1) and (2) of the Commercial Computer Software -
* Restricted Rights at 48 CFR 52.227-19, as applicable.
*
* Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
*
*****************************************************************************/
// common.cpp : 
// initialization and support routines that create services and write/read 
// from the registry
// This file is modified from the original supplied in the sample SDK.
// All the main logic is in this file.

#include "stdafx.h"
#include "common.h"
#include "commonres.h"
#include "registry.h"
#include "lineselect.h"
#include "dxbreg.h"
#include <winreg.h>
#include <winsvc.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif





//
// Global Variables
//
MGSDLCDS	gMgsdlc_data;

/*
 *  Default CardCfg array.  This is overwritten with currently configured card types.
 */
//
// Global Registry Handle.  The remote registry is connected to one time using this 
// handle.
//
static HKEY	hGlobalKey;

// The following are globals specific to the currently installed OS.
DWORD gdwOSMajorVersion;
CString strAdapterRegPath;

/*
**	Purpose:
**		Find a string at the beginning of another string pointer.
**
**	Arguments:
**		lpszStr1		: String pointer containing the source of the string search.
**		lpszStr2		: String to search for.
**
**	Returns:
**		TRUE if the string was found, otherwise returns FALSE.
**		
***************************************************************************************/
BOOL fStringCompare(LPSTR lpszStr1, LPSTR lpszStr2)
{
	BOOL	bStringFound = TRUE;
	DWORD	dIndex;

	for (dIndex = 0; dIndex < strlen(lpszStr1); dIndex++)
	{
		if ((lpszStr1[dIndex] != lpszStr2[dIndex]) ||
		    (dIndex > strlen(lpszStr2) - 1))
		{
			bStringFound = FALSE;
			break;
		}
	}
	return bStringFound;
}


/*
**	Purpose:
**		Find and replace a string within a string.
**
**	Arguments:
**		szStringBuffer		: String pointer containing the source of the string search.
**		szSearchString		: String to search for.
**		szReplaceString		: String to replace szSearchString with.
**
**	Returns:
**		TRUE if the string was found and replaced, otherwise returns FALSE.
**		
***************************************************************************************/
BOOL fFindAndReplaceString(LPSTR szStringBuffer, 
						   LPSTR szSearchString, 
						   LPSTR szReplaceString)
{
	DWORD	dBufferIndex;
	BOOL	bReturnValue = FALSE;

	char	szTempBuffer[MAX_STRING];
	LPSTR	lpszTempPtr;
	LPSTR	lpszStringPtr;

	lpszTempPtr = szTempBuffer;
	lpszStringPtr = szStringBuffer;

	for (dBufferIndex = 0; dBufferIndex <= strlen(szStringBuffer); dBufferIndex++, lpszTempPtr++, lpszStringPtr++)
	{
		if (!fStringCompare(szSearchString, lpszStringPtr))
		{
			szTempBuffer[dBufferIndex] = (char) *lpszStringPtr;
		}
		else
		{
			strcpy(lpszTempPtr, szReplaceString);

			lpszStringPtr += strlen(szSearchString);

			lpszTempPtr += strlen(szReplaceString);

			strcpy(lpszTempPtr, lpszStringPtr);

			strcpy(szStringBuffer, szTempBuffer);

			bReturnValue = TRUE;

			break;
		}
	}

	return bReturnValue;
}


/*=========================================================================*
 * Connect to Registry - this function connects to the remote computer's 
 *						 registry and sets up the global handle.
 *
 *                   szComputerName  - Name of the remote computer
 *
 *=========================================================================*/
BOOL fConnectRegistry(char * szComputerName)
{
	if (ERROR_SUCCESS != RegConnectRegistry((LPSTR) szComputerName, HKEY_LOCAL_MACHINE, &hGlobalKey))
	{
		return FALSE;
	}
	return TRUE;
}


/*=========================================================================*
 * Disconnect from Registry - this function disconnects from the remote 
 *                            computer's registry.
 *
 *=========================================================================*/
void fDisconnectRegistry()
{
	RegFlushKey(hGlobalKey);
	RegCloseKey(hGlobalKey);
}



/*=========================================================================*
 * Add to Registry - this function adds a single entry to the registry from
 *                   values passed in the following parameters:
 *
 *					 szComputerName - name of the remote computer
 *                   RegistryKey    - full path of the registry key
 *                   RegistryName   - name of the registry entry
 *                   RegistryData   - Data to be placed in the registry
 *					 dType		    - Type of registry entry to add
 *					 dSize		    - Contains the size of the new entry
 *
 *=========================================================================*/
BOOL fAddRegistryEntry (char* szRegistryKey, char* szRegistryName, 
						char* szRegistryData, DWORD dType, ULONG_PTR dSize )
{
	HKEY	hKey;
	DWORD	iDisposition;

	if (ERROR_SUCCESS != RegCreateKeyEx(hGlobalKey,
										szRegistryKey,
										0,
										NULL,
										REG_OPTION_NON_VOLATILE,
										KEY_WRITE,
										NULL,
										&hKey,
										(LPDWORD) &iDisposition))
	{
		return FALSE;
	}

	if (ERROR_SUCCESS != RegOpenKeyEx(hGlobalKey,
									  szRegistryKey,
									  0,
									  KEY_WRITE,
									  &hKey))
	{
		return FALSE;
	}

	if (strlen(szRegistryName) != 0)
	{
		RegSetValueEx(hKey,
					  szRegistryName,
					  0,
					  dType,
					  (unsigned char*) szRegistryData,
					  (ULONG)dSize);
	}
	RegCloseKey(hKey);

	return (TRUE);
}


/*
**	Purpose:
**		Queries a value in the registry.
**
**	Arguments:
**		szComputerName		: String pointer containing the name of the computer.
**		szRegistryKey		: Registry Key to delete.
**
**	Returns:
**		TRUE if key was successfully deleted, otherwise returns FALSE.
**		
***************************************************************************************/
BOOL fRemoveRegistryEntry (char* szRegistryKey)
{
	LONG	lStatus;

	lStatus = RegDeleteKey(hGlobalKey, szRegistryKey);

	return TRUE;
}


/*
**	Purpose:
**		Deletes a value in the registry.
**
**	Arguments:
**		szRegistryKey		: String pointer containing the name of the computer.
**		szRegistryValue		: Registry Key to delete.
**
**	Returns:
**		TRUE if key was successfully deleted, otherwise returns FALSE.
**		
***************************************************************************************/
BOOL fRemoveRegistryValue (char* szRegistryKey, char* szRegistryValue)
{
	HKEY	hKey;

	if (ERROR_SUCCESS != RegOpenKeyEx(hGlobalKey,
					  szRegistryKey,
					  0,
					  KEY_WRITE,
					  &hKey))
	{
		return FALSE;
	}
	RegDeleteValue(hKey, szRegistryValue);

	return TRUE;
}


/*
**	Purpose:
**		Determines if a registry key exists.
**
**	Arguments:
**		szComputerName		: String pointer containing the name of the computer.
**		szRegistryKey		: Registry Key to open.
**
**	Returns:
**		TRUE if the key exises, otherwise returns FALSE.
**		
***************************************************************************************/
BOOL fRegistryKeyExists(LPSTR szRegistryKey)
{
	HKEY	hKey = NULL;
	BOOL	bReturnCode = FALSE;

	if (ERROR_SUCCESS == RegOpenKeyEx(hGlobalKey,
									  szRegistryKey,
									  0,
									  KEY_READ,
									  &hKey))
	{
		bReturnCode = TRUE;
	}

	RegCloseKey(hKey);
			
	return bReturnCode;
}


/*
**	Purpose:
**		Queries a value in the registry.
**
**	Arguments:
**		szComputerName		: String pointer containing the name of the computer.
**		szRegistryKey		: Registry Key to open.
**		szRegistryValue		: Registry Value to query.
**		szRegistryData		: String Buffer to contain the registry data.
**		dSize				: DWORD variable to contain the size of the returned data.
**
**	Returns:
**		TRUE if we can administer the remote box, otherwise returns FALSE.
**		
***************************************************************************************/
BOOL fQueryRegistryValue(LPSTR szRegistryKey, LPSTR szRegistryValue, 
						 LPSTR szRegistryData, LPDWORD dSize)
{
	HKEY	hKey = NULL;
	BOOL	bReturnCode = FALSE;
	DWORD	dType;

	if (ERROR_SUCCESS == RegOpenKeyEx(hGlobalKey,
									  szRegistryKey,
									  0,
									  KEY_READ,
									  &hKey))
	{
		if (ERROR_SUCCESS == RegQueryValueEx(hKey, 
										     szRegistryValue,
										     0,
											 (LPDWORD) &dType,
											 (unsigned char*) szRegistryData,
											 dSize))

			bReturnCode = TRUE;
	}

	RegCloseKey(hKey);
			
	return bReturnCode;
}


/*
**	Purpose:
**		Determines if we have Administrative Privilages on the remote computer
**
**	Arguments:
**		sComputerName		: String pointer containing the name of the computer.
**
**	Returns:
**		TRUE if we can administer the remote box, otherwise returns FALSE.
**		
***************************************************************************/
BOOL fCanWeAdministerRemoteBox()
{
#define SERVICES_REGISTRY_KEY	"SYSTEM\\CurrentControlSet\\Services"

	HKEY	hKey = NULL;
	BOOL	bReturnCode = FALSE;

	if (ERROR_SUCCESS == RegOpenKeyEx(hGlobalKey,
									  SERVICES_REGISTRY_KEY,
									  0,
									  KEY_ALL_ACCESS,
									  &hKey))
	{
		bReturnCode = TRUE;
	}

	RegCloseKey(hKey);

	return bReturnCode;
}


/*
**	Purpose:
**		Starting or stopping a service.
**
**	Arguments:
**		ServiceName : String pointer containing the name of the service.
**		sAction : START or STOP
**
**	Returns:
**		TRUE if action was successful, FALSE if the action was not successful.
**		
***************************************************************************/
BOOL bStopService(LPSTR szServiceName, LPSTR szComputer)
{
	SC_HANDLE hSCManager;
	SC_HANDLE hService;
	SC_LOCK   lSCLock;
	BOOL      fStatus = TRUE;
	int       iStopTries = 0;
	int		  iRetryCount = 0;
	DWORD	  dLastError;
	char	  szBuffer[MAX_STRING] = "";
	char *pszStates[] =
	{
		"SERVICE_CONFUSED",
		"SERVICE_STOPPED",
		"SERVICE_START_PENDING",
		"SERVICE_STOP_PENDING",
		"SERVICE_RUNNING",
		"SERVICE_CONTINUE_PENDING",
		"SERVICE_PAUSE_PENDING",
		"SERVICE_PAUSED"
	};

	SERVICE_STATUS ServiceStatus;

	hSCManager = NULL;
	hService   = NULL;
	lSCLock    = NULL;

	/*
	* Open the Service Control Manager
	*/
	hSCManager = OpenSCManager( szComputer, NULL, SC_MANAGER_ALL_ACCESS );
	if( hSCManager == NULL )
	{
		fStatus = FALSE;
		goto QueryCleanup;
	}

	/*
	 * Open the service
	 */
	hService = OpenService( hSCManager, szServiceName, GENERIC_ALL );
	if( hService == NULL )
	{
		goto QueryCleanup;
	}

	/*
	 * Control the service!
	 */
	if( ControlService(hService, SERVICE_CONTROL_STOP, &ServiceStatus) == FALSE )
	{
		dLastError = GetLastError();

		if (ERROR_SERVICE_NOT_ACTIVE != dLastError)
		{
			fStatus = FALSE;
			goto QueryCleanup;
		}
	}

	if(QueryServiceStatus( hService, &ServiceStatus ) == TRUE)
	{
		while (strcmp("SERVICE_STOPPED\0", pszStates[ ServiceStatus.dwCurrentState ]) &&
			   (iRetryCount < 5))
		{
			Sleep(1500);
			QueryServiceStatus( hService, &ServiceStatus );
			iRetryCount++;
		}
		if (iRetryCount == 5)
		{
			CHAR		temp_string[32],temp_string2[128];
			HINSTANCE	hInst;

			hInst = AfxGetResourceHandle();
			LoadString(hInst,IDS_UNABLE_STOP_SERVICE_T,temp_string,sizeof(temp_string));
			LoadString(hInst,IDS_UNABLE_STOP_SERVICE_M,temp_string2,sizeof(temp_string2));
			wsprintf(szBuffer, temp_string2, szServiceName);

			::MessageBox(GetActiveWindow(),
						 szBuffer,
						 temp_string,
						 MB_OK);
		}
	}

QueryCleanup:

	if( hService != NULL )   CloseServiceHandle( hService );
	if( lSCLock != NULL )    UnlockServiceDatabase( lSCLock );
	if( hSCManager != NULL ) CloseServiceHandle( hSCManager );

	return fStatus;
}


/*
**	Purpose:
**		Creating a Service.
**
**	Arguments:
**		sComputerName		: String pointer containing the name of the computer.
**		sServiceName 		: String pointer containing the name of the service.
**		sServicePath 		: Path to the service binary.
**		sServiceDependencies 	: Pointer to a list of null terminated strings + null
**		dServiceLoadType 	: Load type for the service (System Start, Auto Start ...)
**
**	Returns:
**		TRUE if service is created successfully, FALSE if the service could not be created.
**		
***************************************************************************/
BOOL bCreateService(LPSTR szComputerName, LPSTR szServiceName, LPSTR szServicePath, 
					LPSTR szServiceDependencies, DWORD dServiceType, DWORD dServiceLoadType)
{
	SC_HANDLE hSCManager;
	SC_HANDLE hNewService = NULL;
	SC_HANDLE hService;
	SC_LOCK   lSCLock;
	BOOL      fStatus = TRUE;
	DWORD	  dReturnCode = 0;

	hSCManager = NULL;
	hService   = NULL;
	lSCLock    = NULL;

	/*
	 * Open the Service Control Manager
	 */
	hSCManager = OpenSCManager( szComputerName, NULL, SC_MANAGER_ALL_ACCESS );
	if( hSCManager == NULL )
	{
		fStatus = FALSE;
		goto QueryCleanup;
	}

	/*
	 * Lock the active database for our exclusive use
	 */
	lSCLock = LockServiceDatabase( hSCManager );
	if( lSCLock == NULL )
	{
		fStatus = FALSE;
		goto QueryCleanup;
	}

	hNewService = CreateService(hSCManager,	
				    			szServiceName,
				    			szServiceName,
				    			SERVICE_ALL_ACCESS,
				    			dServiceType,
				    			dServiceLoadType,
				    			SERVICE_ERROR_NORMAL,
				    			szServicePath,
				    			NULL,
				    			NULL,
				    			szServiceDependencies,
								NULL,
								NULL);

	if (NULL == hNewService)
	{
		dReturnCode = GetLastError();
	}

QueryCleanup:

	if( lSCLock != NULL )    UnlockServiceDatabase( lSCLock );
	if( hSCManager != NULL ) CloseServiceHandle( hSCManager );
	if( hNewService != NULL )   CloseServiceHandle( hNewService );

	return fStatus;
}



/*
**	Purpose:
**		Deleting a Service.
**
**	Arguments:
**		szComputerName	: String pointer containing the name of the computer.
**		szServiceName	: String pointer containing the name of the service.
**
**	Returns:
**		TRUE if service was successfully deleted, FALSE if the service
**		could not be deleted.
**		
***************************************************************************/
BOOL bDeleteService(LPSTR szComputerName, LPSTR szServiceName)
{
	SC_HANDLE hSCManager;
	SC_HANDLE hService = NULL;
	SC_LOCK   lSCLock;
	BOOL      fStatus = TRUE;
	DWORD	  dReturnCode = 0;

	hSCManager = NULL;
	hService   = NULL;
	lSCLock    = NULL;

	/*
	 * Open the Service Control Manager
	 */
	hSCManager = OpenSCManager( szComputerName, NULL, SC_MANAGER_ALL_ACCESS );
	if( hSCManager == NULL )
	{
		fStatus = FALSE;
		goto QueryCleanup;
	}

	/*
	 * Lock the active database for our exclusive use
	 */
	lSCLock = LockServiceDatabase( hSCManager );
	if( lSCLock == NULL )
	{
		fStatus = FALSE;
		goto QueryCleanup;
	}

	/*
	 * Open the service
	 */
	hService = OpenService( hSCManager, szServiceName, GENERIC_ALL );
	if( hService == NULL )
	{
		fStatus = FALSE;
		goto QueryCleanup;
	}

	if (!DeleteService(hService))
	{
		dReturnCode = GetLastError();
		if (ERROR_SERVICE_MARKED_FOR_DELETE != dReturnCode) 
		{
			fStatus = FALSE;
		}
	}

QueryCleanup:

	if( lSCLock != NULL )    UnlockServiceDatabase( lSCLock );
	if( hSCManager != NULL ) CloseServiceHandle( hSCManager );
	if( hService != NULL )   CloseServiceHandle( hService );

	CloseServiceHandle( hService);

	return fStatus;
}


/*
**	Purpose:
**		Enumerates the Event Log Sources Registry Value.
**
**	Arguments:
**		szEventLogRegistryKey - Event Log (application, system, security) key
**		szValueName - "Sources"
**
**	Returns:
**		none
**		
***************************************************************************/
void fEnumerateEventLogSources(LPSTR szEventLogRegistryKey,
							   LPSTR szValueName)
{
	char		szBuffer[MAX_BUFFER*8];
	char		szKeyName[MAX_STRING];
	DWORD		dwKeySize = MAX_STRING;
	DWORD		dLastError;
	ULONG_PTR	dwBufferSize = 0;
	LPSTR		lpszBufferPointer;
	int			iIndex;
	PFILETIME	lpftLastWrite = NULL;
	HKEY		hKey;

	lpszBufferPointer = szBuffer;

	if (ERROR_SUCCESS == RegOpenKeyEx(hGlobalKey,
									  szEventLogRegistryKey,
									  0,
									  KEY_ALL_ACCESS,
									  &hKey))
	{
		for (iIndex = 0; ERROR_SUCCESS == RegEnumKeyEx(hKey, iIndex, szKeyName, &dwKeySize,
													   NULL, NULL, NULL, lpftLastWrite); iIndex++)
		{
			strcpy(lpszBufferPointer, szKeyName);
			lpszBufferPointer += strlen(szKeyName) + 1;
			dwBufferSize += strlen(szKeyName) + 1;
			dwKeySize = MAX_STRING;
		}
		dLastError = GetLastError();
		strcpy(lpszBufferPointer, "");
		dwBufferSize++;

		fAddRegistryEntry (szEventLogRegistryKey,  
						   szValueName, 
						   szBuffer, 
						   REG_MULTI_SZ, 
						   dwBufferSize);

	}
}


/*
**	Purpose:
**		Adds all registry values for the IBM SDLC Link Service.
**
**	Arguments:
**		none
**
**	Returns:
**		none
**		
***************************************************************************/
void fAddAllRegistryValues(DWORD dStartIndex)
{
	DWORD	iIndex;
	ULONG_PTR	dLength;
	LPSTR	lpszTemp;
	DWORD	dTempDword;
	int		iTempInt;

	for (iIndex = dStartIndex; strlen(MGSDLCREG[iIndex].szRegistryKey) != 0; iIndex++)
	{
		switch (MGSDLCREG[iIndex].dType)
		{
			case REG_SZ:
				{
					dLength = strlen(MGSDLCREG[iIndex].szRegistryData) + 1;
					break;
				}

			case REG_MULTI_SZ:
				{
					dLength = 0;
					lpszTemp = MGSDLCREG[iIndex].szRegistryData;
					while (strlen(lpszTemp) > 0)
					{
						dLength += strlen(lpszTemp) + 1;
						lpszTemp += strlen(lpszTemp) + 1;
					}
					dLength++;
					break;
				}

			case REG_EXPAND_SZ:
				{
					dLength = strlen(MGSDLCREG[iIndex].szRegistryData) + 1;
					break;
				}

			case REG_BINARY:
				{
					dLength = dLength = MGSDLCREG[iIndex].dLength;;
					break;
				}

			case REG_DWORD:
				{
					dLength = 4;
					iTempInt = atoi(MGSDLCREG[iIndex].szRegistryData);
					dTempDword = (DWORD) iTempInt;
					CopyMemory(MGSDLCREG[iIndex].szRegistryData, &dTempDword, 4);
					break;
				}
		}
		if (MGSDLCREG[iIndex].bInstallThisEntry)
		{

			if (gdwOSMajorVersion <= 4)
			{
				fAddRegistryEntry (MGSDLCREG[iIndex].szRegistryKey,  
								   MGSDLCREG[iIndex].szRegistryValue, 
								   MGSDLCREG[iIndex].szRegistryData, 
								   MGSDLCREG[iIndex].dType, 
								   dLength);
			}
			else
			{
				if (MGSDLCREG[iIndex].bInstallThisEntryForNT5)
				{
					fAddRegistryEntry (MGSDLCREG[iIndex].szRegistryKey,  
									   MGSDLCREG[iIndex].szRegistryValue, 
									   MGSDLCREG[iIndex].szRegistryData, 
									   MGSDLCREG[iIndex].dType, 
									   dLength);
				}
			}
		}
	}

}


/*
**	Purpose:
**		Removes all registry values for the IBM SDLC Link Service.
**
**	Arguments:
**		none
**
**	Returns:
**		none
**		
***************************************************************************/
void fRemoveAllRegistryValues(BOOL bRemoveSyncDriver)
{
	int	iIndex;

	if (bRemoveSyncDriver)
	{
		for (iIndex = LAST_MGSYNC_INDEX; iIndex >= FIRST_MGSYNC_INDEX; iIndex--)
		{
			fRemoveRegistryEntry (MGSDLCREG[iIndex].szRegistryKey);
		}
	}

	for (iIndex = LAST_MGSDLC_INDEX; iIndex >= FIRST_MGSDLC_INDEX; iIndex--)
	{
		fRemoveRegistryEntry (MGSDLCREG[iIndex].szRegistryKey);
	}
}


/*
**	Purpose:
**		Replace Global Registry Structure Strings for Registry Key Name
**
**	Arguments:
**		none
**
**	Returns:
**		none
**		
***************************************************************************/
void fReplaceRegistryKeyName(DWORD dStartIndex, DWORD dEndIndex,
							 LPSTR szOldString, LPSTR szNewString)
{
	DWORD	dIndex;

	for (dIndex = dStartIndex; dIndex <= dEndIndex; dIndex++)
	{
		fFindAndReplaceString(MGSDLCREG[dIndex].szRegistryKey, 
							  szOldString, szNewString);
	}
}


/*
**	Purpose:
**		Replace Global Registry Structure Strings for Registry Key Name
**
**	Arguments:
**		none
**
**	Returns:
**		none
**		
***************************************************************************/
void fReplaceRegistryData(DWORD dStartIndex, DWORD dEndIndex,
						  LPSTR szOldString, LPSTR szNewString)
{
	DWORD	dIndex;

	for (dIndex = dStartIndex; dIndex <= dEndIndex; dIndex++)
	{
		fFindAndReplaceString(MGSDLCREG[dIndex].szRegistryData, 
							  szOldString, szNewString);
	}
}


/*
**	Purpose:
**		Update all user provided information in the registry data 
**		structures.
**
**	Arguments:
**		none
**
**	Returns:
**		none
**		
***************************************************************************/
void fReplaceAllRegistryValues(ACTION eAction)
{
	BOOL	bLinkRegistry = TRUE;
	
	/*
	 *  Start with the Link Service Registry Entries.
	 */
	fReplaceRegistryKeyName( LINKSERVICE_START, LINKSERVICE_STOP, "LINKSERVICE", gMgsdlc_data.szLinkServiceSoftwareRegistryRoot); // LINKSERVICE
	fReplaceRegistryKeyName(SNASDLC_START, SNASDLC_STOP, "SnaSdlc", gMgsdlc_data.szLinkServiceServicesRegistryRoot); // SnaSdlc

	fReplaceRegistryData( SSERVICE,  SSERVICE, LSTYPE1, gMgsdlc_data.szLinkServiceServicesRegistryRoot); // SnaSdlc
	fReplaceRegistryData( STITLE1,  STITLE1, DIGISERVICETITLE, gMgsdlc_data.szLinkServiceTitle); // IBM SDLC Link Service #
	fReplaceRegistryData( SBINDFORM,  SBINDFORM, LSTYPE1, gMgsdlc_data.szLinkServiceServicesRegistryRoot); // SnaSdlc
	fReplaceRegistryData( STITLE2, STITLE2, DIGISERVICETITLE, gMgsdlc_data.szLinkServiceTitle); // IBM SDLC Link Service #
	fReplaceRegistryData( STYPE, STYPE, "TYPETAG", gMgsdlc_data.szLinkServiceLinkServicetype); // ibmsdlc  
	fReplaceRegistryData( SNAME, SNAME, LSTYPE1, gMgsdlc_data.szLinkServiceServicesRegistryRoot); // SnaSdlc
	fReplaceRegistryData( SEXPORT, SEXPORT, LSTYPE1, gMgsdlc_data.szLinkServiceServicesRegistryRoot); // SnaSdlc
	fReplaceRegistryData( STITLE3, STITLE3, DIGISERVICETITLE, gMgsdlc_data.szLinkServiceTitle); // IBM SDLC Link Service #
	fReplaceRegistryData( SCARRIER, SCARRIER, "0", gMgsdlc_data.szConstantRTS); // 0
	fReplaceRegistryData( SDEVNAME, SDEVNAME, LSTYPE1, gMgsdlc_data.szDeviceName); // IBMSDLC
	fReplaceRegistryData( SLINETYPE, SLINETYPE, "0", gMgsdlc_data.szLineType); // 0
	fReplaceRegistryData( SRADIOFLAG, SRADIOFLAG, "0", gMgsdlc_data.szRadio4Flag); // 0
	fReplaceRegistryData( SDISTRIB, SDISTRIB, "0", gMgsdlc_data.szDistributable); // 0
	fReplaceRegistryData( 0, LAST_MGSDLC_INDEX, SNA_PATH_TOKEN_STRING, gMgsdlc_data.szInstallPath); // SNAPATHNAME

	char temp_string[MAX_STRING],fmt_string[MAX_STRING];
	HINSTANCE hInst;

	hInst = AfxGetResourceHandle();
	LoadString(hInst,IDS_LINKSERVICE_TITLE3,temp_string,sizeof(temp_string));

	fReplaceRegistryData( DTITLE1, DTITLE1, DIGIDRIVERTITLE, temp_string);
	fReplaceRegistryData( DTITLE3, DTITLE3, DIGIDRIVERTITLE, temp_string);
	fReplaceRegistryData( DTITLE2, DTITLE2, DIGIDRIVERTITLE, temp_string);

	LoadString(hInst,IDS_LINKSERVICE_DESCRIPTION,fmt_string,sizeof(fmt_string));
	sprintf(temp_string,fmt_string,LINKSERVICETYPE);

	fReplaceRegistryData( SDESCRIPT1, SDESCRIPT1, DIGISERVICEDESC, temp_string);
	fReplaceRegistryData( SDESCRIPT2, SDESCRIPT2, DIGISERVICEDESC, temp_string);

	LoadString(hInst,IDS_LINKSERVICE_DESCRIPTION2,temp_string,sizeof(temp_string));
	fReplaceRegistryData( DDESCRIPT1, DDESCRIPT1, DIGIDRIVERDESC, temp_string);
	fReplaceRegistryData( DDESCRIPT2, DDESCRIPT2, DIGIDRIVERDESC, temp_string);


	/*
	 * Continue with the Device Driver Registry Entries.
	 */
	bLinkRegistry = FALSE;

	fReplaceRegistryData( FIRST_MGSYNC_INDEX, LAST_MGSYNC_INDEX, SNA_PATH_TOKEN_STRING, gMgsdlc_data.szInstallPath); // SNAPATHNAME


}

/*
**	Purpose:
**		Add the "class" and "bindform" registry entries.
**
**	Arguments:
**		none
**
**	Returns:
**		none
***************************************************************************/
void fAddClassAndBindformRegistries(void)
{
	DWORD	dIndex;
	char	szTempBuffer1[MAX_STRING];
	char	szTempBuffer2[MAX_STRING];
	BOOL	bFirstLSFound = FALSE;

	for (dIndex=0;	dIndex<=64; dIndex++)
	{

		wsprintf(szTempBuffer1, "SOFTWARE\\Microsoft\\%s%2.2d",
			LINK_REGISTRY_BASE, dIndex);
		if (fRegistryKeyExists(szTempBuffer1))
		{
			strcpy(szTempBuffer2, szTempBuffer1);
			strcat(szTempBuffer2, "\\CurrentVersion\\NetRules");

			if (!bFirstLSFound)
			{
				bFirstLSFound = TRUE;
				strcpy(szTempBuffer1,BINDABLE);
				ULONG_PTR len = strlen(szTempBuffer1);
				szTempBuffer1[len+1] = '\0';
				szTempBuffer1[len+2] = '\0';
				
				fAddRegistryEntry (szTempBuffer2,"bindable",szTempBuffer1, 
						   REG_MULTI_SZ,len+2);
				
				strcpy(szTempBuffer1,CLASS);
				len = strlen(szTempBuffer1);
				szTempBuffer1[len+1] = '\0';
				szTempBuffer1[len+2] = '\0';
				fAddRegistryEntry (szTempBuffer2,"class",szTempBuffer1, 
						   REG_MULTI_SZ, len+2);
			}
			else
			{
				fRemoveRegistryValue (szTempBuffer2, "bindable");
				fRemoveRegistryValue (szTempBuffer2, "class");
			}
		}
	}
}



/*
**	Purpose:
**		Update all user provided information in the registry data 
**		structures.
**
**	Arguments:
**		none
**
**	Returns:
**		TRUE if link service found (new or pre-existing)
**		
***************************************************************************/

BOOL fSetupGlobalValues(LPSTR szLinkServiceTitle, LPSTR szComputerName, LPSTR szSNALinePath)
{
#define	SOFTWARE_REGISTRY_BASE		_TEXT("SOFTWARE\\Microsoft\\")
#define MAX_LINK_SERVICE			64
#define	SOFTWARE_REGISTRY_VALUE		_TEXT("Title")


	char		szCurrentRegistryKey[MAX_STRING];
	DWORD		dLinkServiceNumber = 1;
	char		szLinkServiceNumber[MAX_STRING];
	BOOL		bLinkServiceFound = FALSE;
	DWORD		dSize = 4096;
	DWORD		dCarrier;

	//
	// Get the SNA Server Install Root Directory.
	//
	fQueryRegistryValue("SOFTWARE\\Microsoft\\SNA Server\\CurrentVersion\\Setup", 
					    "RootDir", gMgsdlc_data.szInstallPath, &dSize);

	HINSTANCE	hInst = AfxGetResourceHandle();
	if (!strcmp(szLinkServiceTitle, ""))
	{

		gMgsdlc_data.bBindingsNeeded = TRUE;

		char temp_string[MAX_STRING],fmt_string[MAX_STRING];
		LoadString(hInst,IDS_LINKSERVICE_TITLE,fmt_string,sizeof(fmt_string));
		sprintf(temp_string,fmt_string,LINKSERVICETYPE);
		strcpy(gMgsdlc_data.szLinkServiceTitle, temp_string);

		while (!(bLinkServiceFound) && (MAX_LINK_SERVICE >= dLinkServiceNumber))
		{

			wsprintf(szCurrentRegistryKey,"%s%s%2.2d",
				SOFTWARE_REGISTRY_BASE,LINK_REGISTRY_BASE,dLinkServiceNumber);

			if (fRegistryKeyExists(szCurrentRegistryKey))
			{
					dLinkServiceNumber++;
			}
			else
			{
				wsprintf(szLinkServiceNumber, "%i", (int)dLinkServiceNumber);
				strcat(gMgsdlc_data.szLinkServiceTitle, szLinkServiceNumber);
				bLinkServiceFound = TRUE;
			}
		}
	}
	else
	{
		// Link Service exists search for it.
		//
		strcpy(gMgsdlc_data.szLinkServiceTitle, szLinkServiceTitle);
		char	szCurrentLinkTitle[MAX_STRING];

		while (!(bLinkServiceFound) && (MAX_LINK_SERVICE >= dLinkServiceNumber))
		{

			wsprintf(szCurrentRegistryKey,"%s%s%2.2d\\CurrentVersion",
				SOFTWARE_REGISTRY_BASE,LINK_REGISTRY_BASE,dLinkServiceNumber);

			if (fRegistryKeyExists(szCurrentRegistryKey))
			{
				dSize = MAX_STRING;
				fQueryRegistryValue(szCurrentRegistryKey, SOFTWARE_REGISTRY_VALUE, 
									szCurrentLinkTitle, &dSize);
				if (!strcmp(szCurrentLinkTitle, szLinkServiceTitle))
				{
					bLinkServiceFound = TRUE;
				}
				else
				{
					dLinkServiceNumber++;
				}
			}
			else
			{
				dLinkServiceNumber++;
			}
		}
	}
	wsprintf(gMgsdlc_data.szLinkServiceLinkServicetype, "%s_%2.2d",
		SERVICETITLE,dLinkServiceNumber);

	wsprintf(szLinkServiceNumber, "%x", dLinkServiceNumber);
	
	strcpy(gMgsdlc_data.szLinkServiceName, SERVICETITLE);
	strcat(gMgsdlc_data.szLinkServiceName, szLinkServiceNumber);

	wsprintf(gMgsdlc_data.szLinkServiceSoftwareRegistryRoot,"%s%2.2d",
		LINK_REGISTRY_BASE,dLinkServiceNumber);
//	strcpy(gMgsdlc_data.szLinkServiceSoftwareRegistryRoot, szCurrentLinkName);
	strcpy(gMgsdlc_data.szLinkServiceServicesRegistryRoot, gMgsdlc_data.szLinkServiceName);

	strcpy(szLinkServiceNumber, REGSERVICE);
	strcat(szLinkServiceNumber, gMgsdlc_data.szLinkServiceName);
	strcat(szLinkServiceNumber, REGPARAMS);


	dSize = sizeof(gMgsdlc_data.szCardString);
	fQueryRegistryValue(szLinkServiceNumber,CARD,gMgsdlc_data.szCardString,&dSize);


	// Set the Device name that the driver needs to create.  For NT4 the name is generated by
	// this DLL.  For NT5 the name is defined during the configuration.  This allows PnP to
	// bring up the device even though no Link Service is defined for the port.
	dSize = sizeof(gMgsdlc_data.szDeviceName);
	if (!fQueryRegistryValue(szLinkServiceNumber, DEVICENAME, gMgsdlc_data.szDeviceName, &dSize))
	{
		// This should only happen on a new install
		if (gdwOSMajorVersion <= 4)
		{
			sprintf(gMgsdlc_data.szDeviceName,"%s%d",DIGIDEVICENAME,dLinkServiceNumber);
		}
		else
		{
			CString szAdapterPath;
			szAdapterPath = strAdapterRegPath;
			szAdapterPath += szSNALinePath;
			TRACE("fSetupGlobalValues: szAdapterPath = %s\n", szAdapterPath);
			fQueryRegistryValue((char*)(LPCTSTR) szAdapterPath, SNADEVICE, gMgsdlc_data.szDeviceName, &dSize);
		}
	}

	if (!fQueryRegistryValue(szLinkServiceNumber, CARRIER, (char*)&dCarrier, &dSize))
		dCarrier = 0;
	wsprintf(gMgsdlc_data.szConstantRTS, "%d", dCarrier);

	if (!fQueryRegistryValue(szLinkServiceNumber, LINETYPE, (char*)&dCarrier, &dSize))
		dCarrier = 0;
	wsprintf(gMgsdlc_data.szLineType, "%d", dCarrier);

	if (!fQueryRegistryValue(szLinkServiceNumber, RADIO4FLAG, (char*)&dCarrier, &dSize))
		dCarrier = 0;
	wsprintf(gMgsdlc_data.szRadio4Flag, "%d", dCarrier);

	if (!fQueryRegistryValue(szLinkServiceNumber, LINESPEED, (char*)&dCarrier, &dSize))
		dCarrier = 56000;
	gMgsdlc_data.dLineSpeed = dCarrier;

	if (!fQueryRegistryValue(szLinkServiceNumber, INTERNALCLOCK, (char*)&dCarrier, &dSize))
		dCarrier = 0;
	gMgsdlc_data.bInternalClock = dCarrier;

	strcpy(szLinkServiceNumber, REGSERVICE);
	strcat(szLinkServiceNumber, gMgsdlc_data.szLinkServiceName);
	strcat(szLinkServiceNumber, "\\Parameters");

	if (!fQueryRegistryValue(szLinkServiceNumber, ISREMOTABLE, (char*)&dCarrier, &dSize))
		dCarrier = 0;
	wsprintf(gMgsdlc_data.szDistributable, "%d", dCarrier);

	strcpy(gMgsdlc_data.szDeviceDriverName, "DIGISNA");

	LoadString(hInst,IDS_LINKSERVICE_DESCRIPTION2,
		gMgsdlc_data.szDeviceDriverTitle,sizeof(gMgsdlc_data.szDeviceDriverTitle));
	
	strcpy(gMgsdlc_data.szDeviceDriverSoftwareRegistryRoot, "DIGISNADD_1.0_01");
	strcpy(gMgsdlc_data.szDeviceDriverServicesRegistryRoot, "DIGISNA");

	return bLinkServiceFound;
}


/*
**	Purpose:
**		Check for the last IBM SDLC Link Service.  This routine is used
**		to determine if the IBMSDLC Device Driver can be removed.
**
**	Arguments:
**		none
**
**	Returns:
**		none
**		
***************************************************************************/
BOOL bLastDigiLinkService(LPSTR szComputerName)
{
	BOOL bReturnCode = TRUE;
	CString sztmp(REGSNA);
	sztmp += _T("\\LinkServices");
	DWORD dwIndex = 0;
	char szKeyName[MAX_STRING];
	DWORD dwKeySize = MAX_STRING;
	PFILETIME lpftLastWrite = NULL;
	HKEY hKey;
	if (RegOpenKeyEx(hGlobalKey,sztmp,0,KEY_READ,&hKey) != ERROR_SUCCESS)
		return bReturnCode;
	// simply enumerate to see whether a single service exists. If none, then we can indicate
	// that no linkservices exist.
	if (RegEnumKeyEx(hKey, dwIndex, szKeyName, &dwKeySize,NULL,NULL,NULL, lpftLastWrite) ==
		     ERROR_SUCCESS)
			 bReturnCode = FALSE;
	RegCloseKey(hKey);
	return bReturnCode;
}

/*
**	Purpose:
**		Determine the NT version and set the global variables.
**	Arguments:
**		N/A
**	Returns:
**		N/A	 
***************************************************************************/

void SetOSGlobals()
{
	BOOL bSuccess = FALSE;
	OSVERSIONINFO version;
	version.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);

	GetVersionEx(&version);
	gdwOSMajorVersion = version.dwMajorVersion;

	if (gdwOSMajorVersion <= 4)
	{
		strAdapterRegPath = REGADAPTERPATH_NT4;
	}
	else
	{
		strAdapterRegPath = REGADAPTERPATH_NT5;
	}

}


/*
**	Purpose:
**	search system for all DigiDxB entries and within those entries look for
**	SNA service availability on a line. If that line is not in use, return its registry
**	path in the supplied CString
**	Arguments:
**		reference to CString which will hold line path
**	Returns:
**		TRUE if available line found
***************************************************************************/
BOOL bAvailableSNALineNT4(CString& szLinePath)
{
	#define	DIGIDXB	_T("SraDxb")
	#define DIGI570 _T("DG570MP")
	#define SNASERVICEPATH _T("\\Parameters\\SNAService")
	#define MARKER _T(':')

	HKEY hKey;
	CString szRoot(_T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkCards"));
	if (ERROR_SUCCESS != RegOpenKeyEx(hGlobalKey,szRoot,0,KEY_ALL_ACCESS,&hKey))
		return FALSE;

	DWORD dwIndex = 0,dwKeySize = MAX_STRING;
	char szKeyName[MAX_STRING];
	PFILETIME	lpftLastWrite = NULL;

	// cheap way of storing columns of data.
	CStringArray astrInstances,astrDisplayInstances;
	
	// enumerate network card instances.
	while (RegEnumKeyEx(hKey, dwIndex, szKeyName, &dwKeySize,NULL,NULL,NULL, lpftLastWrite) ==
		     ERROR_SUCCESS)
	{
		char szValue[MAX_STRING];
		DWORD dwValSize = MAX_STRING;
		CString szKey(szRoot);
		szKey += _T("\\");
		szKey += szKeyName;
		// read the ServiceName value and look to find DigiDxb or Digi570
		if (fQueryRegistryValue((char*)(LPCTSTR)szKey,"ServiceName",szValue,&dwValSize))
		{
			if (_strnicmp(DIGIDXB,szValue,strlen(DIGIDXB)) == 0 ||
				_strnicmp(DIGI570,szValue,strlen(DIGI570)) == 0)
			{
				char szTitle[MAX_STRING];
				dwValSize = MAX_STRING;
				fQueryRegistryValue((char*)(LPCTSTR)szKey,"Title",szTitle,&dwValSize);

				// we have a match. Open this instance and try to find an unused SNA port
				CString szService(strAdapterRegPath);
				szService += szValue;
				szService += SNASERVICEPATH;
				// read in the adapter name, while we are here
	//			dwValSize = sizeof(gMgsdlc_data.szCardString);
	//			fQueryRegistryValue((char*)(LPCTSTR)szService,"Option",gMgsdlc_data.szCardString,&dwValSize);
	//			szService += _T("\\SNAService");
				HKEY hInstKey;
				if (RegOpenKeyEx(hGlobalKey,szService,0,KEY_READ,&hInstKey) == ERROR_SUCCESS)
				{
					// SNA key exists. Enum all line keys under it 
					char szLine[8];
					DWORD dwLineIndex=0;
					DWORD dwLineSize=sizeof(szLine);
					while(RegEnumKeyEx(hInstKey,dwLineIndex,szLine,&dwLineSize,
						NULL,NULL,NULL,lpftLastWrite) == ERROR_SUCCESS)
					{
						CString szTemp(szService);
						szTemp += _T("\\");
						szTemp += szLine;
						DWORD nInUse=0;
						DWORD dwSize=sizeof(nInUse);
						fQueryRegistryValue((char*)(LPCTSTR)szTemp,INUSE,(char*)&nInUse,&dwSize);
						if (!nInUse)
						{
							szTemp = szValue;
							szTemp += SNASERVICEPATH;
							szTemp += _T("\\");
							szTemp += szLine;
							astrInstances.Add(szTemp);
							int linenumber = atoi(szLine);
							szTemp.Format("%s - Line %d",szTitle,linenumber+1);
							astrDisplayInstances.Add(szTemp);
						}
						++dwLineIndex;
						dwLineSize=sizeof(szLine);
					}
					RegCloseKey(hInstKey);
				}
			}// strnicmp 
			
		}// ServiceName query
		++dwIndex;
		dwKeySize = MAX_STRING;
	}// while
	RegCloseKey(hKey);

	// if no instances were found, we have an empty array
	// otherwise prompt user with a list of all available instances
	BOOL bFound = FALSE;
	int nIndex = 0;
	if (astrInstances.GetUpperBound() >= 0)
	{
		CLineSelect dlg;
		dlg.SetDisplayList(&astrDisplayInstances);
		if (dlg.DoModal() == IDOK)
		{
			nIndex = dlg.GetSelection();
			// get the instance whose SNA line is being used
			szLinePath = astrInstances[nIndex];
			strcpy(gMgsdlc_data.szCardString,astrDisplayInstances[nIndex]);
			bFound = TRUE;
		}
	}
	else
	{
		CString str;
		str.LoadString(IDS_NOSNA_LINE);
		AfxMessageBox(str);
	}
	return bFound;
	
}


/*
**	Purpose:
**	search system for all DigiDxB entries and within those entries look for
**	SNA service availability on a line. If that line is not in use, return its registry
**	path in the supplied CString
**	Arguments:
**		reference to CString which will hold line path
**	Returns:
**		TRUE if available line found
***************************************************************************/
BOOL bAvailableSNALineNT5(CString& szLinePath)
{
	#define SNASERVICEPATH_NT5 _T("SNAService")

	// cheap way of storing columns of data.
	CStringArray astrInstances,astrDisplayInstances;
	CString strNetCardClass(REGADAPTERPATH_NT5);
	strNetCardClass += REGNETCARDGUID_NT5;

	TRACE("bAvailableSNALineNT5: strNetCardClass = %s\n", strNetCardClass);

	HKEY hkNetCards;
	if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, strNetCardClass, 0, KEY_ALL_ACCESS, &hkNetCards) == ERROR_SUCCESS)
	{
		DWORD dwValSize = MAX_STRING;
		DWORD dwIndex = 0;
		DWORD dwSize;
		TCHAR KeyName[MAX_PATH];
		FILETIME time;

		// enumerate all keys and look for the Digi specific signature
		while(dwSize = sizeof KeyName, RegEnumKeyEx(hkNetCards, dwIndex++, KeyName, &dwSize, NULL, NULL, NULL, &time) == ERROR_SUCCESS)
		{
			HKEY hkParams;
			CString strKey(strAdapterRegPath);
			strKey += REGNETCARDGUID_NT5;
			strKey += KeyName;
			strKey += _T('\\');
			strKey += DXB_REGKEY_PARAMETERS;
			
			// open key\Parameter registry key
			if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, strKey, 0 ,KEY_ALL_ACCESS, &hkParams) == ERROR_SUCCESS)
			{
				// Get the Digi-specific Option value, defining the adapter type
				CString strOption;
				char szOption[MAX_STRING];
				dwValSize = MAX_STRING;
				fQueryRegistryValue((char*)(LPCTSTR)strKey,DXB_REGVAL_OPTION,szOption,&dwValSize);
				strOption = szOption;

				// Finally!  Is this a supported adapter?
				if (strOption == DXB_REGVAL_OPTION_ENUM_CLYDE ||
					strOption == DXB_REGVAL_OPTION_ENUM_SYNC570)
				{
					char szTitle[MAX_STRING];
					dwValSize = MAX_STRING;
					fQueryRegistryValue((char*)(LPCTSTR)strKey,"ServiceName",szTitle,&dwValSize);

					// we have a match. Open this instance and try to find an unused SNA port
					CString strService(SNASERVICEPATH_NT5);

					HKEY hInstKey;
					if (RegOpenKeyEx(hkParams,strService,0,KEY_READ,&hInstKey) == ERROR_SUCCESS)
					{

						// SNA key exists. Enum all line keys under it 
						PFILETIME	lpftLastWrite = NULL;
						char szLine[8];
						DWORD dwLineIndex=0;
						DWORD dwLineSize=sizeof(szLine);
						while(RegEnumKeyEx(hInstKey,dwLineIndex,szLine,&dwLineSize,
							NULL,NULL,NULL,lpftLastWrite) == ERROR_SUCCESS)
						{

							CString strTemp(strKey);
							strTemp += _T("\\");
							strTemp += strService;
							strTemp += _T("\\");
							strTemp += szLine;
							DWORD nInUse=0;
							DWORD nSnid=0;
							DWORD dwSize=sizeof(nInUse);
							fQueryRegistryValue((char*)(LPCTSTR)strTemp,INUSE,(char*)&nInUse,&dwSize);
							dwSize=sizeof(nSnid);
							fQueryRegistryValue((char*)(LPCTSTR)strTemp,SNID,(char*)&nSnid,&dwSize);
							if (!nInUse && nSnid)
							{
								strTemp = REGNETCARDGUID_NT5;
								strTemp += KeyName;
								strTemp += _T('\\');
								strTemp += DXB_REGKEY_PARAMETERS;
								strTemp += _T("\\");
								strTemp += strService;
								strTemp += _T("\\");
								strTemp += szLine;
								astrInstances.Add(strTemp);
								int linenumber = atoi(szLine);
								strTemp.Format("%s - Line %d",szTitle,linenumber+1);
								astrDisplayInstances.Add(strTemp);
							}
							++dwLineIndex;
							dwLineSize=sizeof(szLine);
						}
						RegCloseKey(hInstKey);
					}
				}
				RegCloseKey(hkParams);
			}
		}
		RegCloseKey(hkNetCards);
	}
	else
	{
		return FALSE;
	}

	// if no instances were found, we have an empty array
	// otherwise prompt user with a list of all available instances
	BOOL bFound = FALSE;
	int nIndex = 0;
	if (astrInstances.GetUpperBound() >= 0)
	{
		CLineSelect dlg;
		dlg.SetDisplayList(&astrDisplayInstances);
		if (dlg.DoModal() == IDOK)
		{
			nIndex = dlg.GetSelection();
			// get the instance whose SNA line is being used
			szLinePath = astrInstances[nIndex];
			strcpy(gMgsdlc_data.szCardString,astrDisplayInstances[nIndex]);
			bFound = TRUE;
		}
	}
	else
	{
		CString str;
		str.LoadString(IDS_NOSNA_LINE);
		AfxMessageBox(str);
	}
	return bFound;
	
}

/*
**	Purpose:
**  For a new link service, point the DigiSNA driver to the DigiDxb instance where SNA has been
**  configured to be run. Also point the driver to the linkservice entry
**  for the Line that will be running this linkservice,set its InUse parameter to 1
**	Arguments:
**		const pointer to the registry location of the line on which SNA will
**      run. Obtained from AvailableSNALine
**	Returns:
**		None
***************************************************************************/
void LinkToDriver(const char* szLinePath)
{
	HKEY hkey;
	CString sztmp(REGSNA);
	CString szAdapterPath;
	// verify that the driver key exists
	if (RegOpenKeyEx(hGlobalKey,sztmp,0,KEY_ALL_ACCESS,&hkey) != ERROR_SUCCESS)
	{
		// we shouldn't ever get here
		ASSERT(FALSE);
		AfxMessageBox(_T("Error, DigiSNA entry does not yet exist"));
		return;
	}
	RegCloseKey(hkey);
	// create a key under with the device name as the key name
	sztmp += _T("\\LinkServices\\");
	sztmp += gMgsdlc_data.szLinkServiceServicesRegistryRoot;
	fAddRegistryEntry((char*)(LPCTSTR)sztmp,"","",REG_SZ,1);
	// point to the actual line within DigiDxb instance on which this link service will run
	fAddRegistryEntry((char*)(LPCTSTR)sztmp,DXBPATH,(char*)szLinePath,REG_SZ,strlen(szLinePath)+1);
	// point to the linkservice 
	CString szlspath(gMgsdlc_data.szLinkServiceServicesRegistryRoot);
	szlspath += REGPARAMS;
	fAddRegistryEntry((char*)(LPCTSTR)sztmp,LSPATH,
		(char*)(LPCTSTR)szlspath,REG_SZ,szlspath.GetLength()+1);


	fAddRegistryEntry((char*)(LPCTSTR)sztmp,"Device",gMgsdlc_data.szDeviceName,REG_SZ,
		strlen(gMgsdlc_data.szDeviceName)+1);
	
	// now update the InUse value to 1 to indicate this SNA line is occupied
	szAdapterPath = strAdapterRegPath;
	szAdapterPath += szLinePath;
	DWORD data=1;
	fAddRegistryEntry((char*)(LPCTSTR)szAdapterPath,INUSE,(char*)&data,REG_DWORD,sizeof(DWORD));
}

/*
**	Purpose:
**  remove from DigiSNA the entry associated with the LinkService in question
**	Also set the InUse parameter to 0 for the line on which SNA was running.
**	Basically reverses the action of the LinkToDriver function
**	Arguments:
**		None
**	Returns:
**		None
***************************************************************************/
void UnLinkFromDriver()
{
	CString sztmp(REGSNA);
	sztmp += _T("\\LinkServices\\");
	sztmp += gMgsdlc_data.szLinkServiceServicesRegistryRoot;
	char szValue[MAX_STRING];
	DWORD dwValSize = MAX_STRING;
	// Point to the DXB instance on which this entry was running
	if (fQueryRegistryValue((char*)(LPCTSTR)sztmp,DXBPATH,szValue,&dwValSize))
	{
		CString str(strAdapterRegPath);
		str += szValue;

		TRACE("UnLinkFromDriver: strAdapterRegPath = %s\n", str);

		// remove the InUse key
		fRemoveRegistryValue((char*)(LPCTSTR)str,INUSE);

	}
	// remove the subkey.
	fRemoveRegistryEntry((char*)(LPCTSTR)sztmp);

}

// Add data to the ExtraParameters key
void AddExtraParameters()
{
	CString str(REGSERVICE);
	str += gMgsdlc_data.szLinkServiceServicesRegistryRoot;
	str += REGPARAMS;

	DWORD data = gMgsdlc_data.dLineSpeed;
	fAddRegistryEntry((char*)(LPCTSTR)str,LINESPEED,(char*)&data,REG_DWORD,sizeof(DWORD));
	data = gMgsdlc_data.bInternalClock;
	fAddRegistryEntry((char*)(LPCTSTR)str,INTERNALCLOCK,(char*)&data,REG_DWORD,sizeof(DWORD));
	fAddRegistryEntry((char*)(LPCTSTR)str,CARD,gMgsdlc_data.szCardString,REG_SZ,
		strlen(gMgsdlc_data.szCardString)+1);

}
// install a service. 
void fInstallService(BOOL bNewLinkService,LPSTR pszComputerName,LPSTR pszSNALinePath)
{
	BOOL bReturnCode = FALSE;
	char szImagePath[MAX_PATH];
		strcpy(szImagePath, gMgsdlc_data.szInstallPath);
		strcat(szImagePath, "\\system\\snalink.exe");

		bCreateService(pszComputerName, gMgsdlc_data.szLinkServiceName, 
									 szImagePath, 
									 "", SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START);


		//
		// Create the Digi SNA Miniport Device Driver, For NT5 this is controlled
		// by PnP.
		//
		strcpy(szImagePath, gMgsdlc_data.szInstallPath);
		strcat(szImagePath, "\\system\\digisna.sys");

		if (gdwOSMajorVersion <= 4)
		{
			bCreateService(pszComputerName, gMgsdlc_data.szDeviceDriverName, 
										 szImagePath, 
										 "", SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START);
		}

		//
		// Update the Registry Data Structure.
		//
		fReplaceAllRegistryValues(NEW);

		//
		// Write the Registry Data to the Registry
		//
		fAddAllRegistryValues(FIRST_MGSDLC_INDEX);
		fAddAllRegistryValues(FIRST_MGSYNC_INDEX);
		AddExtraParameters();
		fEnumerateEventLogSources(REGEVENTLOG,"Sources");

		fAddClassAndBindformRegistries();
		if (bNewLinkService)
			LinkToDriver(pszSNALinePath);
}


void fAddPerfmonCounters(LPSTR szComputerName)
{
	//
	// Add Perfmon Counters
	//
	BOOL		fStatus = FALSE;
	HINSTANCE   hLib;
	BOOL 		(__stdcall *pfnProc)(LPCTSTR, LPCTSTR, LPCTSTR) = NULL;
	char		szParameters[MAX_PATH];

	wsprintf(szParameters, "INSTALL %s", gMgsdlc_data.szLinkServiceServicesRegistryRoot);

	if (NULL != (hLib = ::LoadLibrary("snarpccl.dll")) )
	{
#ifdef _M_IX86
		#pragma message("Building I386 version ")
		if (NULL != (pfnProc = (BOOL(__stdcall *)(LPCTSTR, LPCTSTR, LPCTSTR))GetProcAddress( hLib, "_RunRemoteExecutable@12" )) )
#else
		if (NULL != (pfnProc = (BOOL(__stdcall *)(LPCTSTR, LPCTSTR, LPCTSTR))GetProcAddress( hLib, "RunRemoteExecutable" )) )
#endif
		{
			if (!(*pfnProc)(szComputerName, (LPCTSTR)"perfutl", szParameters))
			{
				::MessageBox(::GetActiveWindow(), 
						 "Failure returned from RPC Client DLL",
						 "",
						 MB_OK);
			}
		}
		::FreeLibrary(hLib);
	}

}

/*
**      Purpose:
**              Remove a Link Service.
**
**      Arguments: 
**
**      Notes:
**              
**
***************************************************************************/
BOOL RemoveALinkService(LPSTR szComputerName, 
					    LPSTR szLinkServiceTitle)
{
	BOOL	bReturnCode = TRUE;
	BOOL	bRemoveDeviceDriver = FALSE;
	//
	// Remove all Registry Data from the Registry
	//
	fSetupGlobalValues(szLinkServiceTitle, szComputerName, NULL);

	bStopService(gMgsdlc_data.szLinkServiceName, szComputerName);

	fReplaceAllRegistryValues(REMOVING);

	UnLinkFromDriver();
	//
	// Check to see if the IBM SDLC Device Driver can be removed.
	//
	if (bLastDigiLinkService(szComputerName))
	{
		bRemoveDeviceDriver = TRUE;
	}

	fRemoveAllRegistryValues(bRemoveDeviceDriver);

	//
	// Remove the Link Service Service
	//
	bDeleteService(szComputerName, gMgsdlc_data.szLinkServiceName);

	//
	// Remove the IBM SDLC Device Driver.  For NT5 the service is controller
	// by PNP.
	//
	if (gdwOSMajorVersion <= 4)
	{
		if (bRemoveDeviceDriver)
		{
			bDeleteService(szComputerName, gMgsdlc_data.szDeviceDriverName);
		}
	}
	
	fEnumerateEventLogSources(REGEVENTLOG,"Sources");

	return bReturnCode;
}

/*
**      Purpose:
**              Remove Link Service.
**
**      Arguments: 
**
**      Returns:
**              TRUE	if network bindings need to be recalculated
**              FALSE	if network bindings do not need to be recalculated.
**
**      Notes:
**              
**
***************************************************************************/
BOOL fRemoveLinkService(LPSTR szComputerName, 
											 LPSTR szLinkServiceTitle)
{
	BOOL	bReturnCode;

	fConnectRegistry(szComputerName);

	bReturnCode = RemoveALinkService(szComputerName, 
									 szLinkServiceTitle);

	//
	// Remove Perfmon Counters
	//
	BOOL		fStatus = FALSE;
	HINSTANCE   hLib;
	BOOL 		(__stdcall *pfnProc)(LPCTSTR, LPCTSTR, LPCTSTR) = NULL;
	char		szParameters[MAX_PATH];

	wsprintf(szParameters, "REMOVE %s", gMgsdlc_data.szLinkServiceServicesRegistryRoot);

	if (NULL != (hLib = ::LoadLibrary("snarpccl.dll")) )
	{
#ifdef _M_IX86
		if (NULL != (pfnProc = (BOOL(__stdcall *)(LPCTSTR, LPCTSTR, LPCTSTR))GetProcAddress( hLib, "_RunRemoteExecutable@12" )) )
#else
		if (NULL != (pfnProc = (BOOL(__stdcall *)(LPCTSTR, LPCTSTR, LPCTSTR))GetProcAddress( hLib, "RunRemoteExecutable" )) )
#endif
		{
			if (!(*pfnProc)(szComputerName, (LPCTSTR)"perfutl", szParameters))
			{
				::MessageBox(::GetActiveWindow(), 
						 "Failure returned from RPC Client DLL",
						 "",
						 MB_OK);
			}
		}
		::FreeLibrary(hLib);
	}
	fAddClassAndBindformRegistries();

	fDisconnectRegistry();
	
	return bReturnCode;
}



/*
**      Purpose:
**              Remove All IBM SDLC Link Services.
**
**      Arguments: 
**
**      Returns:
**              TRUE	if network bindings need to be recalculated.
**              FALSE	if network bindings do not need to be recalculated.
**
**      Notes:
**              
**
***************************************************************************/
BOOL  fRemoveAllLinkServices(LPSTR szComputerName)
{
	BOOL		bReturnCode = TRUE;
	DWORD		dCurrentIndex;
	DWORD		dSize;
	char		szLinkServiceTitle[MAX_STRING];
	char		szCurrentRegistryKey[MAX_STRING];
	char			szCommandLine[MAX_STRING];
	PROCESS_INFORMATION	PInfo;
	STARTUPINFO		SInfo = {0};
	SInfo.cb          	= sizeof( STARTUPINFO );
	SInfo.lpTitle     	= "Removing Perfmon Counters";

	fConnectRegistry(szComputerName);

	CopyMemory(&BASEMGSDLCREG, MGSDLCREG, sizeof(MGSDLCREG));

	for (dCurrentIndex = 1; dCurrentIndex <= MAX_MGATE_LINKS; dCurrentIndex++)
	{
		wsprintf(szCurrentRegistryKey, 
				 "SOFTWARE\\Microsoft\\%s%2.2d\\CurrentVersion",
				 LINK_REGISTRY_BASE,dCurrentIndex);

		if (fQueryRegistryValue(szCurrentRegistryKey, "Title", szLinkServiceTitle, &dSize))
		{
			RemoveALinkService(szComputerName, 
							   szLinkServiceTitle);
			// Remove perfmon counters
			wsprintf(szCommandLine,  "PERFUTL REMOVE %s", gMgsdlc_data.szLinkServiceServicesRegistryRoot);
			CreateProcess(
					    NULL,		
					    szCommandLine,		
					    NULL,	
					    NULL,		
					    FALSE,		
					    DETACHED_PROCESS,	
					    NULL,		
					    NULL,
					    &SInfo,	
					    &PInfo
				     );

			CopyMemory(&MGSDLCREG, BASEMGSDLCREG, sizeof(MGSDLCREG));
		}
	}

	fDisconnectRegistry();

	return bReturnCode;
}

