Windows (NT) Service anyone?

General FreeBASIC programming questions.
bobsobol
Posts: 65
Joined: Nov 04, 2006 9:19

Windows (NT) Service anyone?

Postby bobsobol » Feb 03, 2010 20:52

I have a need to create and register a Windows Service program. I can write eloquent enough C, but what I want will involve string manipulation on a level that strings.h is going to be an pain to fiddle and I don't want to go to the extreme of getting a regex library built in to work it. BASIC or Perl would be ideal, and as I'm already a freeBASIC fan and promote it's use in many fields I'd love to see this another score chalked up on the board for it.

Now I could probably write this as a simple console app with no output, and have a service program starter run it, but I'd much rather be able to process SERVICE_CONTROL_PAUSE, SERVICE_CONTROL_CONTINUE, SERVICE_CONTROL_STOP, SERVICE_CONTROL_INTERROGATE and SERVICE_CONTROL_SHUTDOWN my self, register (install) the service with the SCM from the command line and so on.

Has anyone here tried this or, better yet, got a skeleton that I can build on?

Any hints at all would be helpful, as while my C & Pascal is fairly reasonable, my C++ & C# is fair to useless. XD Sadly, most NT Service examples are written in Microsoft standard C++. :( So "Microsoft standard" C++ that most GPP (MinGW) built services can't even manage to set up the service them selves without a helper "daemon".

I'm hoping that this is mostly a matter of headers, and that if I can dig out enough enumerations and such from the official Win32SDK headers I can make a freeBASIC console application service without too much headache.
vdecampo
Posts: 2982
Joined: Aug 07, 2007 23:20
Location: Maryland, USA
Contact:

Postby vdecampo » Feb 04, 2010 0:24

Haven't done it myself, but I found some interesting material here...

http://www.microsoft.com/msj/0298/service.aspx

-Vince
bobsobol
Posts: 65
Joined: Nov 04, 2006 9:19

Postby bobsobol » Feb 04, 2010 8:02

Thanks Vince. :)

Actually, the most interesting point illustrated in that article (for me) is the use of named pipes. It's well described. The rest, I pretty much knew.

What I really need, to get started, is a freeBASIC equivalent of this C++ code by Jonathan NG

Code: Select all

/***************************************************************\
*            TEMPLATE CPP FOR SERVICE PROGRAM   01/08/2003   *   
*                  BY JONATHAN NG                     *   
*                                                *   
*                                                *   
*   Usage:                                          *   
*   1)   Add service.cpp to project workspace               *
*   2)   Change the ServiceName under the global variable      *   
*      to desire name                                 *
*   3)   Add your function into ServiceThread()               *
*   4)   DONE!                                       *
*                                                *
*   Install:                                       *   
*   1)   Use command prompt.                              *
*   2)   Locate the exe.                                 *
*   3)   Type <programname> -i      Install                  *
*      Type <programname> -u      Uninstall               *
*      Type <programname> HELP      For more               *
*                                                *
\***************************************************************/

#include <windows.h>
#include <iostream.h>
#include <stdio.h>

static struct ErrEntry {
   int code;
   const char* msg;
} ErrList[] = {
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/debug/base/system_error_codes.asp
   { 0,   "No error" },
   { 1055,   "The service database is locked." },
   { 1056,   "An instance of the service is already running." },
   { 1060, "The service does not exist as an installed service." },
   { 1061,   "The service cannot accept control messages at this time." },
   { 1062, "The service has not been started." },
   { 1063, "The service process could not connect to the service controller." },
   { 1064,   "An exception occurred in the service when handling the control request." },
   { 1065,   "The database specified does not exist." },
   { 1066,   "The service has returned a service-specific error code." },
   { 1067,   "The process terminated unexpectedly." },
   { 1068,   "The dependency service or group failed to start." },
   { 1069,   "The service did not start due to a logon failure." },
   { 1070,   "After starting, the service hung in a start-pending state." },
   { 1071,   "The specified service database lock is invalid." },
   { 1072, "The service marked for deletion." },
   { 1073, "The service already exists." },
   { 1078,   "The name is already in use as either a service name or a service display name." },
};
const int nErrList = sizeof(ErrList) / sizeof(ErrEntry);


//// Global /////////////////////////////////////////////////////////
FILE*      pLog;                     
char*      ServiceName = "ServiceTest";      // Name of the service
HANDLE      terminateEvent = NULL;         // Event used to hold ServerMain from completing
                                 // Handle used to communicate status info with
                                 // the SCM. Created by RegisterServiceCtrlHandler
HANDLE      threadHandle = 0;            // Thread for the actual work
BOOL      pauseService = FALSE;         // Flags holding current state of service
BOOL      runningService = FALSE;         //
SERVICE_STATUS_HANDLE serviceStatusHandle;   //

DWORD   WINAPI ServiceThread( LPDWORD lParam);
BOOL   InitService();
BOOL   SendStatusToSCM(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwServiceSpecificExitCode, DWORD dwCheckPoint, DWORD dwWaitHint);
void   ResumeService();
void   PauseService();
void   StopService();
void   terminate(DWORD error);
void   ServiceCtrlHandler(DWORD controlCode);
void   ServiceMain(DWORD argc, LPTSTR *argv);
void   ErrorHandler(char *s, int err);
void   GetStatus(SC_HANDLE service);
void   ShowUsage();
// service config program tasks
bool   InstallService();
bool   UninstallService();
bool   GetConfiguration();
bool   ChangeConfig();
// service control program tasks
bool   ServiceRun();
bool   ServiceControl(char* CONTROL);

void main(int argc, char *argv[])
{
   SERVICE_TABLE_ENTRY serviceTable[] =
   {
      { ServiceName, (LPSERVICE_MAIN_FUNCTION) ServiceMain},
      { NULL, NULL}
   };
   BOOL success;
   
   if(argc == 2)
   {
      if (stricmp("-i", argv[1]) == 0)
         InstallService();
      else if (stricmp("-u", argv[1]) == 0)         
         UninstallService();
      else if (stricmp("-r", argv[1]) == 0)
         ServiceRun();
      else if (stricmp("-s", argv[1]) == 0)
         ServiceControl("STOP");
      else if (stricmp("-p", argv[1]) == 0)
         ServiceControl("PAUSE");
      else if (stricmp("-c", argv[1]) == 0)
         ServiceControl("RESUME");
      else if (stricmp("status", argv[1]) == 0)
      {   
         SC_HANDLE scm, service;
         //Open connection to SCM
         scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
         if (!scm)
            ErrorHandler("OpenSCManager", GetLastError());
         //Get service's handle
         service = OpenService(scm, ServiceName, SERVICE_ALL_ACCESS);
         if (!service)
            ErrorHandler("OpenService", GetLastError());
         cout << "STATUS: ";
         GetStatus(service);
      }
      else if (stricmp("config", argv[1]) == 0)
         GetConfiguration();
      else if (stricmp("help", argv[1]) == 0)
         ShowUsage();
      //add other custom commands here and
      //update ShowUsage function

      else
         ShowUsage();
   }
   
   else
   {
      //register with SCM
      success = StartServiceCtrlDispatcher(serviceTable);
      if (!success)
         ErrorHandler("StartServiceCtrlDispatcher",GetLastError());
   }
}


