/*
 * Xailer source code:
 *
 * WinService.prg
 * Clase TWinService
 *
 * Copyright 2017 Jose F. Gimenez
 * Copyright 2017 Xailer.com
 * All rights reserved
 *
 */

#include "Xailer.ch"

//------------------------------------------------------------------------------

CLASS XWinService FROM TComponent

PUBLISHED:
   PROPERTY cName          INIT ""
   PROPERTY cDisplayName   INIT ""
   PROPERTY cDescription   INIT ""

   EVENT OnRun( oSender )
   EVENT OnShutDown( oSender )
   EVENT OnPowerEvent( oSender, nEventType )

PUBLIC:
   PROPERTY nStatus READONLY INIT ssSTOPPED VALUES ssSTOPPED, ssSTART_PENDING, ssSTOP_PENDING, ssRUNNING, ssCONTINUE_PENDING, ssPAUSE_PENDING, ssPAUSED READ METHOD GetStatus()

   METHOD Install( cName, cDisplayName ) // --> lSuccess
   METHOD Uninstall()  // --> lSuccess
   METHOD Start() // --> lSuccess
   METHOD Stop() // --> lSuccess
   METHOD Run() // --> lSuccess

   METHOD IsInstalled() // --> lValue
   METHOD IsRunning()// --> lValue

RESERVED:
   METHOD GetStatus( nStatus )

ENDCLASS

//------------------------------------------------------------------------------

#pragma BEGINDUMP

#undef _WIN32_WINNT
#define _WIN32_WINNT 0x0600
#include "Windows.h"
#include "Xailer.h"

#ifdef __BORLANDC__
#define SERVICE_CONFIG_DELAYED_AUTO_START_INFO 3

typedef struct _SERVICE_DELAYED_AUTO_START_INFO {
  BOOL fDelayedAutostart;
} SERVICE_DELAYED_AUTO_START_INFO, *LPSERVICE_DELAYED_AUTO_START_INFO;
#endif

SERVICE_STATUS_HANDLE hSS = NULL;
SERVICE_STATUS ss;
PHB_ITEM oService = NULL;

//------------------------------------------------------------------------------

static BOOL XA_SetServiceStatus( DWORD nStatus )
{
   ss.dwCurrentState = nStatus;
   if( nStatus == SERVICE_RUNNING )
      ss.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_POWEREVENT;

   return SetServiceStatus( hSS, &ss );
}

//------------------------------------------------------------------------------

static LPHANDLER_FUNCTION_EX ServiceCtrlHandler( DWORD dwControl, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext )
{
   XA_SYMBOL_UNUSED( lpEventData );
   XA_SYMBOL_UNUSED( lpContext );

   if( ( dwControl == SERVICE_CONTROL_STOP ) || ( dwControl == SERVICE_CONTROL_SHUTDOWN ) )
      {
      ss.dwCurrentState = SERVICE_STOP_PENDING;
      if( dwControl == SERVICE_CONTROL_SHUTDOWN )
         XA_ObjSend( oService, "OnShutDown" );
      }
   else if( dwControl == SERVICE_CONTROL_POWEREVENT )
      XA_ObjSendNL( oService, "OnPowerEvent", dwEventType );

   SetServiceStatus( hSS, &ss );

   return NO_ERROR;
}

//------------------------------------------------------------------------------

static VOID WINAPI ServiceMain( DWORD dwArgc, LPTSTR *lpszArgv )
{
   XA_SYMBOL_UNUSED( dwArgc );
   XA_SYMBOL_UNUSED( lpszArgv );

   memset( &ss, 0, sizeof( SERVICE_STATUS ) );
   ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
   hSS = RegisterServiceCtrlHandlerEx( XA_ObjGetC( oService, "cName" ), (LPHANDLER_FUNCTION_EX) ServiceCtrlHandler, 0 );
   if( hSS )
      {
      XA_SetServiceStatus( SERVICE_START_PENDING );
      XA_SetServiceStatus( SERVICE_RUNNING );
      XA_ObjSend( oService, "OnRun" );
      }
   XA_SetServiceStatus( SERVICE_STOPPED );

   hb_itemRelease( oService );
   oService = NULL;
}

//------------------------------------------------------------------------------

HB_FUNC_STATIC( XWINSERVICE_INSTALL )
{
   PHB_ITEM Self = hb_stackSelfItem();
   LPCSTR cName = hb_parc( 1 );
   LPCSTR cDisplayName = hb_parc( 2 );
   TCHAR szPath[ MAX_PATH ];
   SC_HANDLE hSCM, hServ;
   BOOL lOk = FALSE;

   if( cName )
      XA_ObjSendC( Self, "_cName", cName );
   else
      cName = XA_ObjGetC( Self, "cName" );

   if( cDisplayName )
      XA_ObjSendC( Self, "_cDisplayName", cDisplayName );
   else
      cDisplayName = XA_ObjGetC( Self, "cDisplayName" );

   if( GetModuleFileName( NULL, szPath, MAX_PATH ) )
      {
      hSCM = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
      if( hSCM )
         {
         hServ = CreateService( hSCM, cName, cDisplayName ? cDisplayName : cName, SERVICE_ALL_ACCESS,
                                SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL,
                                szPath, NULL, NULL, NULL, NULL, NULL );
         if( hServ )
            {
            SERVICE_DELAYED_AUTO_START_INFO Delayed;
            SERVICE_DESCRIPTION Description;

            Delayed.fDelayedAutostart = TRUE;
            ChangeServiceConfig2( hServ, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &Delayed );
            Description.lpDescription = XA_ObjGetC( Self, "cDescription" );
            ChangeServiceConfig2( hServ, SERVICE_CONFIG_DESCRIPTION, &Description );
            lOk = TRUE;
            CloseServiceHandle( hServ );
            }
         CloseServiceHandle( hSCM );
         }
      }

   hb_retl( lOk );
}

