//  Beta release only.
//
//  Copyright(c) Ivyware Pty Ltd  2009-13  (all rights reserved)
//               MELBOURNE, VICTORIA, AUSTRALIA, 3130
//
//  P2PeerService definitions and prototypes
//  NOTES: Windows service container for the deployment of P2PeerHub's
//         P2PeerMsg's between objects derived from this base class
//       : Only P2PeerHub derived network objects may be assigned
//         network P2Paddress's

#include "stdafx.h"

#include <windows.h>
#include "P2Pwin32.h"
#include "P2PeerService.h"
#include "P2Pevent.h"

using namespace std;

//
//  ServiceStart() definitions
/*VOID WINAPI
P2PeerService_ServiceStart1 ( DWORD argc, LPTSTR *argv )
{
    // Firstly globals
    CexGlobalsInit ( );

    // Delegate
    MscsServiceStart ( argc, argv );
}*/
//extern
//P2PeerService *g_pP2PeerService1;
//VOID WINAPI
//P2PeerService_ServiceStart2 ( DWORD argc, LPTSTR *argv );
//extern
//P2PeerService *g_pP2PeerService2;
//VOID WINAPI
//P2PeerService_ServiceStart3 ( DWORD argc, LPTSTR *argv );
//extern
//P2PeerService *g_pP2PeerService3;

//
//  Win32 Service Control handler
//  NOTES: Default implementation template 
//
//  Parameters: DWORD dwCtrlCode
//              Control code as passed to the registered handler
//              Refer RegisterServiceCtrlHandler() for further
//              details
P2PeerService *g_pP2PeerService = 0;
VOID WINAPI
Win32ServiceServiceMain ( DWORD argc, LPTSTR *argv )
{
    // Delegate to global P2PeerService instance
    g_pP2PeerService -> Service ( argc, argv );
}
VOID WINAPI
Win32ServiceControlHandler ( DWORD dwCtrlCode )
{
    // Delegate to global P2PeerService instance
    g_pP2PeerService -> ServiceCtrlHandler ( dwCtrlCode );
}

///////////////////////////////////////////////////////////////////////////////
//  Contructors and destructor
P2PeerService::P2PeerService ( LPCTSTR lpszServiceName 
                             , LPSERVICE_MAIN_FUNCTION fptrServiceMain 
                             , LPHANDLER_FUNCTION fptrServiceCtrlHandler )
{
    // Firstly
    RenderThisSafe ( );
    m_fptrServiceMain        = fptrServiceMain;
    m_fptrServiceCtrlHandler = fptrServiceCtrlHandler;
   _tcscpy_s ( m_szServiceName, sizeof(m_szServiceName), lpszServiceName );

    // Customised function pointers
    //m_pServiceRun = 0;
}

P2PeerService::~P2PeerService( void )
{
    // Garbage collection
    if ( m_pP2PeerHub )
      delete m_pP2PeerHub;
}

void
P2PeerService::RenderThisSafe ( )
{
    m_pP2PeerHub     =  0;
    m_nMaxHubs       = 16;             // Within service address space
    memset(  m_szServiceName, 0, sizeof(m_szServiceName) );
    memset( &m_oServiceStatus, 0, sizeof(m_oServiceStatus) );
    memset( &m_oDispatchTable[0], 0, sizeof(m_oDispatchTable) );
    m_hServiceStatus =  0;

    // Interbal status and mode flags
    m_bStarted  = false;
    m_bPaused   = false;
    m_bConsole  = false;
    m_bSystem   = false;
    m_bService  = false;
    m_bExitMode = false;

    // Default service control status
    m_oServiceStatus.dwServiceType      = SERVICE_WIN32; 
    m_oServiceStatus.dwCurrentState     = SERVICE_START_PENDING; 
    m_oServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP 
                                        | SERVICE_ACCEPT_PAUSE_CONTINUE
                                        | SERVICE_ACCEPT_SHUTDOWN; 
}