void ServiceMain(DWORD argc, LPTSTR *argv)
{
   BOOL success;

   //immediately call registration function
   serviceStatusHandle = RegisterServiceCtrlHandler(ServiceName, (LPHANDLER_FUNCTION)ServiceCtrlHandler);
   if (!serviceStatusHandle)
   {
      terminate(GetLastError());
      return;
   }

   //notify SCM
   success = SendStatusToSCM(SERVICE_START_PENDING, NO_ERROR, 0 , 1, 5000);
   if (!success)
   {
      terminate(GetLastError());
      return;
   }

   //create termination event
   terminateEvent = CreateEvent (0, TRUE, FALSE, 0);
   if (!terminateEvent)
   {
      terminate(GetLastError());
      return;
   }

   //notify SCM
   success = SendStatusToSCM(SERVICE_START_PENDING, NO_ERROR, 0 , 2, 1000);
   if (!success)
   {
      terminate(GetLastError());
      return;
   }

   /*
   //check for startup parameter
   if (argc == 2)
      
   else
      
   */

   //notify SCM
   success = SendStatusToSCM(SERVICE_START_PENDING, NO_ERROR, 0 , 3, 5000);
   if (!success)
   {
      terminate(GetLastError());
      return;
   }

   //start service
   success = InitService();
   if (!success)
   {
      terminate(GetLastError());
      return;
   }

   //notify SCM service is runnning
   success = SendStatusToSCM(SERVICE_RUNNING, NO_ERROR, 0 , 0, 0);
   if (!success)
   {
      terminate(GetLastError());
      return;
   }

   //wait for stop signal and then terminate
   WaitForSingleObject(terminateEvent, INFINITE);

   terminate(0);
}


DWORD WINAPI ServiceThread(LPDWORD lParam)
{
   
   //DO YOUR THINGS HERE

   return 0;
}

//initialises the service by starting its thread
BOOL InitService()
{
   DWORD id;

   // Start the service's thread
   threadHandle = CreateThread(
   NULL,
   0,
   (LPTHREAD_START_ROUTINE) ServiceThread,
   NULL,
   0,
   &id);
   
   if (threadHandle == 0)
      return FALSE;
   else
   {
      runningService = TRUE;
      return TRUE;
   }
}

//resumes paused service
void ResumeService()
{
   pauseService = FALSE;
   ResumeThread(threadHandle);
}

//pauses service
void PauseService()
{
   pauseService = TRUE;
   SuspendThread(threadHandle);
}

//stops service by allowing ServiceMain to complete
void StopService()
{
   runningService = FALSE;
   //set the event that is holding ServiceMain
   SetEvent(terminateEvent);
}

//this function consolidates the activities of updating
//the service status with SetServiceStatus
BOOL SendStatusToSCM(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwServiceSpecificExitCode, DWORD dwCheckPoint, DWORD dwWaitHint)
{
   BOOL success;
   SERVICE_STATUS serviceStatus;

   //fill in all of the SERVICE_STATUS fields
   serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
   serviceStatus.dwCurrentState = dwCurrentState;

   //if in the process of something, then accept
   //no control events, else accept anything
   if (dwCurrentState == SERVICE_START_PENDING)
      serviceStatus.dwControlsAccepted = 0;
   else
      serviceStatus.dwControlsAccepted =
         SERVICE_ACCEPT_STOP |
         SERVICE_ACCEPT_PAUSE_CONTINUE |
         SERVICE_ACCEPT_SHUTDOWN;

   //if a specific exit code is defines, set up the win32 exit code properly
   if (dwServiceSpecificExitCode == 0)
      serviceStatus.dwWin32ExitCode = dwWin32ExitCode;
   else
      serviceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
   
   serviceStatus.dwServiceSpecificExitCode = dwServiceSpecificExitCode;
   serviceStatus.dwCheckPoint = dwCheckPoint;
   serviceStatus.dwWaitHint = dwWaitHint;
   
   success = SetServiceStatus (serviceStatusHandle, &serviceStatus);
   if (!success)
      StopService();

   return success;
}

void ServiceCtrlHandler(DWORD controlCode)
{
   DWORD currentState = 0;
   BOOL success;

   switch(controlCode)
   {
      // START = ServiceMain()

      // STOP
      case SERVICE_CONTROL_STOP:
         currentState = SERVICE_STOP_PENDING;
         //notify SCM
         success = SendStatusToSCM(
            SERVICE_STOP_PENDING,
            NO_ERROR,
            0,
            1,
            5000);
         //stop service
         StopService();
         return;

      // PAUSE
      case SERVICE_CONTROL_PAUSE:
         if (runningService && !pauseService)
         {
            //notify SCM
            success = SendStatusToSCM(
               SERVICE_PAUSE_PENDING,
               NO_ERROR,
               0,
               1,
               1000);
            
            PauseService();
            currentState = SERVICE_PAUSED;
         }
         break;
         
      // RESUME
      case SERVICE_CONTROL_CONTINUE:
         if (runningService && pauseService)
         {
            //notify SCM
            success = SendStatusToSCM(
               SERVICE_CONTINUE_PENDING,
               NO_ERROR,
               0,
               1,
               1000);
            
            ResumeService();
            currentState = SERVICE_RUNNING;
         }
         break;

      // UPDATE
      case SERVICE_CONTROL_INTERROGATE:
         //update status out of switch()
         break;
         
      case SERVICE_CONTROL_SHUTDOWN:
         //do nothing
         return;
      default:
         break;
   }
   //notify SCM current state
   SendStatusToSCM(currentState, NO_ERROR, 0, 0, 0);
}
   

//handle an error from ServiceMain by cleaning up and tell SCM service didn't start.
void terminate(DWORD error)
{
   //close event handle
   if (terminateEvent)
      CloseHandle(terminateEvent);

   //notify SCM service stopped
   if (serviceStatusHandle)
      SendStatusToSCM(SERVICE_STOPPED, error, 0, 0, 0);

   //close thread handle
   if (threadHandle)
      CloseHandle(threadHandle);
}

   
void ErrorHandler(char *s, int err)
{

   cout << s << " failed" << endl;
   cout << "Error (" << err << "): ";
   int i;
   for (i = 0; i < nErrList; ++i) {
      if (ErrList[i].code == err) {
         cout << ErrList[i].msg;
         break;
      }
   }
   if (i == nErrList) {
      cout << "unknown error";
   }

   cout << endl;

   pLog = fopen("server.log","a");
   fprintf(pLog, "%s failed, error code = %d\n",s , err);
   fclose(pLog);

   ExitProcess(err);
}

void ShowUsage()
{
      cout << endl;
      cout << "USAGE:" << endl;
      cout << "server -i\tInstall service" << endl;
      cout << "server -u\tUninstall service" << endl;
      cout << "server -r\tRun service" << endl;
      cout << "server -s\tStop service" << endl;
      cout << "server -p\tPause service" << endl;
      cout << "server -c\tResume service" << endl;
      cout << "server status\tCurrent status" << endl;
      cout << endl;
}


