Page 1 of 2

Show message when running program as a service

Posted: Sat Mar 14, 2015 7:30 pm
by Jeff Barnes
How can you display a message to the user when a program is running as a service?

Re: Show message when running program as a service

Posted: Sun Mar 15, 2015 6:47 pm
by HunterEC
Jeff:

How do you make a program run as a service ? Can you share the code ? Thank you very much !

Re: Show message when running program as a service

Posted: Sun Mar 15, 2015 9:34 pm
by Jeff Barnes
Hi Hunter,

Have a look here: http://runasservice.com/

Re: Show message when running program as a service

Posted: Tue Mar 17, 2015 3:14 pm
by HunterEC
Jeff:

Thank you very much !

Re: Show message when running program as a service

Posted: Tue Mar 17, 2015 3:35 pm
by Jeff Barnes
You are welcome...

I use 4 batch files.
InstallService.bat - called from my installer program
StartService.bat - called form my program when exiting the user interface
StopService.bat - called from my program when opening the user interface
UninstallService.bat - used if I need to remove the service

InstallService.bat

Code: Select all

c:\YourAppFolder\RunAsService install YourServiceName "Description of Your Service" C:\YourAppFolder\YouApp.exe
net start "YourServiceName"
 

StartService.bat

Code: Select all

net start "YourServiceName"
 

StopService.bat

Code: Select all

net stop "YourServiceName"
 

UninstallService.bat

Code: Select all

net stop "YourServiceName"
c:\YourAppFolder\RunAsService uninstall "YourServiceName"
 

Re: Show message when running program as a service

Posted: Tue Mar 17, 2015 3:36 pm
by Antonio Linares
Jeff,

Please try this:

MsgInfo( GetModuleFileName( GetInstance() ) )

and see if there is a difference running as an EXE or as a service

Re: Show message when running program as a service

Posted: Tue Mar 17, 2015 4:12 pm
by Jeff Barnes
Hi Antonio,

When I run the program normally (ie: user interface) I see the name of the exe.

When I close the program and Start the Service I do not see any messages at all.

Re: Show message when running program as a service

Posted: Tue Mar 17, 2015 4:50 pm
by Gross
Services with Harbour

Testsvc.prg

Code: Select all

/*
 * $Id: testsvc.prg 18716 2012-12-03 13:52:22Z vszakats $
 */

/*
 * Harbour Project source code:
 * Windows Service API test code
 *
 * Copyright 2010 Jose Luis Capel - <jlcapel at hotmail . com>
 * www - http://harbour-project.org
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this software; see the file COPYING.txt.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, MA 02111-1307 USA (or visit the web site http://www.gnu.org/).
 *
 * As a special exception, the Harbour Project gives permission for
 * additional uses of the text contained in its release of Harbour.
 *
 * The exception is that, if you link the Harbour libraries with other
 * files to produce an executable, this does not by itself cause the
 * resulting executable to be covered by the GNU General Public License.
 * Your use of that executable is in no way restricted on account of
 * linking the Harbour library code into it.
 *
 * This exception does not however invalidate any other reasons why
 * the executable file might be covered by the GNU General Public License.
 *
 * This exception applies only to the code released by the Harbour
 * Project under the name Harbour.  If you copy code from other
 * Harbour Project or Free Software Foundation releases into a copy of
 * Harbour, as the General Public License permits, the exception does
 * not apply to the code that you add in this way.  To avoid misleading
 * anyone as to the status of such modified files, you must delete
 * this exception notice from them.
 *
 * If you write modifications of your own for Harbour, it is your choice
 * whether to permit this exception to apply to your modifications.
 * If you do not wish that, delete this exception notice.
 *
 */

#if ! defined( __HBSCRIPT__HBSHELL )

#define _SERVICE_NAME "RisFilSrv"