//
//  Connects the main thread of a service process to the service
//  control manager, which causes the thread to be the service control
//  dispatcher thread for the calling process. 
//  NOTES: Delegates to StartServiceCtrlDispatcher()
//
//
//  Parameters: const SERVICE_TABLE_ENTRY *pServiceTable
//              Refer StartServiceCtrlDispatcher() for further details
//
DWORD
P2PeerService::StartDispatcher ( const SERVICE_TABLE_ENTRY *pServiceTable )
{
    // Direct delegation
    ASSERT ( IsServiceMode() );
    BOOL bResult = StartServiceCtrlDispatcher ( pServiceTable );
    if ( !bResult )
      EVERR->MODULE
           ->Message("StartServiceCtrlDispatcher() failed" )
           ->HResult( GetLastError() )
           ->SetLast( );
    return bResult;
}

DWORD P2PeerService::Startup( void )
{
    //== initialize the dispatch table
    m_oDispatchTable[0].lpServiceName = m_szServiceName; 
    m_oDispatchTable[0].lpServiceProc = m_fptrServiceMain;

    //== starts the service 
    if( !StartServiceCtrlDispatcher( m_oDispatchTable ) )
      return GetLastError(); //TODO:LJM full reporting

    return NO_ERROR;
}

int P2PeerService::Service ( DWORD argc, LPTSTR* argv )
{
    m_hServiceStatus
      = RegisterServiceCtrlHandler ( m_szServiceName
                                   , m_fptrServiceCtrlHandler );
    if( (SERVICE_STATUS_HANDLE)0 == m_hServiceStatus )
    {
        return GetLastError(); //TODO:LJM full reporting
    }
    
    if( Init( argc, argv ) != NO_ERROR )
    {
        ChangeStatus( SERVICE_STOPPED );
        return GetLastError();
    }
    
    ChangeStatus( SERVICE_RUNNING );
    return Run ( );
}

//
//  Service control handler
//  NOTES: Default handler for all P2PeerService derived objects
//
//
//  Parameters:  DWORD dwOpcode
//               Operations code passed from the service control
//               manager.
//
void
P2PeerService::ServiceCtrlHandler ( DWORD dwOpcode )
{
    switch ( dwOpcode )
    {
      case SERVICE_CONTROL_PAUSE:
        ChangeStatus( SERVICE_PAUSE_PENDING );
        if ( OnPause() == NO_ERROR )
        {
          m_bPaused = true;
          ChangeStatus( SERVICE_PAUSED );
        }
        break;

      case SERVICE_CONTROL_CONTINUE:
        ChangeStatus( SERVICE_CONTINUE_PENDING );
        if ( OnContinue() == NO_ERROR )
        {
          m_bPaused = false;
          ChangeStatus( SERVICE_RUNNING );
        }
        break;

      case SERVICE_CONTROL_STOP:
        ChangeStatus( SERVICE_STOP_PENDING );
        OnStop();
        ChangeStatus( SERVICE_STOPPED );
        break;

      case SERVICE_CONTROL_SHUTDOWN:
        ChangeStatus( SERVICE_STOP_PENDING );
        OnShutdown();
        ChangeStatus( SERVICE_STOPPED );
        break;

      case SERVICE_CONTROL_INTERROGATE:
        OnInterrogate();
        SetServiceStatus( m_hServiceStatus, &m_oServiceStatus );
        break;

      default:
        OnUserControl( dwOpcode );
        SetServiceStatus( m_hServiceStatus, &m_oServiceStatus );
        break;
    };
}