////////////////////////////////////////////////////////////////////////////////
// Purpose   :Install service into SCM.
// Parameter:N/A
// Returns   :N/A
////////////////////////////////////////////////////////////////////////////////
bool InstallService()
{
   SC_HANDLE newService;
   SC_HANDLE scm;
    char szBuffer[255];
    char szPath[MAX_PATH];

   //get file path
   GetModuleFileName( GetModuleHandle(NULL), szPath, MAX_PATH );
    strcpy( szBuffer, "\"" );
    strcat( szBuffer, szPath );
    strcat( szBuffer, "\"" );

   //open connection to SCM
   scm = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
   if (!scm)
      ErrorHandler("OpenSCManager", GetLastError());

   //install service
   newService = CreateService(
      scm,                  //scm database
      ServiceName,            //service name
      ServiceName,            //display name
      SERVICE_ALL_ACCESS,         //access rights to the service
      SERVICE_WIN32_OWN_PROCESS,   //service type
      SERVICE_AUTO_START,         //service start type
      SERVICE_ERROR_NORMAL,      //error control type
      szBuffer,               //service path
      NULL,                  //no load ordering group
      NULL,                  //no tag identifier
      NULL,                  //no dependencies   
      NULL,                  //LocalSystem account
      NULL);                  //no password
   if(!newService)
   {
      ErrorHandler("CreateService", GetLastError());
      return false;
   }
   else
   {
      cout << "Service Installed" << endl;
      ServiceRun();
   }

   //clean up
   CloseServiceHandle(newService);
   CloseServiceHandle(scm);
   
   return true;

}

////////////////////////////////////////////////////////////////////////////////
// Purpose   :Uninstall service from SCM.
// Parameter:N/A
// Returns   :N/A
////////////////////////////////////////////////////////////////////////////////
bool UninstallService()
{
   SC_HANDLE service;
   SC_HANDLE scm;
   BOOL SUCCESS;
   SERVICE_STATUS status;

   //Open connection to SCM
   scm = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
   if (!scm)
      ErrorHandler("OpenSCManager", GetLastError());

   //Get service's handle
   service = OpenService(scm, ServiceName, SERVICE_ALL_ACCESS | DELETE);
   if (!service)
      ErrorHandler("OpenService", GetLastError());

   //Get service status
   SUCCESS   = QueryServiceStatus(service, &status);
   if (!SUCCESS)
      ErrorHandler("QueryServiceStatus", GetLastError());
   
   //Stop service if necessary      
   if (status.dwCurrentState != SERVICE_STOPPED)
   {
      cout << "Stopping service..." << endl;
      SUCCESS = ControlService(service, SERVICE_CONTROL_STOP, &status);
      if (!SUCCESS)
         ErrorHandler("ControlService", GetLastError());
      Sleep(500);
   }

   //Delete service
   SUCCESS = DeleteService(service);
   if (SUCCESS)
      cout << "Service Uninstalled" << endl;
   else
      ErrorHandler("DeleteService", GetLastError());

   //Clean up
   CloseServiceHandle(service);
   CloseServiceHandle(scm);

   return true;
}


////////////////////////////////////////////////////////////////////////////////
// Purpose   :Run service
// Parameter:N/A
// Returns   :N/A
////////////////////////////////////////////////////////////////////////////////
bool ServiceRun()
{
    SC_HANDLE scm, Service;
   SERVICE_STATUS ssStatus;
    DWORD dwOldCheckPoint;
    DWORD dwStartTickCount;
    DWORD dwWaitTime;
    DWORD dwStatus;
    
   //open connection to SCM
   scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
   if (!scm)
      ErrorHandler("OpenSCManager", GetLastError());

   //open service
   Service = OpenService(scm, ServiceName, SERVICE_ALL_ACCESS);
   if(!Service)
   {
      ErrorHandler("OpenService", GetLastError());
      return false;
   }
   else
   {
      //start service
      StartService(Service, 0, NULL);
      GetStatus(Service);

      // Check the status until the service is no longer start pending.
      if (!QueryServiceStatus( Service, &ssStatus) )
         ErrorHandler("QueryServiceStatus", GetLastError());
      // Save the tick count and initial checkpoint.
      dwStartTickCount = GetTickCount();
      dwOldCheckPoint = ssStatus.dwCheckPoint;

      while (ssStatus.dwCurrentState == SERVICE_START_PENDING)
      {
         // Do not wait longer than the wait hint. A good interval is
         // one tenth the wait hint, but no less than 1 second and no
         // more than 10 seconds.
         dwWaitTime = ssStatus.dwWaitHint / 10;

         if( dwWaitTime < 1000 )
            dwWaitTime = 1000;
         else if ( dwWaitTime > 10000 )
            dwWaitTime = 10000;

         Sleep( dwWaitTime );

         // Check the status again.
         if (!QueryServiceStatus(Service, &ssStatus) )
            break;

         if ( ssStatus.dwCheckPoint > dwOldCheckPoint )
         {
            // The service is making progress.
            dwStartTickCount = GetTickCount();
            dwOldCheckPoint = ssStatus.dwCheckPoint;
         }
         else
         {
            if(GetTickCount()-dwStartTickCount > ssStatus.dwWaitHint)
            {
               // No progress made within the wait hint
               break;
            }
         }
      }
      
      if (ssStatus.dwCurrentState == SERVICE_RUNNING)
      {
         GetStatus(Service);
         dwStatus = NO_ERROR;
      }
      else
      {

         cout << "\nService not started." << endl;
         cout << "  Current State: " << ssStatus.dwCurrentState << endl;
         cout << "  Exit Code: " << ssStatus.dwWin32ExitCode << endl;
         cout << "  Service Specific Exit Code: " << ssStatus.dwServiceSpecificExitCode << endl;
         cout << "  Check Point: " << ssStatus.dwCheckPoint << endl;
         cout << "  Wait Hint: " << ssStatus.dwWaitHint << endl;
         dwStatus = GetLastError();
      }    
   }

   CloseServiceHandle(scm);
    CloseServiceHandle(Service);
    return true;
}


////////////////////////////////////////////////////////////////////////////////
// Purpose   :Control service (STOP, PAUSE, CONTINUE).
// Parameter:N/A
// Returns   :N/A
////////////////////////////////////////////////////////////////////////////////
bool ServiceControl(char* CONTROL)
{
   SC_HANDLE service;
   SC_HANDLE scm;
   BOOL SUCCESS;
   SERVICE_STATUS status;

   //Open connection to SCM
   scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
   if (!scm)
      ErrorHandler("OpenSCManager", GetLastError());

   //Get service's handle
   service = OpenService(scm, ServiceName, SERVICE_ALL_ACCESS);
   if (!service)
      ErrorHandler("OpenService", GetLastError());
   
   //stop the service
   if (stricmp(CONTROL, "STOP") == 0)
   {
      cout << "Service is stopping..." << endl;
      SUCCESS = ControlService(service, SERVICE_CONTROL_STOP, &status);
   }
   //pause the service
   else if (stricmp(CONTROL, "PAUSE") == 0)
   {
      cout << "Service is pausing..." << endl;
      SUCCESS = ControlService(service, SERVICE_CONTROL_PAUSE, &status);
   }
   //continue the service
   else if (stricmp(CONTROL, "RESUME") == 0)
   {
      cout << "Service is resuming..." << endl;
      SUCCESS = ControlService(service, SERVICE_CONTROL_CONTINUE, &status);
   }
   if (!SUCCESS)
      ErrorHandler("ControlService", GetLastError());
   else
      GetStatus(service);

   //Clean up
   CloseServiceHandle(service);
   CloseServiceHandle(scm);

   return true;
}