PROCEDURE Main( cMode )

   LOCAL nError
   LOCAL cMsg

   hb_default( @cMode, "S" ) /* NOTE: Must be the default action */

   SWITCH Upper( cMode )
   CASE "I"

      IF win_serviceInstall( _SERVICE_NAME, "Harbour Windows Test Service" )
         ? "Service has been successfully installed"
      ELSE
         nError := wapi_GetLastError()
         cMsg := Space( 128 )
         wapi_FormatMessage( ,,,, @cMsg )
         ? "Error installing service: " + hb_ntos( nError ) + " " + cMsg
      ENDIF
      EXIT

   CASE "U"

      IF win_serviceDelete( _SERVICE_NAME )
         ? "Service has been deleted"
      ELSE
         nError := wapi_GetLastError()
         cMsg := Space( 128 )
         wapi_FormatMessage( ,,,, @cMsg )
         ? "Error deleting service: " + hb_ntos( nError ) + " " + cMsg
      ENDIF
      EXIT

   CASE "S"

      /* NOTE: Used when starting up as service.
               Do not invoke the executable manually with this option */

      IF win_serviceStart( _SERVICE_NAME, @SrvMain() )
         ? "Service has started OK"
      ELSE
         nError := wapi_GetLastError()
         cMsg := Space( 128 )
         wapi_FormatMessage( ,,,, @cMsg )
         ? "Service has had some problems: " + hb_ntos( nError ) + " " + cMsg
      ENDIF
      EXIT

   CASE "START"      
      IF win_serviceInitiate( _SERVICE_NAME )
         ? "Service has started OK"
      ELSE
         nError := wapi_GetLastError()
         cMsg := Space( 128 )
         wapi_FormatMessage( ,,,, @cMsg )
         ? "Service has had some problems: " + hb_ntos( nError ) + " " + cMsg
      ENDIF
      EXIT
         
   CASE "STOP"
      IF win_serviceStoped( _SERVICE_NAME )
         ? "Service has stoped OK"
      ELSE
         nError := wapi_GetLastError()
         cMsg := Space( 128 )
         wapi_FormatMessage( ,,,, @cMsg )
         ? "Service has had some problems: " + hb_ntos( nError ) + " " + cMsg
      ENDIF
      EXIT
   ENDSWITCH

   RETURN

#include "fileio.ch"

PROCEDURE SrvMain( cParam1, cParam2 )

   LOCAL n := 0
   LOCAL fhnd := hb_FCreate( hb_DirBase() + "testsvc.out", FC_NORMAL, FO_DENYNONE + FO_WRITE )
   LOCAL cParam

   hb_default( @cParam1, "" )
   hb_default( @cParam2, "" )

   FWrite( fhnd, "Startup" + hb_eol() )
   FWrite( fhnd, "|" + hb_CmdLine() + "|" + hb_eol() )
   FWrite( fhnd, "|" + cParam1 + "|" + cParam2 + "|" + hb_eol() )

   FOR EACH cParam IN hb_AParams()
      FWrite( fhnd, "Parameter " + hb_ntos( cParam:__enumIndex() ) + " >" + cParam + "<" + hb_eol() )
   NEXT

   DO WHILE win_serviceGetStatus() == WIN_SERVICE_RUNNING
      FWrite( fhnd, "Work in progress " + hb_ntos( ++n ) + hb_eol() )
      hb_idleSleep( 0.5 )
   ENDDO

   FWrite( fhnd, "Exiting..." + hb_eol() )
   FClose( fhnd )

   win_serviceSetExitCode( 0 )
   win_serviceStop()

   RETURN

#else

PROCEDURE Main()

   ? "Cannot be used in script mode."

   RETURN

#endif
 
win_svc.c

Code: Select all

/*
 * $Id: win_svc.c 18716 2012-12-03 13:52:22Z vszakats $
 */

/*
 * Harbour Project source code:
 * Windows Service API
 *
 * Copyright 2010 Jose Luis Capel - <jlcapel at hotmail . com>
 * www - http://harbour-project.org
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this software; see the file COPYING.txt.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, MA 02111-1307 USA (or visit the web site http://www.gnu.org/).
 *
 * As a special exception, the Harbour Project gives permission for
 * additional uses of the text contained in its release of Harbour.
 *
 * The exception is that, if you link the Harbour libraries with other
 * files to produce an executable, this does not by itself cause the
 * resulting executable to be covered by the GNU General Public License.
 * Your use of that executable is in no way restricted on account of
 * linking the Harbour library code into it.
 *
 * This exception does not however invalidate any other reasons why
 * the executable file might be covered by the GNU General Public License.
 *
 * This exception applies only to the code released by the Harbour
 * Project under the name Harbour.  If you copy code from other
 * Harbour Project or Free Software Foundation releases into a copy of
 * Harbour, as the General Public License permits, the exception does
 * not apply to the code that you add in this way.  To avoid misleading
 * anyone as to the status of such modified files, you must delete
 * this exception notice from them.
 *
 * If you write modifications of your own for Harbour, it is your choice
 * whether to permit this exception to apply to your modifications.
 * If you do not wish that, delete this exception notice.
 *
 */

#include "hbwapi.h"
#include "hbapiitm.h"
#include "hbvm.h"
#include "hbstack.h"

#if ! defined( HB_OS_WIN_CE )