//
//  Checks for and processes default service arguments
//  NOTES: This default argument set defines the default P2PeerService
//         environment
//
//  Parameters: LPTSTR *argv
//              Argument to be processed
//
//              bool bImplement
//                true... Implement decoded option
//                false.. Validation and object synchronisation
bool
P2PeerService::DefaultMainArgs ( LPTSTR argv[], DWORD& argn, bool bImplement )
{
    // Locals
    bool bResult = false;
    int  a = argn, b = argn;
    int  iSize   = strlen ( argv[a] );

    // Because users are involved
    try
    {
      // Debug mode
      if ( _tcsnicmp ( argv[a], _T("-Debug"), max(2,iSize) ) == 0 ||
           _tcsnicmp ( argv[a], _T("/Debug"), max(2,iSize) ) == 0    )
      {
        m_bDebug = true;
        if ( bImplement )
          P2Pevent::Configure ( P2Pevent::ADDMASK
                              , P2Pevotn_ERROR | P2Pevotn_DEBUG );
        bResult = true;
      }

      // Console mode
      // NOTES: Flags execution from Visual Studio or from a DOS Window,
      //        negates StartServiceCtrlDispatcher() call
      //      : Implementation -C[onsole] or /C[onsole]
      else if ( _tcsnicmp ( argv[a], _T("-Console"), max(2,iSize) ) == 0 ||
                _tcsnicmp ( argv[a], _T("/Console"), max(2,iSize) ) == 0    )
      {
        m_bConsole = true;
        bResult    = true;
      }

      // Service mode
      // NOTES: Flags execution as a formal service for which a
      //        StartServiceCtrlDispatcher() call is ultimately performed
      //      : Implementation -S[ervice] or /S[ervice]
      else if ( _tcsnicmp ( argv[a], _T("-Service"), max(2,iSize) ) == 0 ||
                _tcsnicmp ( argv[a], _T("/Service"), max(2,iSize) ) == 0    )
      {
        m_bService = true;
        bResult    = true;
      }

      // Register as service
      // NOTES: Activates system mode and negates operation as a service
      else if ( _tcsnicmp ( argv[a], _T("-Install"),   max(2,iSize) ) == 0 ||
                _tcsnicmp ( argv[a], _T("/Install"),   max(2,iSize) ) == 0 ||
                _tcsnicmp ( argv[a], _T("/REGSERVER"), max(4,iSize) ) == 0    )
      {
        if ( bImplement )
          Install ( );
        m_bSystem = true;
        bResult   = true;
      }

      // Unregister as service
      // NOTES: /REGSERVER is a special case in which the
      //        service is automatically removed
      //      : Activates system mode and negates operation as a service
      else if ( _tcsnicmp ( argv[a], _T("-Remove"),      max(2,iSize) ) == 0 ||
                _tcsnicmp ( argv[a], _T("/Remove"),      max(2,iSize) ) == 0 ||
                _tcsnicmp ( argv[a], _T("/UNREGSERVER"), max(2,iSize) ) == 0    )
      {
        if ( bImplement )
          UnInstall ( );
        m_bSystem = true;
        bResult   = true;
      }

      // Exit mode
      // NOTES: Used for setting registry parameters and negates operation
      //        as either a console application or service
      else if ( _tcsnicmp ( argv[a], _T("-Xit"),  max(2,iSize) ) == 0 ||
                _tcsnicmp ( argv[a], _T("/Xit"),  max(2,iSize) ) == 0 ||
                _tcsnicmp ( argv[a], _T("-eXit"), max(3,iSize) ) == 0 ||
                _tcsnicmp ( argv[a], _T("/eXit"), max(3,iSize) ) == 0    )
      {
        m_bExitMode = true;
        bResult     = true;
      }
    }

    // Exceptions
    catch ( P2Pevent *pEVT )
    {
      pEVT -> SetLast();
    }
    catch ( ... )
    {
      EVERR->Module ( __FUNCTION__ )
           ->Message("Unknown exception")
           ->SetLast();
    }

    // Tidy up and
    if ( bResult )
      argn = b;                        // Comsumed arguments
    return bResult;                    // Consumed arguments
}