////////////////////////////////////////////////////////////////////////////////
// Purpose   :Get the current status of the service
// Parameter:service handle.
// Returns   :N/A
////////////////////////////////////////////////////////////////////////////////
void GetStatus(SC_HANDLE service)
{
   BOOL SUCCESS;
   SERVICE_STATUS status;   
   DWORD CurrentState;

   SUCCESS = QueryServiceStatus(service, &status);
   
   switch(status.dwCurrentState)
   {
      case SERVICE_RUNNING:
         CurrentState = SERVICE_RUNNING;
         cout << "Service RUNNING." << endl;
         break;
      case SERVICE_STOPPED:
         CurrentState = SERVICE_STOPPED;
         cout << "Service STOPPED." << endl;
         break;
      case SERVICE_PAUSED:
         CurrentState = SERVICE_PAUSED;
         cout << "Service PAUSED." << endl;
         break;
      case SERVICE_CONTINUE_PENDING:
         CurrentState = SERVICE_CONTINUE_PENDING;
         cout << "Service is resuming..." << endl;
         break;
      case SERVICE_PAUSE_PENDING:
         CurrentState = SERVICE_PAUSE_PENDING;
         cout << "Service is pausing..." << endl;
         break;
      case SERVICE_START_PENDING:
         CurrentState = SERVICE_START_PENDING;
         cout << "Service is starting..." << endl;
         break;
      case SERVICE_STOP_PENDING:
         CurrentState = SERVICE_STOP_PENDING;
         cout << "Service is stopping..." << endl;
         break;
      default:
         break;
   }
   SendStatusToSCM(CurrentState, NO_ERROR, 0, 0, 0);
}

////////////////////////////////////////////////////////////////////////////////
// Purpose   :Get configuration of service
// Parameter:N/A
// Returns   :N/A
////////////////////////////////////////////////////////////////////////////////
bool GetConfiguration()
{
   SC_HANDLE service;
   SC_HANDLE scm;
   BOOL success;
   LPQUERY_SERVICE_CONFIG buffer;
   DWORD sizeNeeded;

   //open connection to SCM
   scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
   if (!scm)
      ErrorHandler("OpenSCManager", GetLastError());

   //get service's handle
   service = OpenService(scm, ServiceName, SERVICE_QUERY_CONFIG);
   if (!service)
      ErrorHandler("OpenService", GetLastError());

    //allocate space for buffer
   buffer = (LPQUERY_SERVICE_CONFIG)LocalAlloc(LPTR, 4096);
    // Get the configuration information.
   success = QueryServiceConfig(service, buffer, 4096, &sizeNeeded);
   if (!success)
      ErrorHandler("QueryServiceConfig", GetLastError());

   //display the info
   cout << "Service name\t: " << buffer->lpDisplayName << endl;
   cout << "Service type\t: " << buffer->dwServiceType << endl;
   cout << "Start type\t: " << buffer->dwStartType << endl;
   cout << "Start name\t: " << buffer->lpServiceStartName << endl;
   cout << "Path\t\t: " << buffer->lpBinaryPathName << endl;

   LocalFree(buffer);

   CloseServiceHandle(service);
   CloseServiceHandle(scm);
   return TRUE;
}


bool ChangeConfig()
{
   SC_HANDLE service;
   SC_HANDLE scm;
   BOOL success;
   SC_LOCK lock;

   //open connection to SCM
   scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS | GENERIC_WRITE);
   if (!scm)
      ErrorHandler("OpenSCManager", GetLastError());

   //lock the database to guarantee exclusive access
   lock = LockServiceDatabase(scm);
   if (lock == 0)
      ErrorHandler("LockServiceDatabase", GetLastError());
      
   //get service's handle
   service = OpenService(scm, ServiceName, SERVICE_ALL_ACCESS);
   if (!service)
      ErrorHandler("OpenService", GetLastError());
   
//   serviceType = SERVICE_NO_CHANGE;
//   serviceStart = SERVICE_NO_CHANGE;
//   serviceError = SERVICE_NO_CHANGE;
//   path = 0;

   //change service config
   success = ChangeServiceConfig(
      service,
      SERVICE_NO_CHANGE,
      SERVICE_NO_CHANGE,
      SERVICE_NO_CHANGE,
      NULL,
      NULL,
      NULL,
      NULL,
      NULL,
      NULL,
      NULL);
   if (!success)
   {
      UnlockServiceDatabase(lock);
      ErrorHandler("ChangeServiceConfig", GetLastError());
   }

   //unlock database
   success = UnlockServiceDatabase(lock);
   if (!success)
      ErrorHandler("UnlockServiceDatabase", GetLastError());
   
   //clean up
   CloseServiceHandle(service);
   CloseServiceHandle(scm);
   return TRUE;
}
I have kinda done my homework, but as I say, most such examples are C++, and I just don't feel confident enough that I know C++ well enough to re-write that code in freeBASIC without it taking me a really long time, and still ending up awfully wrong. :s

I have other examples too... but you may notice that (for C++) this one is pretty Functional, as opposed to Object Oriented, and that's deliberate. It makes more sense to the way my mind works. XD
Drago
Posts: 115
Joined: Aug 10, 2005 13:15

Postby Drago » Feb 04, 2010 9:36

D.J.Peters
Posts: 7659
Joined: May 28, 2005 3:28

Postby D.J.Peters » Feb 04, 2010 14:56

only translated and compiled but !!! not tested !!!

Code: Select all

/'**************************************************************
*    TEMPLATE CPP For SERVICE PROGRAM        01/08/2003        *
*    BY JONATHAN NG                                            *
*    TEMPLATE FreeBASIC For SERVICE PROGRAM   02/2010          *
*    BY DJLinux                                                *
*                                                              *
*                                                              *
*    Usage:                                                    *
*      1) Add service.cpp To project workspace                 *
*      2) Change the ServiceName under the global variable     *
*         To desire Name                                       *
*      3) Add your Function into ServiceThread()               *
*      4) DONE!                                                *
*                                                              *
*      Install:                                                *
*      1) Use Command prompt.                                  *
*      2) Locate the exe.                                      *
*      3) Type <programname> -i    Install                     *
*         Type <programname> -u    Uninstall                   *
*         Type <programname> HELP  For more                    *
*                                                              *
***************************************************************'/

#include "windows.bi"
'#include <iostream.h>
'#include "crt/stdio.bi"

type ErrEntry
  as Integer     code
  as zstring ptr msg
end type

