// Include
#include "loginsrv.h"

// Local definitions
#define SafeDelete(P)       if (P!=NULL) { delete(P);      (P)=NULL; } // memory를 안전하게 해제
#define SafeCloseHandle(P)  if (P!=NULL) { CloseHandle(P); (P)=NULL; } // handle를 안전하게 해제

#define ODBC_DSN_ACCOUNT	"IRIS_MEMBERDB"

#define ODBC_UID			"irisdb"			// local test
#define ODBC_PWD			"ehdwjq10aks!"		// local test

//#define ODBC_UID			"gamedev"			// IDC test
//#define ODBC_PWD			"qldhxpa%whdk"		// IDC test

// Global data
cLoginSrv* g_loginSrv = NULL;
bool       g_verbose    = false;
bool       g_fps        = false;
bool       g_packet     = false;


// GenerateDump Prototype
//  1. 속성 < C/C++ < 일반 < 디버깅 정보 형식 = 프로그램 데이터베이스(/Zi)
//  2. SetUnhandledExceptionFilter( Exception_Minidump );
//  3. exceptionPointers->ExceptionRecord->ExceptionCode는 winbase.h and winnt.h 파일참조
//     (EXCEPTION_ACCESS_VIOLATION ... CONTROL_C_EXIT 등)
//
//  주의: DBGHELP.DLL 파일의 버전은 최소 5.1.2600.0 이상이어야 한다.
//        (MiniDumpWriteDump 함수가 DBGHELP.DLL에 들어 있음)
#include <dbghelp.h>

#pragma comment(lib, "dbghelp.lib")

LONG __stdcall GenerateDump(EXCEPTION_POINTERS* exceptionPointers)
{
	char fileName[MAX_PATH];
	GetModuleFileName( NULL, fileName, sizeof(fileName) );

	char* ext = strrchr( fileName, '.' );
	strcpy( ext ? ext : fileName+strlen( fileName ), ".dmp" );

	char temp[256];
	wsprintf( temp, "Exception 0x%08XL arised", exceptionPointers->ExceptionRecord->ExceptionCode );
	printf( "%s[%s]\n", fileName, temp );

	HANDLE process   = GetCurrentProcess( );
	DWORD  processID = GetCurrentProcessId( );
	HANDLE file      = CreateFile( fileName, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );

	MINIDUMP_EXCEPTION_INFORMATION expParam;

	expParam.ThreadId          = GetCurrentThreadId( );
	expParam.ExceptionPointers = exceptionPointers;
	expParam.ClientPointers    = TRUE;

	MiniDumpWriteDump( process, processID, file, MiniDumpWithDataSegs, &expParam, NULL, NULL );

	if ( g_loginSrv != NULL )
	{
		char  message[2048];
		VOID* msgBuf;

		if ( ErrorCode2String( &msgBuf, exceptionPointers->ExceptionRecord->ExceptionCode ) )
		{
			sprintf( message, "Exception Code:0x%08X %s", exceptionPointers->ExceptionRecord->ExceptionCode, msgBuf );
			g_loginSrv->PostServerEvent( message );
			LocalFree( msgBuf );
		}
	}

	return EXCEPTION_EXECUTE_HANDLER;
}