//
//  Checks for and processes -Help service arguments
//  NOTES: The contained argument set defines the default P2PeerService
//         -Help environment
//
//  Parameters: LPTSTR argv[]
//              Argument to be processed
//
//              DWORD& argn
//              Primary argument to be checked
//
//              bool bImplement
//                true... Implement decoded option
//                false.. Validation and object synchronisation
bool
P2PeerService::HelpMainArgs ( LPTSTR argv[], DWORD& argn, bool bImplement )
{
    // Locals
    bool bResult = false;
    int  a = argn, b = argn;
    int  iSize   = strlen ( argv[a] );

    // Because users are involved
    try
    {
      // Help
      // NOTES: Simple TTY expression of default set of execution arguments
      if ( _tcsnicmp ( argv[a], _T("-Help"), max(2,iSize) ) == 0 ||
           _tcsnicmp ( argv[a], _T("/Help"), max(2,iSize) ) == 0 ||
           _tcsnicmp ( argv[a], _T("-?"), 2 )               == 0 ||
           _tcsnicmp ( argv[a], _T("/?"), 2 )               == 0    )
      {
        m_bConsole = true;
        if ( bImplement )
        {
          _tprintf ( _T("\n") );
          _tprintf ( _T("  -C[onsole]\n") );
          _tprintf ( _T("   Run from a DOS window or Visual Studio\n") );
          _tprintf ( _T("  -S[ervice]\n") );
          _tprintf ( _T("   Run as a Windows service\n") );
          _tprintf ( _T("  -I[nstall]\n") );
          _tprintf ( _T("   Windows service installation (also REGSERVER)\n") );
          _tprintf ( _T("  -R[emove]\n") );
          _tprintf ( _T("   Windows service removal (also UNREGISTER)\n") );
          _tprintf ( _T("  -[e]X[it]\n") );
          _tprintf ( _T("  -Exit application immediately after processing command arguments\n") );
        }
        bResult = true;
      }
    }

    // Exceptions
    catch ( ... )
    {
      EVERR->Module ( __FUNCTION__ )
           ->AFP(*argv)->AFP(argn)->AFP(bImplement)
           ->Message(_T("Unknown exception"))
           ->SetLast();
    }

    // Tidy up and
    if ( bResult )
      argn = b;                        // Comsumed arguments
    return bResult;                    // Consumed arguments
}

//
//  Checks for and processes default service arguments
//  NOTES: This default argument set defines the default P2PeerService
//         environment
//
//  Parameters: LPTSTR argv
//              Argument to be processed
//
//              DWORD argn
//              Processed argument list position
//
//              bool bImplement
//                true... Implement decoded option
//                false.. Validation and object synchronisation
//
//  Returns:    bool
//              Processing summary
//                true... Argument identified and processed 
//                        GetP2Pevent() summarises processing result
//                false.. Argument NOT identified
bool
P2PeerService::DefaultDebugArgs ( LPTSTR argv[], DWORD& argn, bool bImplement )
{
    // Locals
    bool bResult = false;
    int  a = argn, b = argn;
    int  iSize   = strlen ( argv[a] );

    // Debug mode
    if ( _memicmp ( argv[a], "-Debug", max(2,iSize) ) == 0 ||
         _memicmp ( argv[a], "/Debug", max(2,iSize) ) == 0    )
    {
      m_bDebug = true;
      if ( bImplement )
        P2Pevent::Configure ( P2Pevent::ADDMASK
                            , P2Pevotn_ERROR | P2Pevotn_DEBUG );
      bResult = true;
    }

    // Tidy up, and
    if ( bResult )
      argn = b + 1;
    return bResult;
}