const nErrList = 18
dim shared as ErrEntry ErrList(nErrList-1) = { _
(    0, @"No error" ), _
( 1055, @"The service database is locked." ), _
( 1056, @"An instance of the service is already running." ), _
( 1060, @"The service does not exist as an installed service." ), _
( 1061, @"The service cannot accept control messages at this time." ), _
( 1062, @"The service has not been started." ), _
( 1063, @"The service process could not connect to the service controller." ), _
( 1064, @"An exception occurred in the service when handling the control request." ), _
( 1065, @"The database specified does not exist." ), _
( 1066, @"The service has returned a service-specific error code." ), _
( 1067, @"The process terminated unexpectedly." ), _
( 1068, @"The dependency service or group failed to start." ), _
( 1069, @"The service did not start due to a logon failure." ), _
( 1070, @"After starting, the service hung in a start-pending state." ), _
( 1071, @"The specified service database lock is invalid." ), _
( 1072, @"The service marked for deletion." ), _
( 1073, @"The service already exists." ), _
( 1078, @"The name is already in use as either a service name or a service display name." ) _
}

' Global
dim shared as integer          hLog
dim shared as zstring ptr      ServiceName = @"ServiceTest" ' Name of the service
dim shared as HANDLE           terminateEvent = NULL        ' Event used To hold ServerMain from completing
                                                            ' Handle used To communicate status info With
                                                            ' the SCM. Created by RegisterServiceCtrlHandler
dim shared as HANDLE           threadHandle = 0             ' Thread For the actual work
dim shared as BOOL             pause_Service = FALSE        ' Flags holding current state of service
dim shared as BOOL             running_Service = FALSE      '
dim shared as SERVICE_STATUS_HANDLE serviceStatusHandle     '

declare function ServiceThread stdcall (lParam as LPDWORD) as DWORD
declare function InitService cdecl () as BOOL
declare function SendStatusToSCM cdecl (dwCurrentState as DWORD, _
                                        dwWin32ExitCode as DWORD, _
                                        dwServiceSpecificExitCode as DWORD, _
                                        dwCheckPoint as DWORD, _
                                        dwWaitHint   as DWORD) as BOOL
declare sub      ResumeService cdecl ()
declare sub      PauseService cdecl ()
declare sub      StopService cdecl ()
declare sub      terminate cdecl (_Error as DWORD)
declare sub      ServiceCtrlHandler cdecl (controlCode as DWORD )
declare sub      ServiceMain cdecl (argc as DWORD, argv as LPTSTR ptr)
declare sub      ErrorHandler cdecl (s as zstring ptr,_Err as Integer)
declare sub      GetStatus cdecl (service as SC_HANDLE)
declare sub      ShowUsage cdecl ()
' service config program tasks
declare function InstallService cdecl () as bool
declare function UninstallService cdecl () as bool
declare function GetConfiguration cdecl () as bool
declare function ChangeConfig cdecl () as bool
' service control program tasks
declare function ServiceRun cdecl () as bool
declare function ServiceControl cdecl (CONTROL as zstring ptr) as bool

'
' main
'
'void main(Int argc, char *argv[])
dim as SERVICE_TABLE_ENTRY serviceTable(3) = { _
( ServiceName, cptr(LPSERVICE_MAIN_FUNCTION, @ServiceMain) ), _
( NULL, NULL) }

dim as BOOL success
       
if __FB_ARGC__=2 then
  If instr(command,"-i") then
    InstallService()
  ElseIf instr(command,"-u") then
    UninstallService()
  ElseIf instr(command,"-r") then
    ServiceRun()
  ElseIf instr(command,"-s") then
    ServiceControl("STOP")
  ElseIf instr(command,"-p") then
    ServiceControl("PAUSE")
  ElseIf instr(command,"-c") then
    ServiceControl("RESUME")
  ElseIf instr(command,"status") then
    dim as SC_HANDLE scm, service
    ' Open connection To SCM
    scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS)
    If (scm=0) then
      ErrorHandler("OpenSCManager", GetLastError())
    end if
    ' Get service's handle
    service = OpenService(scm, ServiceName, SERVICE_ALL_ACCESS)
    If (service=0) then
      ErrorHandler("OpenService", GetLastError())
      print "STATUS: ";
      GetStatus(service)
    end if
  ElseIf instr(command,"config") then
    GetConfiguration()
  ElseIf instr(command,"help") then
    ShowUsage()
    ' add other custom commands here And
    ' update ShowUsage Function
  Else
    ShowUsage()
  end if

Else
  ' register With SCM
  success = StartServiceCtrlDispatcher(@serviceTable(0))
  If (success=0) then
    ErrorHandler("StartServiceCtrlDispatcher",GetLastError())
  end if
end if

end


sub ServiceMain cdecl (argc as DWORD,argv as LPTSTR ptr)
  dim as BOOL success
  ' immediately Call registration Function
  serviceStatusHandle = RegisterServiceCtrlHandler( _
                        ServiceName,  _
                        cptr(LPHANDLER_FUNCTION, _
                        @ServiceCtrlHandler) )
  If (serviceStatusHandle=NULL) then
    terminate(GetLastError())
    Return
  end if

  ' notify SCM
  success = SendStatusToSCM(SERVICE_START_PENDING, NO_ERROR, 0 , 1, 5000)
  If (success=0) then
    terminate(GetLastError())
    Return
  end if

  ' create termination event
  terminateEvent = CreateEvent (0, TRUE, FALSE, 0)
  If (terminateEvent=0) then
    terminate(GetLastError())
    Return
  end if

  ' notify SCM
  success = SendStatusToSCM(SERVICE_START_PENDING, NO_ERROR, 0 , 2, 1000)
  If (success=0) then
    terminate(GetLastError())
    Return
  end if

  /'
  'check For startup parameter
  If (argc == 2) then

  Else

  end if
  '/

  ' notify SCM
  success = SendStatusToSCM(SERVICE_START_PENDING, NO_ERROR, 0 , 3, 5000)
  If (success=0) then
    terminate(GetLastError())
    Return
  end if

  ' start service
  success = InitService()
  If (success=0) then
    terminate(GetLastError())
    Return
  end if

  ' notify SCM service Is runnning
  success = SendStatusToSCM(SERVICE_RUNNING, NO_ERROR, 0 , 0, 0)
  If (success=0) then
    terminate(GetLastError())
    Return
  end if

  ' Wait For Stop signal And Then terminate
  WaitForSingleObject(terminateEvent, INFINITE)

  terminate(0)
end sub

function ServiceThread stdcall (lParam as LPDWORD) as DWORD
  'Do YOUR THINGS HERE
  Return 0
end function

'initialises the service by starting its thread
function InitService cdecl () as bool
  dim as DWORD id

  ' Start the service's thread
  threadHandle = CreateThread(NULL, _
                              0, _
                              cptr(LPTHREAD_START_ROUTINE, _
                              @ServiceThread), _
                              NULL, _
                              0, _
                              @id)
  If (threadHandle =0) then
    Return FALSE
  Else
    running_Service = TRUE
    Return TRUE
  end if
end function

'resumes paused service
sub ResumeService cdecl ()
  pause_Service = FALSE
  ResumeThread(threadHandle)
end sub

' pauses service
sub PauseService cdecl ()
  pause_Service = TRUE
  SuspendThread(threadHandle)