// ErrorCode2String Method
//
//	오류 코드를 메시지로 변환한다.
//
//	msgBuf    :오류메시지 (사용후 LocalFree 하여 준다.)
//	lastError :오류코드
//	priLangId :primary language identifier
//	subLangId :sublanguage identifier
//
//	문자메시지 언어는 아래와 같이 설정한다.
//
//	Primary language identifier : Sublanguage identifier : Meaning 
//	LANG_NEUTRAL                  SUBLANG_NEUTRAL          Language neutral  
//	LANG_NEUTRAL                  SUBLANG_DEFAULT          User default language
//	LANG_NEUTRAL                  SUBLANG_SYS_DEFAULT      System default language 
DWORD ErrorCode2String(VOID** msgBuf, DWORD lastError, WORD priLangId, WORD subLangId)
{
	// default to system source
	HMODULE module = NULL;

	DWORD   bufferLength;
	DWORD   flags;

	flags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM;

	// If lastError is in the network range, load the message source.
	// 만약 lastError가 NERR_BASE 에서 MAX_NERR 일경우, netmsg.dll 의
	// 메시지를 사용한다. (NERR_BASE, MAX_NERR는 lmerr.h 파일에 정의)
	if ( lastError >= NERR_BASE && lastError <= MAX_NERR )
	{
		module = LoadLibraryEx( "netmsg.dll", NULL, LOAD_LIBRARY_AS_DATAFILE );
		if ( module != NULL )
		{
			flags |= FORMAT_MESSAGE_FROM_HMODULE;
		}
	}

	// If lastError is in the Exception Code range, load the message source.
	// 만약 lastError가 0xC0000000L 보다 클경우, ntdll.dll의 메시지를 사용한다.
	// (winbase.h, winnt.h 파일에 정의)
	if ( lastError > 0xC0000000L )
	{
		module = LoadLibraryEx( "ntdll.dll", NULL, LOAD_LIBRARY_AS_DATAFILE );
		if ( module != NULL )
		{
			flags |= FORMAT_MESSAGE_FROM_HMODULE;
		}
	}

	// Call FormatMessage() to allow for message text to be acquired
	// from the system or from the supplied module handle. module to
	// get message from (NULL == system)
	bufferLength = FormatMessage( flags, module, lastError, MAKELANGID(priLangId, subLangId), (LPTSTR)msgBuf, 0, NULL );

	// If we loaded a message source, unload it.
	if ( module != NULL )
	{
		FreeLibrary( module );
	}
	return bufferLength;
}

// ConsolCtrlHandler Prototype - Windows Command Processor 제어용 함수.
BOOL WINAPI ConsolCtrlHandler(DWORD opcode)
{
	if ( g_loginSrv )
		return g_loginSrv->ConsolCtrlHandler( opcode );
	else
		return FALSE;
}

// main Prototype (This method is the standard Windows program entry point.)
int main(int argc, char* argv[])
{
	SetUnhandledExceptionFilter( GenerateDump );

	cLoginSrv* loginSrv = NULL;

	loginSrv = new cLoginSrv( );
	loginSrv->ConsoleRun( argc, argv );

	SafeDelete( loginSrv );
	return 0L;
}

// ServiceCtrlHandler Prototype - Windows Service 제어용 함수.
void WINAPI ServiceCtrlHandler(DWORD opcode)
{
	if ( g_loginSrv )
		g_loginSrv->ServiceCtrlHandler( opcode );
}

// ServiceMain Prototype (This method is the entry point for a service.)
void WINAPI ServiceMain(DWORD argc, LPTSTR* argv)
{
	cLoginSrv* loginSrv = NULL;

	loginSrv = new cLoginSrv( );
	loginSrv->ServiceRun( argc, argv );

	SafeDelete( loginSrv );
}

// cLoginSrv Constructor.
cLoginSrv::cLoginSrv(void) : mSqlAccount(NULL), mSender(NULL), mRecver(NULL), mLoginProcess(NULL)
{
	// 전역 클래스 생성.
	g_loginSrv    = this;

	// 환경변수 초기.
	mCompanyName = TEXT( "EYA INTERACTIVE" );
	mServiceName = TEXT( "LoginServer" );
	mDisplayName = TEXT( "EYA INTERACTIVE - LOGIN SERVER" );
	mDescription = TEXT( "Copyright(c) 2009 EYA INTERACTIVE Corporation All rights reserved" );
	mUserName    = NULL;
	mRunService  = false;
	mVersion     = MAKEWORD(1, 0);

	memset( mServerAddr,          0, sizeof(mServerAddr)          );
	memset( mServerBroadcastRecv, 0, sizeof(mServerBroadcastRecv) );
	memset( mServerBroadcastSend, 0, sizeof(mServerBroadcastSend) );
}

// ~cLoginSrv Destructor.
cLoginSrv::~cLoginSrv(void)
{
	// cLoginProcess 클래스 해제.
	SafeDelete( mLoginProcess );

	// cRecver 클래스 해제.
	SafeDelete( mRecver );

	// cSender 클래스 해제.
	SafeDelete( mSender );

	// cSQLAccount 클래스 해제.
	SafeDelete( mSqlAccount );

	// 전역 클래스 해제.
	g_loginSrv = NULL;
}