//
//  Service installation
//  NOTES: Skips operation if service already installed
//       : Usually activated by one of following execution arguments
//           -I[nstall]
//           /I[nstall]
//           /REG[SERVER]
//       : Refer complimentary UnInstall() for removal of installed
//         services
//
//  Returns:    BOOL
//              Operation summary
//                TRUE... Successful
//                FALSE.. Failed, refer GetP2Pevent() for details
BOOL
P2PeerService::Install (  )
{
    // Check existing state
    if ( IsInstalled ( m_szServiceName ) )
        return TRUE;

    // Open the Service Control Manager
    SafeSCHandle shSCMgr
      = OpenSCManager ( NULL, NULL, SC_MANAGER_ALL_ACCESS );
    if ( shSCMgr == NULL )
    {
      EVERR->MODULE
           ->Message("OpenSCManager() failed" )
           ->HResult( GetLastError() )
           ->SetLast( );
      return FALSE;
    }

    // Fetch full path of this executable
    TCHAR szFilePath[_MAX_PATH];
    ::GetModuleFileName ( NULL, szFilePath, sizeof(szFilePath)/sizeof(szFilePath) );

    // Win32 implementation
    SafeSCHandle shSrv
      = CreateService ( shSCMgr
                      , m_szServiceName
                      , m_szServiceName
                      , SERVICE_ALL_ACCESS
                      , SERVICE_WIN32_OWN_PROCESS
                      , SERVICE_DEMAND_START
                      , SERVICE_ERROR_NORMAL
                      , szFilePath
                      , NULL
                      , NULL
                      , NULL
                      , NULL
                      , NULL );
    if ( shSrv == NULL )
    {
      EVERR->MODULE
           ->Message("CreateService(%s) failed", m_szServiceName )
           ->HResult( GetLastError() )
           ->SetLast( );
      return FALSE;
    }
    
    // Tidy up, and
    // NOTES: Observe post Install() management of registry entries etc
    PostInstall ( );
    shSrv   = 0;
    shSCMgr = 0;
    return TRUE;
}

void
P2PeerService::PostInstall ( )
{
}

//
//  Service removal
//  NOTES: Skips operation if service already installed
//       : Usually activated by one of following execution arguments
//           -R[nstall]
//           /R[nstall]
//           /UNREG[SERVER]
//       : Refer complimentary UnInstall() for removal of installed
//         services
//
//  Returns:    BOOL
//              Operation summary
//                TRUE... Successful
//                FALSE.. Failed, refer GetP2Pevent() for details
BOOL
P2PeerService::UnInstall ( )
{
    // Redundancy check
    if ( !IsInstalled(m_szServiceName) )
        return TRUE;

    // Open the Service Control Manager
    SetLastError ( 0 );                // Clear failure flag
    SafeSCHandle shSCMgr
      = OpenSCManager ( NULL, NULL, SC_MANAGER_ALL_ACCESS );
    if ( shSCMgr == NULL )
    {
      EVERR->MODULE
           ->Message("OpenSCManager() failed" )
           ->HResult( GetLastError() )
           ->SetLast( );
      return FALSE;
    }

    // Isloate service
    SafeSCHandle shSrv = OpenService ( shSCMgr, m_szServiceName, DELETE );
    if ( shSrv == NULL )
    {
      EVERR->MODULE
           ->Message("OpenService() failed" )
           ->HResult( GetLastError() )
           ->SetLast( );
      return FALSE;
    }

    // Delete service
    if ( !DeleteService(shSrv) )
    {
      EVERR->MODULE
           ->Message("DeleteService() failed" )
           ->HResult( GetLastError() )
           ->SetLast( );
      return FALSE;
    }

    // Tidy up, and
    // NOTES: Observe post UnInstall() management of registry entries etc
    PostUnInstall ( );
    shSrv   = 0;
    shSCMgr = 0;
    return TRUE;
}

void
P2PeerService::PostUnInstall ( )
{
}

//
//  Summarise installed status of passed service name
//  NOTES: Passed service name resolves to the "Name" column in
//         the Windows Service Control Manager
//
//  Parameters: LPCTSTR lpszServiceName
//              Name of the service to be checked for installation
//
//  Returns:    bool
//              Installed service summary
//                true... Installed
//                false.. Not installed or error
bool
P2PeerService::IsInstalled ( LPCTSTR lpszServiceName )
{
    // Locals
    bool bResult = false;

    // Open the Service Control Manager
    SetLastError ( 0 );                // Clear failure flag
    SafeSCHandle shSCMgr = ::OpenSCManager ( NULL, NULL
                                           , SC_MANAGER_ALL_ACCESS );
    if ( shSCMgr == NULL )
    {
      EVERR->MODULE->AFP(lpszServiceName)
           ->Message ("OpenSCManager() failed" )
           ->HResult( GetLastError() )
           ->SetLast ( );
      return false;
    }

    // Now open the service
    SafeSCHandle shSrv = OpenService ( shSCMgr, lpszServiceName
                                     , SERVICE_QUERY_CONFIG );
    if ( shSrv != NULL ) 
      bResult = true;

    // Tidy up, and
    shSrv   = 0;
    shSCMgr = 0;
    return bResult;
}