end sub

'stops service by allowing ServiceMain To complete
sub StopService cdecl ()
  running_Service = FALSE
  ' set the event that Is holding ServiceMain
  SetEvent(terminateEvent)
end sub

'this Function consolidates the activities of updating
'the service status With SetServiceStatus
function SendStatusToSCM cdecl (dwCurrentState as DWORD, _
                                dwWin32ExitCode as DWORD, _
                                dwServiceSpecificExitCode as DWORD, _
                                dwCheckPoint as DWORD, _
                                dwWaitHint as DWORD) as bool
  dim as BOOL success
  dim as SERVICE_STATUS serviceStatus

  ' fill in all of the SERVICE_STATUS fields
  serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS
  serviceStatus.dwCurrentState = dwCurrentState

  ' If in the process of something, Then accept
  ' no control events, Else accept anything
  If (dwCurrentState = SERVICE_START_PENDING) then
    serviceStatus.dwControlsAccepted = 0
  Else
    serviceStatus.dwControlsAccepted = _
                        SERVICE_ACCEPT_STOP or _
                        SERVICE_ACCEPT_PAUSE_CONTINUE or _
                        SERVICE_ACCEPT_SHUTDOWN
  end if

  ' If a specific Exit code Is defines, set up the win32 Exit code properly
  If (dwServiceSpecificExitCode = 0) then
    serviceStatus.dwWin32ExitCode = dwWin32ExitCode
  Else
    serviceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR
  end if

  serviceStatus.dwServiceSpecificExitCode = dwServiceSpecificExitCode
  serviceStatus.dwCheckPoint = dwCheckPoint
  serviceStatus.dwWaitHint = dwWaitHint

  success = SetServiceStatus (serviceStatusHandle, @serviceStatus)
  If (success=0) then
    StopService()
  end if
  Return success
end function

sub ServiceCtrlHandler cdecl (controlCode as DWORD)
  dim as DWORD currentState = 0
  dim as BOOL success

  select case controlCode
    ' START = ServiceMain()

    ' Stop
    Case SERVICE_CONTROL_STOP:
      currentState = SERVICE_STOP_PENDING
      ' notify SCM
      success = SendStatusToSCM(SERVICE_STOP_PENDING, _
                                NO_ERROR, _
                                0, _
                                1, _
                                5000)
      ' Stop service
      StopService()
      Return

    ' PAUSE
    Case SERVICE_CONTROL_PAUSE:
      If running_Service and (pause_Service=0) then
        ' notify SCM
        success = SendStatusToSCM(SERVICE_PAUSE_PENDING, _
                                  NO_ERROR, _
                                  0, _
                                  1, _
                                  1000)
        PauseService()
        currentState = SERVICE_PAUSED
      end if

    ' Resume
    Case SERVICE_CONTROL_CONTINUE:
      If (running_Service and pause_Service) then
        ' notify SCM
        success = SendStatusToSCM(SERVICE_CONTINUE_PENDING, _
                                  NO_ERROR, _
                                  0, _
                                  1, _
                                  1000)
        ResumeService()
        currentState = SERVICE_RUNNING
      end if

    ' UPDATE
    Case SERVICE_CONTROL_INTERROGATE:
      ' update status Out of switch()

    Case SERVICE_CONTROL_SHUTDOWN:
      ' Do nothing
      Return
    Case Else
  end select

  ' notify SCM current state
  SendStatusToSCM(currentState, NO_ERROR, 0, 0, 0)
end sub

'handle an Error from ServiceMain by cleaning up And tell SCM service didn't start.
sub terminate cdecl (_Error as DWORD)
  ' Close event handle
  If (terminateEvent) then
    CloseHandle(terminateEvent)
  end if
  ' notify SCM service stopped
  If (serviceStatusHandle) then
    SendStatusToSCM(SERVICE_STOPPED, _Error, 0, 0, 0)
  end if
  ' Close thread handle
  If (threadHandle) then
    CloseHandle(threadHandle)
  end if
end sub

sub ErrorHandler cdecl (s as zstring ptr,_Err as Integer)

  print *s & " failed"
  print "Error (" & _Err & "): ";
  dim as Integer i
  For i = 0 to i < nErrList-1
    If (ErrList(i).code = _Err) then
      print ErrList(i).msg
      exit for
    end if
  next
  If (i = nErrList) then
    print "unknown error"
  end if

  print

  hLog = FreeFile
  open "server.log" for append as #hLog
  print #hLog, *s & " failed, error code = " & _Err
  close hLog

  ExitProcess(_Err)
end sub

sub ShowUsage cdecl()
  print
  print "USAGE:"
  print !"  server -i\tInstall service"
  print !"  server -u\tUninstall service"
  print !"  server -r\tRun service"
  print !"  server -s\tStop service"
  print !"  server -p\tPause service"
  print !"  server -c\tResume service"
  print !"server status\tCurrent status"
  print
end sub


''''''''''''''''''''''''''''''''''''''''
' Purpose        :Install service into SCM.
' Parameter:N/A
' Returns        :N/A
''''''''''''''''''''''''''''''''''''''''
function InstallService cdecl () as bool
  dim as SC_HANDLE newService
  dim as SC_HANDLE scm
  dim as string szBuffer=string(MAX_PATH,0)
  dim as string szPath  =string(MAX_PATH,0)

  'Get file path
  GetModuleFileName( GetModuleHandle(NULL), szPath, MAX_PATH )
  dim as integer i,slen=len(szPath)
  for i=sLen-1 to 0 step -1
    if szPath[i]=asc("\") then exit for
  next
  sLen=i
  for i=0 to sLen
    szBuffer[i]=szPath[i]
  next

  'Open connection To SCM
  scm = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE)
  If (scm=0) then
    ErrorHandler("OpenSCManager", GetLastError())
  end if
  'install service
  newService = CreateService(scm                      , _ ' scm database
                             ServiceName              , _ ' service Name
                             ServiceName              , _ ' display Name
                             SERVICE_ALL_ACCESS       , _ ' Access rights To the service
                             SERVICE_WIN32_OWN_PROCESS, _ ' service Type
                             SERVICE_AUTO_START       , _ ' service start Type
                             SERVICE_ERROR_NORMAL     , _ ' Error control Type
                             szBuffer                 , _ ' service path
                             NULL                     , _ ' no load ordering group
                             NULL                     , _ ' no tag identifier
                             NULL                     , _ ' no dependencies       
                             NULL                     , _ ' LocalSystem account
                             NULL)                        ' no password
  If (newService=0) then
    ErrorHandler("CreateService", GetLastError())
    Return false
  Else
    print "Service Installed"
    ServiceRun()
  end if

  ' clean up
  CloseServiceHandle(newService)
  CloseServiceHandle(scm)

  Return true
end function