//------------------------------------------------------------------------------

HB_FUNC_STATIC( XWINSERVICE_UNINSTALL )
{
   PHB_ITEM Self = hb_stackSelfItem();
   LPCSTR cName = hb_parc( 1 );
   SC_HANDLE hSCM, hServ;
   BOOL lOk = FALSE;

   if( cName )
      XA_ObjSendC( Self, "_cName", cName );
   else
      cName = XA_ObjGetC( Self, "cName" );

   hSCM = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
   if( hSCM )
      {
      hServ = OpenService( hSCM, cName, SERVICE_ALL_ACCESS );
      if( hServ )
         {
         lOk = DeleteService( hServ );
         CloseServiceHandle( hServ );
         }
      CloseServiceHandle( hSCM );
      }

   hb_retl( lOk );
}

//------------------------------------------------------------------------------

HB_FUNC_STATIC( XWINSERVICE_START )
{
   PHB_ITEM Self = hb_stackSelfItem();
   LPCSTR cName = hb_parc( 1 );
   SC_HANDLE hSCM, hServ;
   BOOL lOk = FALSE;

   if( cName )
      XA_ObjSendC( Self, "_cName", cName );
   else
      cName = XA_ObjGetC( Self, "cName" );

   hSCM = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
   if( hSCM )
      {
      hServ = OpenService( hSCM, cName, SERVICE_ALL_ACCESS );
      if( hServ )
         {
         lOk = StartService( hServ, 0, NULL );
         CloseServiceHandle( hServ );
         }
      CloseServiceHandle( hSCM );
      }

   hb_retl( lOk );
}

//------------------------------------------------------------------------------

HB_FUNC_STATIC( XWINSERVICE_STOP )
{
   PHB_ITEM Self = hb_stackSelfItem();
   LPCSTR cName = hb_parc( 1 );
   SC_HANDLE hSCM, hServ;
   SERVICE_STATUS ss;
   BOOL lOk = FALSE;

   if( cName )
      XA_ObjSendC( Self, "_cName", cName );
   else
      cName = XA_ObjGetC( Self, "cName" );

   hSCM = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
   if( hSCM )
      {
      hServ = OpenService( hSCM, cName, SERVICE_ALL_ACCESS );
      if( hServ )
         {
         memset( &ss, 0, sizeof( SERVICE_STATUS ) );
         lOk = ControlService( hServ, SERVICE_CONTROL_STOP, &ss );
         CloseServiceHandle( hServ );
         }
      CloseServiceHandle( hSCM );
      }

   hb_retl( lOk );
}

//------------------------------------------------------------------------------

HB_FUNC_STATIC( XWINSERVICE_RUN )
{
   PHB_ITEM Self = hb_stackSelfItem();
   LPCSTR cName = hb_parc( 1 );
   SERVICE_TABLE_ENTRY ste[] = { { NULL, ServiceMain }, { NULL, NULL } };

   if( cName )
      XA_ObjSendC( Self, "_cName", cName );
   else
      cName = XA_ObjGetC( Self, "cName" );

   ste[ 0 ].lpServiceName = (LPSTR) cName;
   oService = hb_itemNew( Self );

   hb_retl( StartServiceCtrlDispatcher( ste ) );
}

//------------------------------------------------------------------------------

HB_FUNC_STATIC( XWINSERVICE_ISINSTALLED )
{
   PHB_ITEM Self = hb_stackSelfItem();
   LPCSTR cName = hb_parc( 1 );
   SC_HANDLE hSCM, hServ;
   BOOL lOk = FALSE;

   if( cName )
      XA_ObjSendC( Self, "_cName", cName );
   else
      cName = XA_ObjGetC( Self, "cName" );

   hSCM = OpenSCManager( NULL, NULL, SC_MANAGER_CONNECT );
   if( hSCM )
      {
      hServ = OpenService( hSCM, cName, SERVICE_QUERY_STATUS );
      if( hServ )
         {
         lOk = TRUE;
         CloseServiceHandle( hServ );
         }
      CloseServiceHandle( hSCM );
      }

   hb_retl( lOk );
}

//------------------------------------------------------------------------------

HB_FUNC_STATIC( XWINSERVICE_ISRUNNING )
{
   PHB_ITEM Self = hb_stackSelfItem();
   LPCSTR cName = hb_parc( 1 );
   SC_HANDLE hSCM, hServ;
   SERVICE_STATUS ss;
   BOOL lOk = FALSE;

   if( cName )
      XA_ObjSendC( Self, "_cName", cName );
   else
      cName = XA_ObjGetC( Self, "cName" );

   hSCM = OpenSCManager( NULL, NULL, SC_MANAGER_CONNECT );
   if( hSCM )
      {
      hServ = OpenService( hSCM, cName, SERVICE_QUERY_STATUS );
      if( hServ )
         {
         if( QueryServiceStatus( hServ, &ss ) )
            lOk = ( ss.dwCurrentState == SERVICE_RUNNING );
         CloseServiceHandle( hServ );
         }
      CloseServiceHandle( hSCM );
      }

   hb_retl( lOk );
}

//------------------------------------------------------------------------------

HB_FUNC_STATIC( XWINSERVICE_GETSTATUS )
{
   PHB_ITEM Self = hb_stackSelfItem();
   DWORD nStatus;

   if( oService )
      nStatus = ss.dwCurrentState;
   else
      nStatus = XA_ObjGetNL( Self, "FnStatus" );

   hb_retnl( nStatus );
}

//------------------------------------------------------------------------------

#pragma ENDDUMP