/*=================================================================
    Stubs
=================================================================*/
DWORD 
P2PeerService::Init( DWORD argc, LPTSTR* argv ) { return NO_ERROR; }


///////////////////////////////////////////////////////////////////////
//  Service operation

//
//  Service execution entry point
//  NOTES: For consistency -C[onsole] mode also utilises this entry point
//         but spawns the hub off into its own context and runs a console
//         from this context
int
P2PeerService::Run ( )
{
    // Environmental
    // NOTES: Demand initialisation of P2Pmsg'ing environment
    int iResult = 0;
    ASSERT(m_nMaxHubs>0);
    StartupP2Pmsg ( m_nMaxHubs );
    P3PmsgBSTR::SetDefaultP2Pmsgnn ( VBLock_Addr32 );
    WSADATA oWsaData;
    WORD    wVersionRequested = MAKEWORD( 2, 0 );
    if ( WSAStartup ( wVersionRequested, &oWsaData ) )
    {
      EVERR->MODULE
           ->Message("WSAStartup() failed" ) 
           ->HResult( WSAGetLastError() )
           ->Cancel();
      return 1;
    }

    // Initiate and run contained hub as service
    // NOTES: In Service mode the hub locks up this processing context
    //        Console mode spawns hub off into another context
    //      : Hence the Service and Console threading models are subtly
    //        different
    if ( IsServiceMode() )
    {
      ASSERT(m_pP2PeerHub);
      ASSERT(m_pP2PeerHub->GetHubID()<=0);
      ASSERT(m_nMaxPumps>0);
      m_pP2PeerHub -> CreateHub ( m_strP2PaddrHub, m_nMaxPumps );
      if ( m_pP2PeerHub->GetHubID() > 0 )
        m_pP2PeerHub -> RunHub ( );
    }

    // Initiate and run contained hub as console
    // NOTES: In Console mode the hub is spawned off into its own
    //        processing context and this context by default becomes
    //        the operator console
    //      : Hence the Service and Console threading models are subtly
    //        different
    else if ( IsConsoleMode() )
    {
      ASSERT(m_pP2PeerHub);
      ASSERT(m_pP2PeerHub->GetHubID()<=0);
      m_pP2PeerHub -> SpawnHub ( );
      iResult = RunConsoleModeAttachment ( );
    }
    
    // Tidy up
    // NOTES: Common to both Service and Console modes
    m_pP2PeerHub -> CloseHub ( );
    CleanupP2Pmsg ( );
    return 0;
}