#if defined( __POCC__ ) || defined( __XCC__ )
#  include <winsvc.h>   /* it's disabled by WIN32_LEAN_AND_MEAN */
#endif

static SERVICE_STATUS        s_ServiceStatus;
static SERVICE_STATUS_HANDLE s_hStatus;
static PHB_ITEM              s_pHarbourEntryFunc = NULL;
static TCHAR                 s_lpServiceName[ 256 ];

/* Control handler function */
static VOID WINAPI hbwin_SvcControlHandler( DWORD fdwControl )
{
   switch( fdwControl )
   {
      case SERVICE_CONTROL_STOP:
         s_ServiceStatus.dwWin32ExitCode = 0;
         s_ServiceStatus.dwCurrentState  = SERVICE_STOPPED;
         return;

      case SERVICE_CONTROL_SHUTDOWN:
         s_ServiceStatus.dwWin32ExitCode = 0;
         s_ServiceStatus.dwCurrentState  = SERVICE_STOPPED;
         return;
   }

   SetServiceStatus( s_hStatus, &s_ServiceStatus ); /* Report current status */
}

static VOID WINAPI hbwin_SvcMainFunction( DWORD dwArgc, LPTSTR * lpszArgv )
{
   s_ServiceStatus.dwServiceType             = SERVICE_WIN32;
   s_ServiceStatus.dwCurrentState            = SERVICE_START_PENDING;
   s_ServiceStatus.dwControlsAccepted        = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
   s_ServiceStatus.dwWin32ExitCode           = 0;
   s_ServiceStatus.dwServiceSpecificExitCode = 0;
   s_ServiceStatus.dwCheckPoint              = 0;
   s_ServiceStatus.dwWaitHint                = 0;

   s_hStatus = RegisterServiceCtrlHandler( s_lpServiceName, ( LPHANDLER_FUNCTION ) hbwin_SvcControlHandler );

   if( s_hStatus != ( SERVICE_STATUS_HANDLE ) 0 )
   {
      if( s_pHarbourEntryFunc != NULL )
      {
         if( hb_vmRequestReenterExt() )
         {
            DWORD i;
            int iArgCount = 0;

            /* We report the running status to SCM. */
            s_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
            SetServiceStatus( s_hStatus, &s_ServiceStatus );

            hb_vmPushEvalSym();
            hb_vmPush( s_pHarbourEntryFunc );

            for( i = 1; i < dwArgc; ++i )
            {
               PHB_ITEM pItem = hb_stackAllocItem();

               HB_ITEMPUTSTR( pItem, lpszArgv[ i ] );
               if( hb_cmdargIsInternal( hb_itemGetCPtr( pItem ), NULL ) )
                  hb_stackPop();
               else
                  ++iArgCount;
            }

            hb_vmSend( ( HB_USHORT ) iArgCount );

            hb_vmRequestRestore();
         }
         else
            HB_TRACE( HB_TR_DEBUG, ( "HVM stack not available" ) );
      }
      else
         HB_TRACE( HB_TR_DEBUG, ( "Harbour service entry function not found" ) );
   }
   else
      HB_TRACE( HB_TR_DEBUG, ( "Error registering service" ) );
}

#endif

HB_FUNC( WIN_SERVICEGETSTATUS )
{
#if ! defined( HB_OS_WIN_CE )
   hb_retnl( s_ServiceStatus.dwCurrentState );
#else
   hb_retnl( 0 );
#endif
}

HB_FUNC( WIN_SERVICESETSTATUS )
{
#if ! defined( HB_OS_WIN_CE )
   s_ServiceStatus.dwCurrentState = ( DWORD ) hb_parnl( 1 );
   hb_retl( SetServiceStatus( s_hStatus, &s_ServiceStatus ) );
#else
   hb_retl( HB_FALSE );
#endif
}

HB_FUNC( WIN_SERVICESETEXITCODE )
{
#if ! defined( HB_OS_WIN_CE )
   s_ServiceStatus.dwWin32ExitCode = ( DWORD ) hb_parnl( 1 );
   hb_retl( SetServiceStatus( s_hStatus, &s_ServiceStatus ) );
#else
   hb_retl( HB_FALSE );
#endif
}

HB_FUNC( WIN_SERVICESTOP )
{
#if ! defined( HB_OS_WIN_CE )
   s_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
   SetServiceStatus( s_hStatus, &s_ServiceStatus );
#endif
}