// InstallService Method - 서비스 설치 함수.
bool cLoginSrv::InstallService(LPCTSTR binaryPathName, DWORD startType)
{
	SC_HANDLE           hManager(NULL);
	SC_HANDLE           hService(NULL);
	bool                retValue(false);

	SERVICE_DESCRIPTION scDesc;

	// Open a handle to the SC Manager database.
	hManager = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
	if ( hManager != NULL )
	{
		// The function creates a service object and adds it to the specified service control manager database.
		hService = CreateService( hManager,                  // handle to SCM database
								  mServiceName,              // name of service to start
								  mDisplayName,              // display name
								  SERVICE_ALL_ACCESS,        // type of access to service
								  SERVICE_WIN32_OWN_PROCESS, // type of service
								  startType,                 // when to start service
								  SERVICE_ERROR_NORMAL,      // severity of service failure
								  binaryPathName,            // name of binary file
								  NULL,                      // name of load ordering group
								  NULL,                      // tag identifier
								  NULL,                      // array of dependency names
								  NULL,                      // account name
								  NULL );                    // account password
		if ( hService != NULL )
		{
			// Success CreateService
			retValue = true;

			// The function changes the optional configuration parameters of a service.
			scDesc.lpDescription = mDescription;
			ChangeServiceConfig2( hService, SERVICE_CONFIG_DESCRIPTION, &scDesc );

			// Close the handle to the service.
			CloseServiceHandle( hService );
		}
		else
		{
			// 오류메시지 출력 - CreateService.
			VOID* msgBuf;
			if ( ErrorCode2String( &msgBuf ) )
			{
				printf( "\nCreateService (%d): %s\n", GetLastError( ), (char*)msgBuf );
				LocalFree( msgBuf );
			}
		}
	}
	return retValue;
}

// UninstallService Method - 서비스 삭제 함수.
bool cLoginSrv::UninstallService()
{
	SC_HANDLE hManager(NULL);
	SC_HANDLE hService(NULL);
	bool      retValue(false);

	// Open a handle to the SC Manager database.
	hManager = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
	if ( hManager != NULL )
	{
		// Open a handle to the service.
		hService = OpenService( hManager, mServiceName, SERVICE_ALL_ACCESS );
		if ( hService != NULL )
		{
			// The function marks the specified service for deletion from the service control manager database.
			if ( DeleteService( hService ) != 0 )
			{
				// Success DeleteService
				retValue = true;
			}
			else
			{
				// 오류메시지 출력 - DeleteService
				VOID* msgBuf;
				if ( ErrorCode2String( &msgBuf ) )
				{
					printf( "\nDeleteService (%d): %s\n", GetLastError( ), (char*)msgBuf );
					LocalFree( msgBuf );
				}
			}

			// Close the handle to the service.
			CloseServiceHandle( hService );
		}
		else
		{
			// 오류메시지 출력 - OpenService
			VOID* msgBuf;
			if ( ErrorCode2String( &msgBuf ) )
			{
				printf( "\nOpenService (%d): %s\n", GetLastError( ), (char*)msgBuf );
				LocalFree( msgBuf );
			}
		}
	}
	return retValue;
}

// ConsolCtrlHandler Method
BOOL cLoginSrv::ConsolCtrlHandler(DWORD opcode)
{
    switch ( opcode ) 
	{
	case CTRL_C_EVENT:
	case CTRL_BREAK_EVENT:
	case CTRL_CLOSE_EVENT:
	case CTRL_LOGOFF_EVENT:
	case CTRL_SHUTDOWN_EVENT:
		Destroy( );
		return TRUE;

	default:
		// unknown type--better pass it on.
		return FALSE;
	}
}