int
P2PeerService::RunConsoleModeAttachment ( )
{
    // Locals
    int   nRetCode = 0;
    TCHAR cOption = 0;
    
    // Assume problematic environment
TOP:try
    {
      // Until terminated
      while ( cOption != 'Q' &&
              cOption != 'q'    )
      {
        UINT nHubID = m_pP2PeerHub -> GetHubID();
        UINT nPause = nHubID > 0 ? GetP2PmsgHubConCount ( nHubID ) : 0;
        cout << std::endl;
        cout << "(P2PeerService) Select option from:" << std::endl;
        if ( nHubID <= 0 )
          cout << " 1. Start Hub" << std::endl;
        if ( nHubID > 0 )
          cout << " 2. Stop Hub [" << nHubID << "]" << std::endl;
        if ( nPause )
          cout << " 3. Pause Hub" << std::endl;
        if ( nPause == 0 )
          cout << " 4. Continue Hub" << std::endl;
        //cout << " 5. Spare5 [" << szSpare2 << "]" << std::endl;
        //cout << " 6. Spare6" << std::endl;
        //cout << " 7. Spare7" << std::endl;
        cout << " 8. Toggle debug" << std::endl;
        cout << " R. Refresh" << std::endl;
        cout << " Q. Quit application" << std::endl;

        cout << "Option:";
        cin  >> cOption;
        cout << std::endl;
        nHubID = m_pP2PeerHub -> GetHubID();
        nPause = nHubID > 0 ? GetP2PmsgHubConCount ( nHubID ) : 0;

        // Start hub
        if ( cOption == _T('1') &&
             nHubID  <=  0         )
          m_pP2PeerHub -> SpawnHub ( );

        // Stop hub
        if ( cOption == _T('2') )
          m_pP2PeerHub -> CloseHub ( );

        // Pause hub by delegating to service equivalent
        if (  cOption == _T('3') &&
              nHubID  >      0   &&
              nPause                 )
        {
          nPause = 1;
          m_pP2PeerHub -> PauseHub ( );
        }

        // Continue hub by delegating to service equivalent
        if (  cOption == _T('4') &&
              nHubID  >      0   &&
             !nPause                 )
          m_pP2PeerHub -> WakeupHub ( );

        // Toggle debug mode
        // NOTES: Results are asynchronously displayed in the
        //        P2PeerSQLconsole hub.  Since the main() thread does
        //        not integrate seamlessly into a P2PeerHub virtual
        //        network
        if ( cOption == '8' )
        {
          P2Pevent::Configure ( P2Pevent::ADDMASK
                              , P2Pevotn_ERROR | P2Pevotn_DEBUG );
        }
      }
    }

    // Exceptions
    catch ( P2Pevent *pEVT )
    {
      pEVT->Cancel ( );
      goto TOP;
    }

    // Tidy up, and
	  return nRetCode;
}

///////////////////////////////////////////////////////////////////////
//  Standard Win32 Service control operations
//  NOTES: Referenced from the P2PeerService::ServiceCtrlHandler(),
//         and usually in the context of a Win32 system callback and
//         as such we NOT processing within hub context
//       : Default behaviour is to signal P2PeerHub.  At which
//         point implementation occurs in the context of the hub
DWORD
P2PeerService::OnPause ( void )
{
    if ( m_pP2PeerHub             == 0 ||
         m_pP2PeerHub->GetHubID() <= 0    )
      return NO_ERROR;
    m_pP2PeerHub -> PauseHub ( );
    return NO_ERROR;
}
DWORD
 P2PeerService::OnContinue ( void )
{
    if ( m_pP2PeerHub             == 0 ||
         m_pP2PeerHub->GetHubID() <= 0    )
      return NO_ERROR;
    m_pP2PeerHub -> WakeupHub ( );
    return NO_ERROR;
}
void
P2PeerService::OnStop ( void )
{
    if ( m_pP2PeerHub             == 0 ||
         m_pP2PeerHub->GetHubID() <= 0    )
      return;
    SignalP2PmsgHub ( m_pP2PeerHub->GetHubID(), P2PsigHub_CLOSEONIDLE );
  __time64_t tms = _time64 ( 0 ) + 5;
    while ( m_pP2PeerHub->GetHubID() > 0 &&
            tms <= _time64(0)               )
        Sleep ( 10 );
    return;
}
void
P2PeerService::OnShutdown ( void )
{   
    if ( m_pP2PeerHub             == 0 ||
         m_pP2PeerHub->GetHubID() <= 0    )
      return;
    SignalP2PmsgHub ( m_pP2PeerHub->GetHubID(), P2PsigHub_CLOSE );
  __time64_t tms = _time64 ( 0 ) + 5;
    while ( m_pP2PeerHub->GetHubID() > 0 &&
            tms <= _time64(0)               )
        Sleep ( 10 );
    return;
}
void
P2PeerService::OnInterrogate ( void )
{
}
void
P2PeerService::OnUserControl ( DWORD )
{
}

///////////////////////////////////////////////////////////////////////
//  Properties