HB_FUNC( WIN_SERVICEINSTALL )
{
   HB_BOOL bRetVal = HB_FALSE;

#if ! defined( HB_OS_WIN_CE )

   void * hPath;
   LPCTSTR lpPath = HB_PARSTR( 3, &hPath, NULL );

   TCHAR lpPathBuffer[ MAX_PATH ];

   if( lpPath == NULL )
   {
      if( GetModuleFileName( NULL, lpPathBuffer, HB_SIZEOFARRAY( lpPathBuffer ) ) )
         lpPath = lpPathBuffer;
      else
         hbwapi_SetLastError( GetLastError() );
   }

   if( lpPath )
   {
      SC_HANDLE schSCM = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );

      if( schSCM )
      {
         SC_HANDLE schSrv;

         void * hServiceName;
         void * hDisplayName;

         LPCTSTR lpServiceName = HB_PARSTRDEF( 1, &hServiceName, NULL );
         LPCTSTR lpDisplayName = HB_PARSTRDEF( 2, &hDisplayName, NULL );

         schSrv = CreateService( schSCM,                    /* SCM database */
                                 lpServiceName,             /* name of service */
                                 lpDisplayName,             /* service name to display */
                                 SERVICE_ALL_ACCESS,        /* desired access */
                                 SERVICE_WIN32_OWN_PROCESS, /* service type */
                                 HB_ISNUM( 4 ) ? ( DWORD ) hb_parnl( 4 ) : SERVICE_DEMAND_START,
                                                            /* start type */
                                 SERVICE_ERROR_NORMAL,      /* error control type */
                                 lpPath,                    /* path to service's binary */
                                 NULL,                      /* no load ordering group */
                                 NULL,                      /* no tag identifier */
                                 NULL,                      /* no dependencies */
                                 NULL,                      /* LocalSystem account */
                                 NULL );                    /* no password */

         if( schSrv )
         {
            bRetVal = HB_TRUE;

            CloseServiceHandle( schSrv );
         }
         else
            hbwapi_SetLastError( GetLastError() );

         hb_strfree( hServiceName );
         hb_strfree( hDisplayName );

         CloseServiceHandle( schSCM );
      }
      else
         hbwapi_SetLastError( GetLastError() );
   }

   hb_strfree( hPath );

#endif
   hb_retl( bRetVal );
}

HB_FUNC( WIN_SERVICEDELETE )
{
   HB_BOOL bRetVal = HB_FALSE;

#if ! defined( HB_OS_WIN_CE )
   SC_HANDLE schSCM = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );

   if( schSCM )
   {
      void * hServiceName;

      SC_HANDLE schSrv = OpenService( schSCM,
                                      HB_PARSTRDEF( 1, &hServiceName, NULL ),
                                      SERVICE_ALL_ACCESS );

      if( schSrv )
      {
         /* TODO: check if service is up and then stop it */

         bRetVal = ( HB_BOOL ) DeleteService( schSrv );

         CloseServiceHandle( schSrv );
      }
      else
         hbwapi_SetLastError( GetLastError() );

      hb_strfree( hServiceName );

      CloseServiceHandle( schSCM );
   }
   else
      hbwapi_SetLastError( GetLastError() );
#endif
   hb_retl( bRetVal );
}

HB_FUNC( WIN_SERVICESTART )
{
   HB_BOOL bRetVal = HB_FALSE;

#if ! defined( HB_OS_WIN_CE )
   PHB_ITEM pEntryFunc;

   SERVICE_TABLE_ENTRY lpServiceTable[ 2 ];

   HB_ITEMCOPYSTR( hb_param( 1, HB_IT_STRING ), s_lpServiceName, HB_SIZEOFARRAY( s_lpServiceName ) );

   if( s_pHarbourEntryFunc )
   {
      hb_itemRelease( s_pHarbourEntryFunc );
      s_pHarbourEntryFunc = NULL;
   }

   pEntryFunc = hb_param( 2, HB_IT_BLOCK | HB_IT_SYMBOL );

   if( pEntryFunc )
      s_pHarbourEntryFunc = hb_itemNew( pEntryFunc );

   lpServiceTable[ 0 ].lpServiceName = s_lpServiceName;
   lpServiceTable[ 0 ].lpServiceProc = ( LPSERVICE_MAIN_FUNCTION ) hbwin_SvcMainFunction;

   lpServiceTable[ 1 ].lpServiceName = NULL;
   lpServiceTable[ 1 ].lpServiceProc = NULL;

   if( StartServiceCtrlDispatcher( lpServiceTable ) )
      bRetVal = HB_TRUE;
   else
      hbwapi_SetLastError( GetLastError() );

#endif
   hb_retl( bRetVal );
}
HB_FUNC( WIN_SERVICEINITIATE )
{
   HB_BOOL bRetVal = HB_FALSE;
#if ! defined( HB_OS_WIN_CE )
// open a handle to the SCM
SC_HANDLE schSCM = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );

if( schSCM )
{
      // open a handle to the service
      void * hServiceName;

      SC_HANDLE schSrv = OpenService( schSCM, HB_PARSTRDEF( 1, &hServiceName, NULL ), GENERIC_EXECUTE );
      if( schSrv )
      {
         if( ! ( bRetVal = StartService( schSrv, 0, NULL ) ) )
            hbwapi_SetLastError( GetLastError() );

      }else
         hbwapi_SetLastError( GetLastError() );

      hb_strfree( hServiceName );

      CloseServiceHandle( schSrv );
      CloseServiceHandle( schSCM );

   }else
      hbwapi_SetLastError( GetLastError() );

#endif
   hb_retl( bRetVal );
}


HB_FUNC( WIN_SERVICESTOPED )
{
   HB_BOOL bRetVal = HB_FALSE;
#if ! defined( HB_OS_WIN_CE )
// open a handle to the SCM
SC_HANDLE schSCM = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
if( schSCM )
{
  void * hServiceName;
  // open a handle to the service
  SC_HANDLE schSrv = OpenService( schSCM, HB_PARSTRDEF( 1, &hServiceName, NULL ), GENERIC_EXECUTE );
  if( schSrv ){
     // send the STOP control request to the service
     SERVICE_STATUS status;
     ControlService( schSrv, SERVICE_CONTROL_STOP, &status );
     CloseServiceHandle( schSrv );
     CloseServiceHandle( schSCM );

         if( ( bRetVal = status.dwCurrentState != SERVICE_STOPPED ) )
            hbwapi_SetLastError( GetLastError() );

         hb_strfree( hServiceName );

  }else
         hbwapi_SetLastError( GetLastError() );

   }else
      hbwapi_SetLastError( GetLastError() );
#endif
   hb_retl( bRetVal );
}

 

Re: Show message when running program as a service

Posted: Wed Mar 18, 2015 7:54 am
by Antonio Linares
Jeff,

Ok, thats because services are not allowed to show a MsgInfo()

Change it into a call to LogFile() or OutputDebugString()

Re: Show message when running program as a service

Posted: Wed Mar 18, 2015 3:06 pm
by Gale FORd
You can allow service to interact with the desktop.
See if the info below helps.
Although typically services do not have a user interface, developers can add forms and other UI components. In this case, the "Allow service to interact with desktop" should be checked on the Logon tab in the Service properties dialog (though care should be taken with this approach as this can cause a security risk since any logged in user would be able to interact with the service)

Re: Show message when running program as a service

Posted: Wed Mar 18, 2015 4:13 pm
by Jeff Barnes
Tried OutputDebugString("TEST") ... nothing is displayed.
Verified that I have "allow service to interact with desktop" checked ... nothing is displayed.

I have come up with a work-around ... Since the service interacts with another running program I can use the Running Program to display the messages I need.

Thanks for all the input with this one :)

Re: Show message when running program as a service

Posted: Wed Mar 18, 2015 4:29 pm
by Antonio Linares
Jeff,
Tried OutputDebugString("TEST") ... nothing is displayed
Did you had dbwin32.exe running to see the output messages from OutputDebugString() ?

https://bitbucket.org/fivetech/fivewin- ... bwin32.zip

Re: Show message when running program as a service

Posted: Wed Mar 18, 2015 5:41 pm
by Jeff Barnes
Just tried with DBWIN32 running ... still nothing is displayed.

Re: Show message when running program as a service

Posted: Wed Mar 18, 2015 6:26 pm
by Gale FORd
There is another way using Windows Interactive Service.

https://msdn.microsoft.com/en-us/librar ... 85%29.aspx

Re: Show message when running program as a service

Posted: Thu Mar 19, 2015 4:35 am
by rhlawek
Regarding output from OutputDebugString(): Unless you've explicitly configured the service to run under a specific account, the service will be running as System. The debug viewer running under your desktop account likely doesn't have sufficient privilege to see these messages. That is certainly my experience. I happen to use DebugView from sysinternals. For it to see OutputDebugString() messages Capture Global Win32 messages must be enabled, which in DebugView is disabled by default. To enable that requires that I launch DebugView as Administrator.

It shouldn't matter for the above paragraph but I also don't use any external helper for any of my services (let's me stay with only a single .exe for an app). I've used the service manager control example from the harbour hbwin contribution, it's pretty straightforward.

Robb