// ConsoleRun Method
void cLoginSrv::ConsoleRun(DWORD argc, LPTSTR* argv)
{
	char  fullPath[MAX_PATH];
	char* fileName;

	GetFullPathName( argv[0], MAX_PATH, fullPath, &fileName );

	if ( argc < 2 )
	{
		// 사용설명.
		printf( "USAGE:\n" );
		printf( "      %s [/INSTALL | /UNINSTALL | /VER | /TEST -VERBOSE -FPS -PACKET]\n\n", fileName );

		// 1. 서비스 프로세스의 메인 쓰레드를 SCM(Service Control Manager)로 넘기는 함수다.
		//    이 함수를 부른 쓰레드는 Service Control Dispatcher 쓰레드가 된다.
		//
		//	 2. 서비스 프로그램이 시작되면, SCM은 StartServiceCtrlDispatcher를 불리길 기다리게 된다.
		//     StartServiceCtrlDispatcher가 성공하면, 쓰레드는 SCM 이 관리하며, 서비스가 끝날때까지 리턴되지 않는다.
		//     실제 서비스의 시작지점은 이 함수가 넘기는 인자값인 ServiceMain 이라고 볼수 있다.
		//
		//	 !. SERVICE_TABLE_ENTRY를 등록해야만 ServiceMain이 호출 된다.
		SERVICE_TABLE_ENTRY DispatchTable[]=
		{
			{ mServiceName, (LPSERVICE_MAIN_FUNCTION)ServiceMain },
			{ NULL,         (LPSERVICE_MAIN_FUNCTION)NULL        }
		};

		printf( "StartServiceCtrlDispatcher being called.\n" );
		printf( "This may take several seconds.  Please wait.\n" );

 		if ( !StartServiceCtrlDispatcher( DispatchTable ) )
		{
			VOID* msgBuf;
			if ( ErrorCode2String( &msgBuf ) )
			{
				printf( "\nStartServiceCtrlDispatcher (%d): %s\n", GetLastError( ), (char*)msgBuf );
				LocalFree( msgBuf );
			}
		}
	}
	else if ( !_stricmp( argv[1], "/INSTALL" ) )
	{
		// 서비스로 설치를 시도합니다.
		printf( "\nInstallation to your service is initiated.\n" );

		if ( InstallService( fullPath ) )
		{
			// 서비스로 설치를 완료 하였습니다.
			printf( "\nInstallation to your service is completed.\n" );
		}
		else
		{
			// 서비스로 설치를 완료하지 못 하였습니다.
			printf( "\nInstallation to your service is not completed.\n" );
		}
	}
	else if ( !_stricmp( argv[1], "/UNINSTALL" ) )
	{
		// 서비스로 삭제를 시도합니다.
		printf( "\nDeleting your service is initiated.\n" );

		if ( UninstallService( ) )
		{
			// 서비스로 삭제를 완료 하였습니다.
			printf( "\nDeleting your service is completed.\n" );
		}
		else
		{
			// 서비스로 삭제를 완료하지 못 하였습니다.
			printf( "\nDeleting our service is not completed.\n" );
		}
	}
	else if ( !_stricmp( argv[1], "/VER" ) )
	{
		// 버전 정보 출력.
		printf( "Company Name: %s\n", mCompanyName );
		printf( "Service Name: %s\n", mServiceName );
		printf( "Display Name: %s\n", mDisplayName );
		printf( "Description : %s\n", mDescription );
		printf( "Version     : %d.%d\n", (mVersion>>8), (mVersion&0xff) );
	}
	else if ( !_stricmp( argv[1], "/TEST" ) )
	{
		DWORD status;
		DWORD error;

		if ( SetConsoleCtrlHandler( ::ConsolCtrlHandler, TRUE ) != 0 )
		{
			// 테스트 옵션 검사.
			for ( DWORD i = 2; i < argc; i++ )
			{
				if ( !_stricmp( argv[ i ], "-VERBOSE" ) )
				{
					g_verbose = true;
				}
				else if ( !_stricmp( argv[ i ], "-FPS" ) )
				{
					g_fps = true;
				}
				else if ( !_stricmp( argv[ i ], "-PACKET" ) )
				{
					g_packet = true;
				}
			}

			// 콘솔에서의 테스트.
			status = Initialization( argc, argv, &error );
			if ( status == NO_ERROR )
			{
				printf( "\nStart %s / Test\n", mDisplayName );
				Run( );
				printf( "\nStop %s / Test\n", mDisplayName );
			}
			else
			{
				printf( "\nInitialize fail %s\n", mDisplayName );
			}
			SetConsoleCtrlHandler( ::ConsolCtrlHandler, FALSE );
		}
	}
}