''''''''''''''''''''''''''''''''''''''''
' Purpose        :Uninstall service from SCM.
' Parameter:N/A
' Returns        :N/A
''''''''''''''''''''''''''''''''''''''''
function UninstallService cdecl () as bool

  dim as SC_HANDLE service
  dim as SC_HANDLE scm
  dim as BOOL SUCCESS
  dim as SERVICE_STATUS status

  ' Open connection To SCM
  scm = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE)
  If (scm=0) then
    ErrorHandler("OpenSCManager", GetLastError())
  end if

  ' Get service's handle
  service = OpenService(scm, ServiceName, SERVICE_ALL_ACCESS) '!!! or Delete)
  If (service=0) then
    ErrorHandler("OpenService", GetLastError())
  end if

  ' Get service status
  SUCCESS = QueryServiceStatus(service, @status)
  If (SUCCESS=0) then
    ErrorHandler("QueryServiceStatus", GetLastError())
  end if

  ' Stop service If necessary               
  If (status.dwCurrentState <> SERVICE_STOPPED) then
    print "Stopping service..."
    SUCCESS = ControlService(service, SERVICE_CONTROL_STOP, @status)
    If (SUCCESS=0) then
      ErrorHandler("ControlService", GetLastError())
    end if
    Sleep(500)
  end if

  ' Delete service
  SUCCESS = DeleteService(service)
  If (SUCCESS) then
    print "Service Uninstalled"
  Else
    ErrorHandler("DeleteService", GetLastError())
  end if
  ' Clean up
  CloseServiceHandle(service)
  CloseServiceHandle(scm)

  Return true
end function


''''''''''''''''''''''''''''''''''''''''
' Purpose        :Run service
' Parameter:N/A
' Returns        :N/A
''''''''''''''''''''''''''''''''''''''''
function ServiceRun cdecl () as bool
  dim as SC_HANDLE scm, Service
  dim as SERVICE_STATUS ssStatus
  dim as DWORD dwOldCheckPoint
  dim as DWORD dwStartTickCount
  dim as DWORD dwWaitTime
  dim as DWORD dwStatus

  ' Open connection To SCM
  scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS)
  If (scm=0) then
    ErrorHandler("OpenSCManager", GetLastError())
  end if
  ' Open service
  Service = OpenService(scm, ServiceName, SERVICE_ALL_ACCESS)
  If (Service=0) then
    ErrorHandler("OpenService", GetLastError())
    Return false
  Else
    ' start service
    StartService(Service, 0, NULL)
    GetStatus(Service)

    ' Check the status Until the service Is no longer start pending.
    If (QueryServiceStatus( Service, @ssStatus)=0 ) then
      ErrorHandler("QueryServiceStatus", GetLastError())
    end if
    ' Save the tick count And initial checkpoint.
    dwStartTickCount = GetTickCount()
    dwOldCheckPoint = ssStatus.dwCheckPoint

    While (ssStatus.dwCurrentState = SERVICE_START_PENDING)
      ' Do Not Wait longer than the Wait hint. A good interval Is
      ' one tenth the Wait hint, but no less than 1 second And no
      ' more than 10 seconds.
      dwWaitTime = ssStatus.dwWaitHint / 10

      If ( dwWaitTime < 1000 ) then
        dwWaitTime = 1000
      ElseIf ( dwWaitTime > 10000 ) then
        dwWaitTime = 10000
      end if
      Sleep( dwWaitTime )

      ' Check the status again.
      If (QueryServiceStatus(Service, @ssStatus)=0 ) then
        exit while
      end if
     
      If ( ssStatus.dwCheckPoint > dwOldCheckPoint ) then
        ' The service Is making progress.
        dwStartTickCount = GetTickCount()
        dwOldCheckPoint = ssStatus.dwCheckPoint
      Else
        If(GetTickCount()-dwStartTickCount > ssStatus.dwWaitHint) then
          ' No progress made within the Wait hint
          exit while
        end if
      end if 
    wend
  end if

  If (ssStatus.dwCurrentState = SERVICE_RUNNING) then
    GetStatus(Service)
    dwStatus = NO_ERROR
  Else
    print !"\nService not started."
    print "  Current State: " & ssStatus.dwCurrentState
    print "  Exit Code: " & ssStatus.dwWin32ExitCode
    print "  Service Specific Exit Code: " & ssStatus.dwServiceSpecificExitCode
    print "  Check Point: " & ssStatus.dwCheckPoint
    print "  Wait Hint: " & ssStatus.dwWaitHint
    dwStatus = GetLastError()
  end if

  CloseServiceHandle(scm)
  CloseServiceHandle(Service)

  Return true
end function


''''''''''''''''''''''''''''''''''''''''
' Purpose        :Control service (Stop, PAUSE, Continue).
' Parameter:N/A
' Returns        :N/A
''''''''''''''''''''''''''''''''''''''''
function ServiceControl cdecl (CONTROL as zstring ptr) as bool
  dim as SC_HANDLE service
  dim as SC_HANDLE scm
  dim as BOOL SUCCESS
  dim as SERVICE_STATUS status

  ' Open connection To SCM
  scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS)
  If (scm=0) then
    ErrorHandler("OpenSCManager", GetLastError())
  end if

  ' Get service's handle
  service = OpenService(scm, ServiceName, SERVICE_ALL_ACCESS)
  If (service=0) then
    ErrorHandler("OpenService", GetLastError())
  end if

  ' Stop the service
  If instr(*CONTROL, "STOP") then
    print "Service is stopping..."
    SUCCESS = ControlService(service, _
                             SERVICE_CONTROL_STOP, _
                             @status)
    'pause the service
  ElseIf instr(*CONTROL, "PAUSE") then
    print "Service is pausing..."
    SUCCESS = ControlService(service, _
                             SERVICE_CONTROL_PAUSE, _
                             @status)
    ' Continue the service
  ElseIf instr(*CONTROL, "RESUME") then
    print "Service is resuming..."
    SUCCESS = ControlService(service, _
                             SERVICE_CONTROL_CONTINUE, _
                             @status)
  end if

  If (SUCCESS=0) then
    ErrorHandler("ControlService", GetLastError())
  Else
    GetStatus(service)
  end if
 
  'Clean up
  CloseServiceHandle(service)
  CloseServiceHandle(scm)

  Return true
end function


''''''''''''''''''''''''''''''''''''''''
' Purpose        :Get the current status of the service
' Parameter:service handle.
' Returns        :N/A
''''''''''''''''''''''''''''''''''''''''
sub GetStatus cdecl (service as SC_HANDLE)

  dim as BOOL SUCCESS
  dim as SERVICE_STATUS status
  dim as DWORD CurrentState

  SUCCESS = QueryServiceStatus(service, @status)

  select case  (status.dwCurrentState)
    Case SERVICE_RUNNING:
      CurrentState = SERVICE_RUNNING
      print "Service RUNNING."
    Case SERVICE_STOPPED:
      CurrentState = SERVICE_STOPPED
      print "Service STOPPED."
    Case SERVICE_PAUSED:
      CurrentState = SERVICE_PAUSED
      print "Service PAUSED."
    Case SERVICE_CONTINUE_PENDING:
      CurrentState = SERVICE_CONTINUE_PENDING
      print "Service is resuming..."
    Case SERVICE_PAUSE_PENDING:
      CurrentState = SERVICE_PAUSE_PENDING
      print "Service is pausing..."
    Case SERVICE_START_PENDING:
      CurrentState = SERVICE_START_PENDING
      print "Service is starting..."
    Case SERVICE_STOP_PENDING:
      CurrentState = SERVICE_STOP_PENDING
      print "Service is stopping..."
    Case Else
     
  end select
  SendStatusToSCM(CurrentState, NO_ERROR, 0, 0, 0)