LPCTSTR
P2PeerService::SetServiceName ( LPCTSTR lpszServiceName )
{
    m_strServiceName = lpszServiceName;
    return m_strServiceName;
}
LPCTSTR
P2PeerService::GetServiceName ( )
{
    return m_strServiceName;
}
LPCTSTR
P2PeerService::SetDisplayName ( LPCTSTR lpszDisplayName )
{
    m_strDisplayName = lpszDisplayName;
    return m_strDisplayName;
}
LPCTSTR
P2PeerService::GetDisplayName ( )
{
    return m_strDisplayName;
}
LPCTSTR
P2PeerService::SetServiceDesc ( LPCTSTR lpszServiceDesc )
{
    m_strServiceDesc = lpszServiceDesc;
    return m_strServiceDesc;
}
LPCTSTR
P2PeerService::GetServiceDesc ( )
{
    return m_strServiceDesc;
}
void
P2PeerService::SetAcceptedControls ( DWORD controls )
{
    m_oServiceStatus.dwControlsAccepted = controls;
}

void
P2PeerService::ChangeStatus ( DWORD state, DWORD checkpoint
                            , DWORD waithint )
{
    m_oServiceStatus.dwCurrentState  = state;
    m_oServiceStatus.dwCheckPoint    = checkpoint;
    m_oServiceStatus.dwWaitHint      = waithint;
    
    SetServiceStatus( m_hServiceStatus, &m_oServiceStatus );
}

DWORD
P2PeerService::SetExitMode ( DWORD nExitCode )
{
    if (  m_bExitMode                      &&
         !m_oServiceStatus.dwWin32ExitCode    )
      m_oServiceStatus.dwWin32ExitCode = (int)nExitCode;
    m_bExitMode = true;
    return m_oServiceStatus.dwWin32ExitCode;
}
DWORD
P2PeerService::GetExitCode ( )
{
    return m_oServiceStatus.dwWin32ExitCode;
}

///////////////////////////////////////////////////////////////////////        
//  P2PeerHub integration

P2PeerHub*
P2PeerService::PostP2PeerHub ( P2PeerHub *pHub )
{
    if ( m_pP2PeerHub == pHub )
      return m_pP2PeerHub;
    if ( m_pP2PeerHub )
      delete m_pP2PeerHub;
    m_pP2PeerHub = pHub;
    return m_pP2PeerHub;
}

P2PeerHub*
P2PeerService::GetP2PeerHub ( )
{
    return m_pP2PeerHub;
}

///////////////////////////////////////////////////////////////////////
//  Property and state exposure
//
bool
P2PeerService::IsServiceMode ( )
{
    if (  m_bService  &&
         !m_bSystem   &&
         !m_bConsole     )
      return true;
    if ( !m_bSystem   &&
         !m_bConsole  &&
         !m_bExitMode    )
      return true;
    return false;
}

bool
P2PeerService::IsConsoleMode ( )
{
    return m_bConsole;
}

bool
P2PeerService::IsDebugMode ( )
{
    return m_bDebug;
}

bool
P2PeerService::IsExitMode() 
{
    return m_bExitMode;
}

///////////////////////////////////////////////////////////////////////
//  Safe SERVICE CONTROL MANAGER handle implementation
//  NOTES: HANDLE is managed within the life cycle of this object.
//
SafeSCHandle::SafeSCHandle ( )
{
    m_hSC = NULL;
}
SafeSCHandle::SafeSCHandle ( SC_HANDLE hSC )
{   
    m_hSC = hSC;
}
SafeSCHandle::~SafeSCHandle ( )
{   
    if ( m_hSC != NULL )
      CloseServiceHandle ( m_hSC );
}
SafeSCHandle&
SafeSCHandle::operator = ( SC_HANDLE hSC )
{   
    if ( m_hSC != hSC  &&
         m_hSC != NULL    )
      CloseServiceHandle ( m_hSC );
    m_hSC = hSC;
    return *this;
}
SafeSCHandle::operator SC_HANDLE ( )
{   
    return m_hSC;
}
SC_HANDLE
SafeSCHandle::Dereference()
{   
    SC_HANDLE hSC = m_hSC;
    m_hSC = NULL;
    return hSC;
}