// ServiceCtrlHandler Method
void cLoginSrv::ServiceCtrlHandler(DWORD opcode)
{
	switch ( opcode )
	{
	case SERVICE_CONTROL_STOP:
		// 종료할 코드는 Destroy 함수를 사용.
		Destroy( );

		// Notifies a service that it should stop.
		mServiceStatus.dwWin32ExitCode = 0;
		mServiceStatus.dwCurrentState  = SERVICE_STOPPED;
		mServiceStatus.dwCheckPoint    = 0;
		mServiceStatus.dwWaitHint      = 0;
		// Updates the service control manager's status information for the calling service.
		SetServiceStatus( mServiceStatusHandle, &mServiceStatus );
		return;
	case SERVICE_CONTROL_PAUSE:
		// Notifies a service that it should pause.
		mServiceStatus.dwCurrentState = SERVICE_PAUSED;
		break;
	case SERVICE_CONTROL_CONTINUE:
		// Notifies a paused service that it should resume.
		mServiceStatus.dwCurrentState = SERVICE_RUNNING;
		break;
	case SERVICE_CONTROL_INTERROGATE:
		// Notifies a service that it should report its current status information to the service control manager.
		break;
	case SERVICE_CONTROL_SHUTDOWN:
		// Notifies a service that the system is shutting down so the service can perform cleanup tasks.
		break;
	case SERVICE_CONTROL_PARAMCHANGE:
		// Notifies a service that its startup parameters have changed. The service should reread its startup parameters.
		break;
	case SERVICE_CONTROL_NETBINDADD:
		// Notifies a network service that there is a new component for binding. The service should bind to the new component.
		break;
	case SERVICE_CONTROL_NETBINDREMOVE:
		// Notifies a network service that a component for binding has been removed.
		// The service should reread its binding information and unbind from the removed component.
		break;
	case SERVICE_CONTROL_NETBINDENABLE:
		// Notifies a network service that a disabled binding has been enabled.
		// The service should reread its binding information and add the new binding.
		break;
	case SERVICE_CONTROL_NETBINDDISABLE:
		// Notifies a network service that one of its bindings has been disabled.
		// The service should reread its binding information and remove the binding.
		break;
	}
	// Updates the service control manager's status information for the calling service.
	SetServiceStatus( mServiceStatusHandle, &mServiceStatus );
}

// ServiceRun Method
void cLoginSrv::ServiceRun(DWORD argc, LPTSTR* argv)
{
	DWORD status;
	DWORD error;

	mServiceStatus.dwServiceType             = SERVICE_WIN32;
	mServiceStatus.dwCurrentState            = SERVICE_START_PENDING;
	mServiceStatus.dwControlsAccepted        = SERVICE_ACCEPT_STOP;
	mServiceStatus.dwWin32ExitCode           = 0;
	mServiceStatus.dwServiceSpecificExitCode = 0;
	mServiceStatus.dwCheckPoint              = 0;
	mServiceStatus.dwWaitHint                = 0;

	// 서비스 제어 처리자를 등록한다. - 실패시 0를 반환 한다.
	mServiceStatusHandle = RegisterServiceCtrlHandler( mServiceName, ::ServiceCtrlHandler );
	if ( mServiceStatusHandle == (SERVICE_STATUS_HANDLE)0 )
		return;

	// Initialization code goes here.
	// - 초기화 할 코드는 ServiceInitialization 함수를 사용
	// - 실패시 NO_ERROR(0)가 아닌 값를 반환.
	status = Initialization( argc, argv, &error );	

	// Handle error condition - 초기화 실패 할 경우 종료코드(status)를 표시해 준다.
	if ( status != NO_ERROR )
	{
		mServiceStatus.dwCurrentState            = SERVICE_STOPPED;
		mServiceStatus.dwCheckPoint              = 0;
		mServiceStatus.dwWaitHint                = 0;
		mServiceStatus.dwWin32ExitCode           = status;
		mServiceStatus.dwServiceSpecificExitCode = error;

		// Updates the service control manager's status information for the calling service.
		if ( SetServiceStatus( mServiceStatusHandle, &mServiceStatus ) == FALSE )
			return;
	}

	// Initialization complete - report running status.
	// - 초기화 완료 - 서비스 상태를 동작(RUNNING)으로 설정.
	mServiceStatus.dwCurrentState = SERVICE_RUNNING;
	mServiceStatus.dwCheckPoint   = 0;
	mServiceStatus.dwWaitHint     = 0;

	// Updates the service control manager's status information for the calling service.
	if ( SetServiceStatus( mServiceStatusHandle, &mServiceStatus ) == FALSE )
		return;

	// Run - 서비스 시작.
	Run( );
}