end sub

''''''''''''''''''''''''''''''''''''''''
' Purpose        :Get configuration of service
' Parameter:N/A
' Returns        :N/A
''''''''''''''''''''''''''''''''''''''''
function GetConfiguration cdecl () as bool

  dim as SC_HANDLE service
  dim as SC_HANDLE scm
  dim as BOOL success
  dim as LPQUERY_SERVICE_CONFIG buffer
  dim as DWORD sizeNeeded

  ' Open connection To SCM
  scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS)
  If (scm=0) then
    ErrorHandler("OpenSCManager", GetLastError())
  end if

  ' Get service's handle
  service = OpenService(scm, ServiceName, SERVICE_QUERY_CONFIG)
  If (service=0) then
    ErrorHandler("OpenService", GetLastError())
  end if

  ' allocate Space For buffer
  buffer = Callocate(4096)
  ' Get the configuration information.
  success = QueryServiceConfig(service, buffer, 4096, @sizeNeeded)
  If (success=0) then
    ErrorHandler("QueryServiceConfig", GetLastError())
  end if

  ' display the info
  print !"Service name\t: " & *buffer->lpDisplayName
  print !"Service type\t: " & buffer->dwServiceType
  print !"Start type\t: "   & buffer->dwStartType
  print !"Start name\t: "   & *buffer->lpServiceStartName
  print !"Path\t\t: "       & *buffer->lpBinaryPathName

  deallocate(buffer)

  CloseServiceHandle(service)
  CloseServiceHandle(scm)

  Return TRUE
end function


function ChangeConfig cdecl () as bool

  dim as SC_HANDLE service
  dim as SC_HANDLE scm
  dim as BOOL success
  dim as SC_LOCK sLock

  ' Open connection To SCM
  scm = OpenSCManager(NULL, _
                      NULL, _
                      SC_MANAGER_ALL_ACCESS or _
                      GENERIC_WRITE)
  If (scm=0) then
    ErrorHandler("OpenSCManager", GetLastError())
  end if

  ' Lock the database To guarantee exclusive Access
  sLock = LockServiceDatabase(scm)
  If (sLock = 0) then
    ErrorHandler("LockServiceDatabase", GetLastError())
  end if

  ' Get service's handle
  service = OpenService(scm, ServiceName, SERVICE_ALL_ACCESS)
  If (service=0) then
    ErrorHandler("OpenService", GetLastError())
  end if
  ' serviceType = SERVICE_NO_CHANGE
  ' serviceStart = SERVICE_NO_CHANGE
  ' serviceError = SERVICE_NO_CHANGE
  ' path = 0

  ' change service config
  success = ChangeServiceConfig( _
                service, _
                SERVICE_NO_CHANGE, _
                SERVICE_NO_CHANGE, _
                SERVICE_NO_CHANGE, _
                NULL, _
                NULL, _
                NULL, _
                NULL, _
                NULL, _
                NULL, _
                NULL)
  If (success=0) then
    UnlockServiceDatabase(sLock)
    ErrorHandler("ChangeServiceConfig", GetLastError())
  end if

  ' Unlock database
  success = UnlockServiceDatabase(sLock)
  If (success=0) then
    ErrorHandler("UnlockServiceDatabase", GetLastError())
  end if
  ' clean up
  CloseServiceHandle(service)
  CloseServiceHandle(scm)

  Return TRUE
end function

bfuller
Posts: 328
Joined: Jun 02, 2007 12:35
Location: Sydney, Australia

Postby bfuller » Feb 04, 2010 23:44

Joshy,

I'm curious, how did you translate the C++ code to FB? Did you use a special tool, or a 'find and replace' with some word processor, was it manually done, or some combination of all those.

As an exercise, I did a "Compare and Merge" of the two code blocks using MS Word and even though the structure of the blocks are similar, there is mind boggling a lot of actual text changes done.

There is a lot of C and C++ code out there but unfortunately as soon as I see the "void" statement and the squiggly brackets "{" my eyes glaze over. I know there are a lot of similarities between the C style languages and BASIC, I just can't seem to get the hang of it and wondering how I should go about learning. There is a tutorial in one of the old QB Express issues and it is a start but my trouble is that many C and C++ programs seem to take shortcuts and are very hard to follow the logic (I suppose this is true of any other programming language, especially if it not well commented---even though I studied FORTRAN many years ago, I would hate to have to translate something now).

Anyway, just curious to hear how you achieve the translation of such a large block of code so quickly.
D.J.Peters
Posts: 7659
Joined: May 28, 2005 3:28

Postby D.J.Peters » Feb 05, 2010 13:58

bfuller wrote:Did you use a special tool
yes of course the best tool ever my brain :-)

I jump between Assembler for PC and C64, VB, FB, Basic4GL, C/C++, PHP, VBScript and some Microcontroller Assembler (ATMEL, PROPELLER).

Joshy

By the way without the havy good job of the whole FB dev team
it would not possible to translate any code 1:1.
bobsobol
Posts: 65
Joined: Nov 04, 2006 9:19

Postby bobsobol » Feb 05, 2010 15:08

TYVM for the translate Joshy.

Yes, I programmed in Sinclair Basic first, and then Z80 Assembler and moved on to IBM Basic A, Pascal, C, x86 Assembler, 680x0 Assembler, Fortran, COBOL, D, Lua, PHP etc from there, and of course many different dialects.

In College, my Lecturers kept saying about the things you could and couldn't do with different languages... so to prove them wrong, and see how similar the machine code turned out, I wrote the exact same program in TurboBasic, TurboPascal and TurboC++ for DOS, and finished off with as close a translation in COBOL as I could make. COBOL is the oddball because it cannot handle numbers except when stored as strings, so it was larger and more complex to do the same simple task, the Pascal had problems with apostrophes in displayed strings, because TurboPascal can't use C style escape codes or the Basic style Char(x) translation... but other than that, the executable variance was minimal in all other languages.

I still use Borland C++ Builder, and Lazarus Pascal but I generally avoid all the OOP features outside of the VCL or LCL usage, just as I did with VB6 code. No Classes, few Enums, no constructors, destructors, operator overloads etc. So it's basically C manipulating C++ objects that already exist.

Translating code is rather like translating any "human" language... but it does require you to "understand" the origin language. For me, I understand code in high level languages by kind of seeing the Assembler it would equate to, and that becomes so much harder with Objects. If I see a method as a function, and a property as a variable or parameter, I can usually get by. XD But I'm not C shy, and "void main(void){};" doesn't scare me, it's just Objects and C++ness in general.

I flunked Java "big time", and can't get the hang of any .net / mono code at all, for much the same reason. Probably much the same way (as an English speaker) I can handle Japanese, but fail epically with French or Spanish because I can't get the hang of gendered inanimates.

So, again, thank you for the translation. That is something I can work with and figure out from there. :D

Return to “General”

Who is online

Users browsing this forum: No registered users and 4 guests