#include "pch.h"
#include "AllSrvModuleIDL_i.c"
#include <AGC.h>
#include <..\TCAtl\ObjectMap.h>
#include <..\TCLib\CoInit.h>
#if !defined(ALLSRV_STANDALONE)
#include "AdminEventLoggerHook.h"
CComObjectGlobal<CAdminEventLoggerHook> g_DBLoggingHook;
#endif #ifdef _DEBUG
#include "FedSrvApp.h"
#include <AGCDebugHook.h>
CComObjectGlobal<CAGCDebugHook> g_DebugHook;
#endif static const CATID CATID_AllegianceAdmin =
{0xe1a86098,0xdd81,0x11d2,{0x8b,0x45,0x0,0xc0,0x4f,0x68,0x16,0x33}};
static const GUID APPID_AllSrv =
{0xE4E8767E,0xDFDB,0x11d2,{0x8B,0x46,0x00,0xC0,0x4F,0x68,0x16,0x33}};
static const char *c_szAPPID_AllSrv = "{E4E8767E-DFDB-11d2-8B46-00C04F681633}";
CAGCModule _AGCModule;
CServiceModule _Module;
extern Global g;
TC_OBJECT_EXTERN(CAdminSession)
TC_OBJECT_EXTERN_NON_CREATEABLE(CAdminGame)
TC_OBJECT_EXTERN_NON_CREATEABLE(CAdminGames)
TC_OBJECT_EXTERN_NON_CREATEABLE(CAdminServer)
TC_OBJECT_EXTERN_NON_CREATEABLE(CAdminUser)
TC_OBJECT_EXTERN_NON_CREATEABLE(CAdminUsers)
TC_OBJECT_EXTERN_NON_CREATEABLE(CAdminShip)
BEGIN_OBJECT_MAP(ObjectMap)
TC_OBJECT_ENTRY_STD(AdminSession)
TC_OBJECT_ENTRY_STD_NON_CREATEABLE(AdminGame)
TC_OBJECT_ENTRY_STD_NON_CREATEABLE(AdminGames)
TC_OBJECT_ENTRY_STD_NON_CREATEABLE(AdminServer)
TC_OBJECT_ENTRY_STD_NON_CREATEABLE(AdminUser)
TC_OBJECT_ENTRY_STD_NON_CREATEABLE(AdminUsers)
TC_OBJECT_ENTRY_STD_NON_CREATEABLE(AdminShip)
END_OBJECT_MAP()
#ifdef SHOW_SPONSOR_INFO
int cSponsor = 0; int cLimb = 0;
CDebugClass DebugClass;
#endif
void PrintSystemErrorMessage(LPCTSTR szText, DWORD dwErrorCode)
{
LPVOID lpMsgBuf;
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dwErrorCode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0,
NULL
);
printf("%s", (LPCTSTR)szText);
printf("%s", (LPCTSTR)lpMsgBuf);
_AGCModule.TriggerEvent(NULL, AllsrvEventID_SystemError, "", -1, -1, -1, 2,
"Text", VT_LPSTR, szText,
"Message", VT_LPSTR, lpMsgBuf);
LocalFree( lpMsgBuf );
}
BOOL CServiceModule::IsInstalled()
{
CRegKey keyAppID;
LONG lRes = keyAppID.Open(HKEY_CLASSES_ROOT, _T("AppID"), KEY_READ);
if (lRes != ERROR_SUCCESS)
return FALSE;
CRegKey key;
lRes = key.Open(keyAppID, c_szAPPID_AllSrv, KEY_READ);
if (lRes != ERROR_SUCCESS)
return FALSE;
return TRUE;
}
BOOL CServiceModule::IsInstalledAsService()
{
if (IsWin9x())
return false;
CRegKey keyAppID;
LONG lRes = keyAppID.Open(HKEY_CLASSES_ROOT, _T("AppID"), KEY_READ);
if (lRes != ERROR_SUCCESS)
return FALSE;
CRegKey key;
lRes = key.Open(keyAppID, c_szAPPID_AllSrv, KEY_READ);
if (lRes != ERROR_SUCCESS)
return FALSE;
TCHAR szValue[_MAX_PATH];
DWORD dwLen = _MAX_PATH;
lRes = key.QueryValue(szValue, _T("LocalService"), &dwLen);
if (lRes == ERROR_SUCCESS)
return TRUE;
return FALSE;
}
BOOL CServiceModule::IsInServiceControlManager()
{
if (IsWin9x())
return false;
BOOL bResult = FALSE;
SC_HANDLE hSCM = ::OpenSCManager(NULL, NULL, GENERIC_READ);
if (hSCM != NULL)
{
SC_HANDLE hService = ::OpenService(hSCM, c_szSvcName, SERVICE_QUERY_CONFIG);
if (hService != NULL)
{
bResult = TRUE;
::CloseServiceHandle(hService);
}
::CloseServiceHandle(hSCM);
}
return bResult;
}
HRESULT CServiceModule::Init(HINSTANCE hInst)
{
HRESULT hr;
ZSucceeded(hr = CComModule::Init(ObjectMap, hInst, &LIBID_ALLEGIANCESERVERLib));
RETURN_FAILED(hr);
return S_OK;
}
void CServiceModule::Term()
{
CComModule::Term();
}
HRESULT CServiceModule::InitAGC()
{
HRESULT hr;
ZSucceeded(hr = _AGCModule.Init());
RETURN_FAILED(hr);
_AGCModule.SetDebugBreakOnErrors(g.fWantInt3);
#ifdef _DEBUG
_AGCModule.GetAGCGlobal()->SetDebugHook(&g_DebugHook);
#endif CComPtr<IAGCEventIDRanges> spRanges;
ZSucceeded(hr = spRanges.CoCreateInstance(L"AGC.EventIDRanges"));
RETURN_FAILED(hr);
ZSucceeded(spRanges->AddByValues(EventID_AGC_LowerBound, EventID_AGC_UpperBound));
ZSucceeded(spRanges->AddByValues(AllsrvEventID_Allsrv_LowerBound, AllsrvEventID_Allsrv_UpperBound));
ZSucceeded(spRanges->AddByValues(EventID_Admin_LowerBound, EventID_Admin_UpperBound));
GetAGCGlobal()->SetAvailableEventIDRanges(spRanges);
ZSucceeded(hr = m_spEventLogger.CreateInstance("AGC.EventLogger"));
RETURN_FAILED(hr);
CComBSTR bstrEventSource(__MODULE__);
CComBSTR bstrRegKey("HKLM\\" HKLM_FedSrv);
IAGCEventLoggerPrivatePtr spPrivate(m_spEventLogger);
ZSucceeded(hr = spPrivate->Initialize(bstrEventSource, bstrRegKey));
RETURN_FAILED(hr);
#if !defined(ALLSRV_STANDALONE)
{
ZSucceeded(hr = spPrivate->put_HookForDBLogging(&g_DBLoggingHook));
}
#endif return S_OK;
}
void CServiceModule::TermAGC()
{
if (NULL != m_spEventLogger)
{
IAGCEventLoggerPrivatePtr spPrivate(m_spEventLogger);
assert(NULL != spPrivate);
ZSucceeded(spPrivate->Terminate());
#if !defined(ALLSRV_STANDALONE)
{
ZSucceeded(spPrivate->put_HookForDBLogging(NULL));
}
#endif spPrivate = NULL;
m_spEventLogger = NULL;
}
_AGCModule.Term();
}
HRESULT CServiceModule::get_EventLog(IAGCEventLogger** ppEventLogger)
{
CLEAROUT(ppEventLogger, (IAGCEventLogger*)NULL);
(*ppEventLogger = m_spEventLogger)->AddRef();
return S_OK;
}
void CServiceModule::RegisterCOMObjects()
{
assert(m_shevtMTAReady.IsNull());
assert(m_shevtMTAExit.IsNull());
assert(m_shthMTA.IsNull());
m_shevtMTAReady = CreateEvent(NULL, false, false, NULL);
m_shevtMTAExit = CreateEvent(NULL, false, false, NULL);
assert(!m_shevtMTAReady.IsNull());
assert(!m_shevtMTAExit.IsNull());
DWORD dwID = 0;
m_shthMTA = CreateThread(NULL, 8192, MTAKeepAliveThunk, this, 0, &dwID);
assert(!m_shthMTA.IsNull());
WaitForSingleObject(m_shevtMTAReady, INFINITE);
m_shevtMTAReady = NULL;
if (FAILED(m_hrMTAKeepAlive))
PrintSystemErrorMessage("Failed to register COM class objects for Admin Session.", m_hrMTAKeepAlive);
ZSucceeded(m_hrMTAKeepAlive);
}
void CServiceModule::RevokeCOMObjects()
{
assert(m_shevtMTAReady.IsNull());
assert(!m_shevtMTAExit.IsNull());
assert(!m_shthMTA.IsNull());
assert(WAIT_TIMEOUT == WaitForSingleObject(m_shthMTA, 0));
SetEvent(m_shevtMTAExit);
HANDLE hth = m_shthMTA;
while (WAIT_OBJECT_0 != MsgWaitForMultipleObjects(1, &hth, false, INFINITE, QS_ALLINPUT))
{
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, true))
DispatchMessage(&msg);
}
m_shthMTA = NULL;
m_shevtMTAExit = NULL;
}
#ifdef MONOLITHIC_DPLAY
void RegisterMonolithicDPlay()
{
HKEY hKey = NULL;
const char * szKey = "CLSID\\{DA9CABC6-C724-4265-A61D-6E78EB2042B4}\\InprocServer32";
if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_CLASSES_ROOT, szKey, 0, KEY_READ, &hKey))
{
TCHAR szFilePath[_MAX_PATH * 2], szDrive[_MAX_DRIVE], szDir[_MAX_DIR * 2];
::GetModuleFileName(NULL, szFilePath, sizeofArray(szFilePath));
_tsplitpath(szFilePath, szDrive, szDir, NULL, NULL);
_tmakepath(szFilePath, szDrive, szDir, NULL, NULL);
int cch = _tcslen(szFilePath);
if (TEXT('\\') == szFilePath[cch - 1])
szFilePath[cch - 1] = TEXT('\0');
USES_CONVERSION;
_ATL_REGMAP_ENTRY regmap[] =
{
{L"MODULE_PATH", T2COLE(szFilePath)},
{NULL , NULL},
};
_Module.UpdateRegistryFromResource(IDR_DPMONO, true, regmap);
}
else
{
RegCloseKey(hKey);
}
}
#endif
HRESULT CServiceModule::RegisterServer(BOOL bReRegister, BOOL bRegTypeLib, BOOL bService, int argc, char * argv[])
{
TCCoInit init(COINIT_MULTITHREADED);
if (init.Failed())
return init;
HRESULT hr = _Module.Init(g.hInst); ZSucceeded(hr);
if (IsWin9x())
bService = false; if (IsInServiceControlManager() && !bReRegister)
RemoveService();
_Module.UpdateRegistryFromResource(IDR_AllSrv, true,
_AGCModule.GetRegMapEntries());
#ifdef MONOLITHIC_DPLAY
RegisterMonolithicDPlay();
#endif
CComPtr<ICatRegister> spCatReg;
hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr, NULL, CLSCTX_ALL,
IID_ICatRegister, (void**)&spCatReg);
ZSucceeded(hr);
if (SUCCEEDED(hr))
{
const LANGID langid = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
const LCID lcid = MAKELCID(langid, SORT_DEFAULT);
CATEGORYINFO catinfo;
catinfo.catid = CATID_AllegianceAdmin;
catinfo.lcid = lcid;
wcscpy(catinfo.szDescription, L"Allegiance Admin Objects");
hr = spCatReg->RegisterCategories(1, &catinfo);
ZSucceeded(hr);
spCatReg = NULL;
}
if(!bReRegister)
{
CRegKey keyAppID;
DWORD lRes = keyAppID.Open(HKEY_CLASSES_ROOT, _T("AppID"), KEY_WRITE);
if (lRes != ERROR_SUCCESS)
return lRes;
CRegKey key;
lRes = key.Open(keyAppID, _T("{E4E8767E-DFDB-11d2-8B46-00C04F681633}"), KEY_WRITE);
if (lRes != ERROR_SUCCESS)
return lRes;
key.DeleteValue(_T("LocalService"));
if (bService)
{
key.SetValue(__MODULE__, _T("LocalService"));
key.SetValue(_T("-Service"), _T("ServiceParameters"));
InstallService(argc, argv);
}
}
hr = _Module.CComModule::RegisterServer(bRegTypeLib);
_Module.Term();
return hr;
}
HRESULT CServiceModule::UnregisterServer()
{
TCCoInit init(COINIT_MULTITHREADED);
if (init.Failed())
return init;
HRESULT hr = _Module.Init(g.hInst); ZSucceeded(hr);
CComPtr<ICatRegister> spCatReg;
hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr, NULL, CLSCTX_ALL,
IID_ICatRegister, (void**)&spCatReg);
ZSucceeded(hr);
if (SUCCEEDED(hr))
{
CATID catid = CATID_AllegianceAdmin;
spCatReg->UnRegisterCategories(1, &catid);
spCatReg = NULL;
}
UpdateRegistryFromResource(IDR_AllSrv, FALSE);
if (IsInServiceControlManager())
{
RemoveService();
}
CComModule::UnregisterServer(TRUE);
_Module.Term();
return hr;
}
BOOL CServiceModule::InstallService(int argc, char * argv[])
{
SC_HANDLE schMgr;
SC_HANDLE schSvc;
char szPath[512];
char * szUserName;
char * szPassword;
schMgr = OpenSCManager(NULL,NULL,SC_MANAGER_CREATE_SERVICE);
if (!schMgr)
{
printf("Unable to open SCManager. Service not installed.\n");
return FALSE;
}
GetModuleFileName(NULL,szPath,sizeof(szPath));
if (argc > 3)
{
szUserName = argv[2];
szPassword = argv[3];
} else
{
szUserName = NULL;
szPassword = NULL;
}
schSvc = CreateService(schMgr,
c_szSvcName,
"MS Allegiance Game Server",
SERVICE_ALL_ACCESS,
SERVICE_WIN32_OWN_PROCESS,
SERVICE_AUTO_START,
SERVICE_ERROR_NORMAL,
szPath,
NULL,
NULL,
_T("RPCSS\0"),
szUserName,
szPassword);
if (!schSvc)
{
char szBuf[MAX_PATH];
DWORD dwErrorCode(GetLastError());
sprintf(szBuf, "Unable to create service [0x%08x]. Service not installed.\n", dwErrorCode);
PrintSystemErrorMessage(szBuf, dwErrorCode);
CloseServiceHandle(schMgr);
return FALSE;
}
CloseServiceHandle(schSvc);
CloseServiceHandle(schMgr);
printf("%s service installed.\n", c_szSvcName);
if (szUserName)
{
TCUserAccount acct;
acct.Init(szUserName); if (S_OK != acct.HasRight(SE_SERVICE_LOGON_NAME))
{
acct.SetRight(SE_SERVICE_LOGON_NAME);
printf("The account %ls\\%ls has been granted the Logon As A Service right.", acct.GetDomainNameW(), acct.GetUserNameW());
}
}
return TRUE;
}
BOOL CServiceModule::RemoveService(void)
{
SC_HANDLE schMgr;
SC_HANDLE schSvc;
SERVICE_STATUS ss;
schMgr = OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);
if (!schMgr)
{
printf("Unable to open SCManager. Service not removed.\n");
return FALSE;
}
schSvc = OpenService(schMgr, c_szSvcName, SERVICE_ALL_ACCESS);
if (!schSvc)
{
printf("Unable to open %s service. Service not removed.\n", c_szSvcName);
CloseServiceHandle(schMgr);
return FALSE;
}
QueryServiceStatus(schSvc,&ss);
if (ss.dwCurrentState!=SERVICE_STOPPED)
{
printf("Unable to remove %s service while it is running.\n", c_szSvcName);
CloseServiceHandle(schSvc);
CloseServiceHandle(schMgr);
return FALSE;
}
if (DeleteService(schSvc))
printf("%s removed (as an NT Service).\n", c_szSvcName);
else
{
char szBuf[MAX_PATH];
sprintf(szBuf, "Unable to delete %s service.\n", c_szSvcName);
DWORD dwErrorCode(GetLastError());
PrintSystemErrorMessage(szBuf, dwErrorCode);
}
CloseServiceHandle(schSvc);
CloseServiceHandle(schMgr);
return TRUE;
}
BOOL WINAPI HandlerRoutine(
DWORD dwCtrlType )
{
if (dwCtrlType == CTRL_CLOSE_EVENT)
{
SetEvent(g.hKillReceiveEvent);
return TRUE;
}
return FALSE;
}
void RunAsWindow()
{
HWND hwnd = ::CreateWindow("static", "AllSrv", WS_OVERLAPPED,
0, 0, 0, 0, NULL, NULL, ::GetModuleHandle(NULL), NULL);
HICON hIcon = reinterpret_cast<HICON>(::LoadImage(::GetModuleHandle(NULL),
MAKEINTRESOURCE(ICO_FEDSRV), IMAGE_ICON, ::GetSystemMetrics(SM_CXSMICON),
::GetSystemMetrics(SM_CYSMICON), 0));
const UINT wm_ShellNotifyIcon = WM_APP;
NOTIFYICONDATA nid = {sizeof(nid), hwnd, 0, NIF_ICON | NIF_MESSAGE | NIF_TIP,
wm_ShellNotifyIcon, hIcon, "Allegiance Server"};
Shell_NotifyIcon(NIM_ADD, &nid);
while (true)
{
DWORD dwWait = ::MsgWaitForMultipleObjects(1, &g.hKillReceiveEvent,
false, INFINITE, QS_ALLINPUT);
if (WAIT_OBJECT_0 == dwWait)
break;
MSG msg;
while (::PeekMessage(&msg, NULL, 0, 0, true))
{
::TranslateMessage(&msg);
switch (msg.message)
{
case WM_QUIT:
::SetEvent(g.hKillReceiveEvent);
break;
default:
::DispatchMessage(&msg);
}
}
}
Shell_NotifyIcon(NIM_DELETE, &nid);
FedSrv_Terminate();
}
VOID CServiceModule::RunAsExecutable()
{
HRESULT hr;
ZVersionInfo vi;
printf("%s\n%s\n\n",
(LPCSTR)vi.GetFileDescription(), (LPCSTR)vi.GetLegalCopyright());
if (IsWinNT())
printf("Running as an executable.\n");
printf("Initializing...\n");
hr = FedSrv_Init();
#if !defined(_CONSOLE)
if (SUCCEEDED(hr))
{
RunAsWindow();
return;
}
#endif
if (SUCCEEDED(hr))
{
HANDLE hConsole;
printf("\rType 'Q' to Quit.\n");
printf("\rType 'P' to Pause.\n");
printf("\rType 'C' to Continue.\n");
hConsole = GetStdHandle(STD_INPUT_HANDLE);
SetConsoleMode(hConsole,0);
SetConsoleCtrlHandler(HandlerRoutine, true);
assert(g.hKillReceiveEvent);
HANDLE hEventArray[] = { g.hKillReceiveEvent, hConsole };
DWORD dwAwaker;
do
{
dwAwaker = MsgWaitForMultipleObjects(2, hEventArray, FALSE, INFINITE, 0);
if (dwAwaker == WAIT_OBJECT_0 + 1)
{
INPUT_RECORD Key;
DWORD dwRead;
ReadConsoleInput(hConsole, &Key, 1, &dwRead);
if (Key.EventType == KEY_EVENT &&
Key.Event.KeyEvent.wVirtualKeyCode == 'Q')
{
debugf("Q pressed\n");
SetEvent(g.hKillReceiveEvent);
}
if (Key.EventType == KEY_EVENT &&
Key.Event.KeyEvent.wVirtualKeyCode == 'P')
{
debugf("P pressed\n");
FedSrv_Pause();
}
if (Key.EventType == KEY_EVENT &&
Key.Event.KeyEvent.wVirtualKeyCode == 'C')
{
debugf("C pressed\n");
FedSrv_Continue();
}
}
} while (dwAwaker != WAIT_OBJECT_0);
FedSrv_Terminate();
}
else
{
printf("\rInitialization failed. (%x)\n", hr);
}
}
void CServiceModule::SetSvcStatus(
DWORD state,
DWORD exitcode
)
{
if (g.ssh)
{
SERVICE_STATUS ss;
static DWORD s_cp = 1;
ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
ss.dwCurrentState = state;
ss.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;
ss.dwWin32ExitCode = exitcode;
ss.dwServiceSpecificExitCode = 0;
switch (state)
{
case SERVICE_RUNNING:
case SERVICE_STOPPED:
case SERVICE_PAUSED:
ss.dwCheckPoint = s_cp++;
ss.dwWaitHint = 1000;
break;
case SERVICE_START_PENDING:
ss.dwCheckPoint = s_cp++;
ss.dwWaitHint = 30000; break;
case SERVICE_STOP_PENDING:
ss.dwCheckPoint = s_cp++;
ss.dwWaitHint = 30000; break;
default:
ss.dwCheckPoint = 0;
ss.dwWaitHint = 0;
break;
}
SetServiceStatus(g.ssh,&ss);
}
}
DWORD WINAPI CServiceModule::ServerTerminateThread(DWORD dwUnused)
{
FedSrv_Terminate();
SetSvcStatus(SERVICE_STOPPED,0);
return 0;
}
void WINAPI CServiceModule::StartServerTerminateThread()
{
DWORD dwId;
HANDLE hthrPending = CreateThread(NULL, 8192, (LPTHREAD_START_ROUTINE) ServerTerminateThread, 0, 0, &dwId);
CloseHandle(hthrPending);
}
void WINAPI CServiceModule::ServiceControl(DWORD dwCode)
{
switch (dwCode)
{
case SERVICE_CONTROL_STOP:
SetSvcStatus(SERVICE_STOP_PENDING, 0);
StartServerTerminateThread();
break;
case SERVICE_CONTROL_PAUSE:
FedSrv_Pause();
SetSvcStatus(SERVICE_PAUSED, 0);
break;
case SERVICE_CONTROL_CONTINUE:
FedSrv_Continue();
SetSvcStatus(SERVICE_RUNNING, 0);
break;
case SERVICE_CONTROL_SHUTDOWN:
break;
case SERVICE_CONTROL_INTERROGATE:
break;
}
}
void WINAPI CServiceModule::ServiceMain(
DWORD dwArgc,
LPSTR *lpszArgv)
{
HRESULT hr;
g.ssh = RegisterServiceCtrlHandler(c_szSvcName, ServiceControl);
if (!g.ssh)
{
SetSvcStatus(SERVICE_STOPPED,GetLastError());
return;
}
SetSvcStatus(SERVICE_START_PENDING,NO_ERROR);
hr = FedSrv_Init();
if (SUCCEEDED(hr))
SetSvcStatus(SERVICE_RUNNING,NO_ERROR);
else
SetSvcStatus(SERVICE_STOPPED,GetLastError());
}
void WINAPI _ServiceMain(DWORD dwArgc, LPTSTR* lpszArgv)
{
_Module.ServiceMain(dwArgc, lpszArgv);
}
void CServiceModule::StopAllsrv()
{
if(_Module.IsInstalledAsService())
_Module.ServiceControl(SERVICE_CONTROL_STOP);
else
{
if(g.hKillReceiveEvent)
SetEvent(g.hKillReceiveEvent);
}
}
DWORD WINAPI CServiceModule::MTAKeepAliveThunk(void* pvThis)
{
CServiceModule* pThis = reinterpret_cast<CServiceModule*>(pvThis);
pThis->MTAKeepAliveThread();
return 0;
}
void CServiceModule::MTAKeepAliveThread()
{
TCCoInit init(COINIT_MULTITHREADED);
m_hrMTAKeepAlive = init;
if (init.Succeeded())
m_hrMTAKeepAlive = _Module.RegisterClassObjects(CLSCTX_LOCAL_SERVER,
REGCLS_MULTIPLEUSE);
assert(!m_shevtMTAReady.IsNull());
SetEvent(m_shevtMTAReady);
RETURN_FAILED_VOID(m_hrMTAKeepAlive);
assert(!m_shevtMTAExit.IsNull());
WaitForSingleObject(m_shevtMTAExit, INFINITE);
CAdminSession::DestroyAllSessions();
_Module.RevokeClassObjects();
}