// Initialization Method (Stub initialization function.)
// 주위: 초기화 및 생성 순서를 변경할 수 없음.
DWORD cLoginSrv::Initialization(DWORD argc, LPTSTR* argv, DWORD* error)
{
	// WINSOCK2 초기화 (버전 2.2).
	if ( LOBYTE( m_wsaData.wVersion ) != 2 || HIBYTE( m_wsaData.wVersion ) != 2 )
		return 1L;

	// 사용자 정보.
	DWORD userNameSize   = 0;
	DWORD userNameRetVal = 0;

	userNameRetVal = GetUserName( mUserName, &userNameSize );

	if ( userNameRetVal == 0 && GetLastError( ) == ERROR_INSUFFICIENT_BUFFER )
	{
		mUserName      = (TCHAR*)GlobalAlloc( GPTR, userNameSize );
		userNameRetVal = GetUserName( mUserName, &userNameSize );
	}
	if ( userNameRetVal == 0 && GetLastError( ) == ERROR_INSUFFICIENT_BUFFER )
	{
		if ( g_verbose == true )
		{
			VOID* msgBuf;
			if ( ErrorCode2String( &msgBuf ) )
			{
				printf( "\nGetUserName (%d): %s\n", GetLastError( ), (char*)msgBuf );
				LocalFree( msgBuf );
			}
		}
		return 1L;
	}

	// 호스트 정보.
	if ( GetLocalHostInfo( ) == false )
	{
		if ( g_verbose == true )
		{
			VOID* msgBuf;
			if ( ErrorCode2String( &msgBuf ) )
			{
				printf( "\nGetNetworkParams (%d): %s\n", GetLastError( ), (char*)msgBuf );
				LocalFree( msgBuf );
			}
		}
		return 1L;
	}

	// 사용자 & 호스트 정보출력.
	if ( g_verbose == true )
	{
		printf( "\n" );
		printf( "User Name............: %s \n", mUserName );
		printf( "Host Name............: %s \n", mFixedInfo->HostName );
		printf( "NetBIOS Scope ID.....: %s \n", mFixedInfo->ScopeId );
		printf( "IP Routing Enabled...: %s \n", (mFixedInfo->EnableRouting ? "YES" : "NO") );
		printf( "WINS Proxy Enabled...: %s \n", (mFixedInfo->EnableProxy ? "YES" : "NO") );
		printf( "\n" );
	}

	// 실행파일 경로 출력.
	SC_HANDLE              manager = NULL;
	SC_HANDLE              service = NULL;
	LPQUERY_SERVICE_CONFIG config  = NULL; 
	DWORD                  bytesNeeded; 

	// 환경 변수.
	TCHAR                  filename[MAX_PATH]="\0";
	TCHAR                  directory[MAX_PATH]="\0";

	// Open a handle to the SC Manager database.
	manager = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
	if ( manager != NULL )
	{
		// Open a handle to the service.
		service = OpenService( manager, mServiceName, SERVICE_ALL_ACCESS );
		if ( service != NULL )
		{
			if ( QueryServiceConfig( service, config, 0, &bytesNeeded ) == FALSE )
			{
				config = (LPQUERY_SERVICE_CONFIG)LocalAlloc( LPTR, bytesNeeded );
				QueryServiceConfig( service, config, bytesNeeded, &bytesNeeded );
			}
			if ( config != NULL )
			{
				char drive[_MAX_DRIVE];
				char dir[_MAX_DIR];
				char fname[_MAX_FNAME];
				_splitpath( config->lpBinaryPathName, drive, dir, fname, NULL );

				if ( g_verbose == true )
				{
					printf( "Binary Path Name.....: %s%s%s \n", drive, dir, fname      );
					printf( "Service Start Name...: %s \n", config->lpServiceStartName );
					printf( "Display Name.........: %s \n", config->lpDisplayName      );
				}

				sprintf( directory, "%s%s", drive, dir );
				sprintf( filename, "%s%s.cfg", directory, fname );

				LocalFree( config );
			}
			CloseServiceHandle( service );
		}
		else 1L;
	}
	else
		return 1L;

	FILE* stream = fopen( filename, "rt" );
	char  buffer[MAX_PATH];
	if ( stream != NULL )
	{
		fscanf( stream, "%s", buffer );
		if ( stricmp( buffer, "LOGIN_SERVER_ADDRESS(IPv4):" ) == 0 )
			fscanf( stream, "%s", mServerAddr );
		else
			return 1L;

		fscanf( stream, "%s", buffer );
		if ( stricmp( buffer, "LOGIN_BROADCAST_RECV(IPv4):" ) == 0 )
			fscanf( stream, "%s", mServerBroadcastRecv );
		else
			return 1L;

		fscanf( stream, "%s", buffer );
		if ( stricmp( buffer, "LOGIN_BROADCAST_SEND(IPv4):" ) == 0 )
			fscanf( stream, "%s", mServerBroadcastSend );
		else
			return 1L;

		fclose( stream );
	}
	else
		return 1L;

	/*-- ODBC-DNS & WINSOCK2 초기화 --*/

	// cSender 생성.
	mSender = new cSender( );
	if ( mSender->Initialize( mServerBroadcastSend ) == false )
		return 1L;

	// cSQLAccount 생성.
	mSqlAccount = new cSQLAccount( );
	if ( mSqlAccount->Initialize( ODBC_DSN_ACCOUNT, ODBC_UID, ODBC_PWD, 4 ) == false )
		return 1L;

	// cLoginProcess 생성.
	mLoginProcess = new cLoginProcess( );
	if ( mLoginProcess->Initialize( mServerAddr, T_LOGIN_CPORT ) == false )
		return 1L;

	// 우선순위 2. cRecver 클래스 생성.
	mRecver = new cRecver( );
	if ( mRecver->Initialize( mServerBroadcastRecv, U_LOGIN_SPORT ) == false )
		return 1L;

	return 0L;
}

// Destroy Method
// 주위: 종료 순서를 변경 할 수 없음.
void cLoginSrv::Destroy(void)
{
	// cRecver 종료.
	mRecver->Shutdown( );

	// cLoginProcess 종료.
	mLoginProcess->Shutdown( );

	// cSQLAccount 종료.
	mSqlAccount->Shutdown( );

	// cSender 종료.
	mSender->Shutdown( );

	// 메모리 해제.
	if ( mUserName != NULL )
	{
		GlobalFree( mUserName );
		mUserName = NULL;
	}

	// 실행종료.
	mRunService = false;
}

// PostServerEvent Method
bool cLoginSrv::PostServerEvent(char* message)
{
	if ( mSender != NULL )
	{
		return mSender->PostServerEvent( message );
	}
	return false;
}

// Run Method
void cLoginSrv::Run(void)
{
	DWORD currentTick = 0;
	DWORD elapsedTick = 0;

	// 서비스가 시작 됨.
	mRunService = true;
	while ( mRunService )
	{
		currentTick = GetTickCount( );

		// 지연시간 약100(ms) - CPU 과부화 방지 & 내부 동기화.
		elapsedTick = GetTickCount( ) - currentTick;
		if ( elapsedTick < 100 )
		{
			Sleep( (100 - elapsedTick) );
		}
	}
}
