• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 772
  • Last Modified:

Window's service

Hi All,

I'm in the process of trying to write a Windows 2000 Server service which will every minute move all matching files from one directory to another. However I'm probably being quite thick as it isn't working. Here is my code: (it compiles fine but the service wont start)

#include <errno.h>
#include <io.h>
#include <process.h>
#include <stdio.h>
#include <windows.h>

#define SLEEP_TIME 60

SERVICE_STATUS ServiceStatus;
SERVICE_STATUS_HANDLE hStatus;
PROCESS_INFORMATION fgProc;
STARTUPINFO procSU;
DWORD exitCode;

void ServiceMain(int argc, char** argv);
void ControlHandler(DWORD request);
int InitService();
void move_attachments(char *drive_letter);

void main() {
        SERVICE_TABLE_ENTRY ServiceTable[2];
        ServiceTable[0].lpServiceName = "UK-IT-CRM Attachment mover";
        ServiceTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceMain;

        ServiceTable[1].lpServiceName = NULL;
        ServiceTable[1].lpServiceProc = NULL;
        // Start the control dispatcher thread for our service
        StartServiceCtrlDispatcher(ServiceTable);
}

void ServiceMain(int argc, char** argv) {
        int error;

        ServiceStatus.dwServiceType = SERVICE_WIN32;
        ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
        ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
        ServiceStatus.dwWin32ExitCode = 0;
        ServiceStatus.dwServiceSpecificExitCode = 0;
        ServiceStatus.dwCheckPoint = 0;
        ServiceStatus.dwWaitHint = 0;

        hStatus = RegisterServiceCtrlHandler("UK-IT-CRM Attachment mover", (LPHANDLER_FUNCTION)ControlHandler);
        if (hStatus == (SERVICE_STATUS_HANDLE)0) {
                // Registering Control Handler failed
                return;
        }
        // Initialize Service
        error = InitService();
        if (error == false) {
                // Initialization failed
                ServiceStatus.dwCurrentState = SERVICE_STOPPED;
                ServiceStatus.dwWin32ExitCode = -1;
                SetServiceStatus(hStatus, &ServiceStatus);
                return;
        }
        // We report the running status to SCM.
        ServiceStatus.dwCurrentState = SERVICE_RUNNING;
        SetServiceStatus (hStatus, &ServiceStatus);

        // The worker loop of a service
        while (ServiceStatus.dwCurrentState == SERVICE_RUNNING) {
                /* check if the process is still running */
                GetExitCodeProcess(fgProc.hProcess, &exitCode);

                switch(exitCode) {
                        case STILL_ACTIVE:
                                move_attachments("c:");
                                Sleep(SLEEP_TIME);
                                break;
                        default:
                                ServiceStatus.dwCurrentState = SERVICE_STOPPED;
                                ServiceStatus.dwWin32ExitCode = -1;
                                SetServiceStatus(hStatus, &ServiceStatus);
                                return;
                                break;
                }
        }
        return;
}

int InitService() {
        int result;

        GetStartupInfo(&procSU);
        return(result);
}

void ControlHandler(DWORD request) {
        switch(request) {
                case SERVICE_CONTROL_STOP:
                        ServiceStatus.dwWin32ExitCode = 0;
                        ServiceStatus.dwCurrentState = SERVICE_STOPPED;
                        TerminateProcess(fgProc.hProcess, 0);
                        SetServiceStatus (hStatus, &ServiceStatus);
                return;

                case SERVICE_CONTROL_SHUTDOWN:
                        ServiceStatus.dwWin32ExitCode = 0;
                        ServiceStatus.dwCurrentState = SERVICE_STOPPED;
                        TerminateProcess(fgProc.hProcess, 0);
                        SetServiceStatus (hStatus, &ServiceStatus);
                return;

                default:
                break;
        }

        // Report current status
        SetServiceStatus (hStatus, &ServiceStatus);

        return;
}

void move_attachments(char *drive_letter) {
      char *attachments_path = "";
      char *commandline = "";
      char *filesystem_path = "";

      sprintf(filesystem_path, "%s\\sieFS", drive_letter);
      sprintf(attachments_path, "%s\\att\\", filesystem_path);
      sprintf(filesystem_path, "%s\\S_*.saf", filesystem_path);

      sprintf(commandline, "move /Y %s %s", filesystem_path, attachments_path);
      system(commandline);
}

Ideally I would rather that the drive letter would be read in from a .ini file but thats for a later date, at the moment I'd rather just get it working!

I know that it's probably my use of the system() function which is screwing it up but I can't for the life of me think of a better way of doing this?

If anyone can help I'd really appreciate it!!!

Thanks in advance as always

Gareth
0
g_tunley
Asked:
g_tunley
  • 10
  • 10
  • 3
  • +1
1 Solution
 
itsmeandnobodyelseCommented:
You have to create the service by calling

  - OpenSCManager
  - CreateService

Normally, that is made from the command line bei starting the service prog with -i option. You would have to evaluate commandline arguments in main(int argc, char* argv[]) function and do the install job when "-i" was passed as first argument (argv[1]).

Look at the "service sample" of MSDN to learn more.

Regards, Alex
0
 
Julian HansenCommented:
When you say it is not working - does the service start and then not work or does it not start at all?

I suspect you may be right about the system() call - a better method would be to write a move folder function incorporating the MoveFile api call - shout if you want help with this - I have some fairly simple recursive code somewhere for doing something similar that you can modify.
0
 
g_tunleyAuthor Commented:
Hi Julian,

Thank you for your comments - the service doesn't even actually start!

I've used this code before to create a service so I know that it's not the
actual service code so I guess it must be my move_attachments() routine.

If you could send me some examples of a better way to move these files that
would be fantastic!

Thanks again!

Gareth
0
Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

 
g_tunleyAuthor Commented:
Hi All,

Think it definitely is my system call. If I use the following:

#include <windows.h>
#include <stdio.h>
#include <io.h>
#include <process.h>
#include <errno.h>

#define SLEEP_TIME 60

SERVICE_STATUS ServiceStatus;
SERVICE_STATUS_HANDLE hStatus;
PROCESS_INFORMATION fgProc;
STARTUPINFO procSU;
DWORD exitCode;

void ServiceMain(int argc, char** argv);
void ControlHandler(DWORD request);
int InitService();

void main() {
        SERVICE_TABLE_ENTRY ServiceTable[2];
        ServiceTable[0].lpServiceName = "Test service";
        ServiceTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceMain;

        ServiceTable[1].lpServiceName = NULL;
        ServiceTable[1].lpServiceProc = NULL;
        // Start the control dispatcher thread for our service
        StartServiceCtrlDispatcher(ServiceTable);
}

void ServiceMain(int argc, char** argv) {
        int error;

        ServiceStatus.dwServiceType = SERVICE_WIN32;
        ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
        ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
        ServiceStatus.dwWin32ExitCode = 0;
        ServiceStatus.dwServiceSpecificExitCode = 0;
        ServiceStatus.dwCheckPoint = 0;
        ServiceStatus.dwWaitHint = 0;

        hStatus = RegisterServiceCtrlHandler("Test service", (LPHANDLER_FUNCTION)ControlHandler);
        if (hStatus == (SERVICE_STATUS_HANDLE)0) {
                // Registering Control Handler failed
                return;
        }
        // Initialize Service
        error = InitService();
        if (error == false) {
                // Initialization failed
                ServiceStatus.dwCurrentState = SERVICE_STOPPED;
                ServiceStatus.dwWin32ExitCode = -1;
                SetServiceStatus(hStatus, &ServiceStatus);
                return;
        }
        // We report the running status to SCM.
        ServiceStatus.dwCurrentState = SERVICE_RUNNING;
        SetServiceStatus (hStatus, &ServiceStatus);

        // The worker loop of a service
        while (ServiceStatus.dwCurrentState == SERVICE_RUNNING) {
                /* check if the process is still running */
                GetExitCodeProcess(fgProc.hProcess, &exitCode);

                switch(exitCode) {
                        case STILL_ACTIVE:
                                Sleep(SLEEP_TIME);
                                break;
                        default:
                                ServiceStatus.dwCurrentState = SERVICE_STOPPED;
                                ServiceStatus.dwWin32ExitCode = -1;
                                SetServiceStatus(hStatus, &ServiceStatus);
                                return;
                                break;
                }
        }
        return;
}

int InitService() {
        int result;

        GetStartupInfo(&procSU);
        result = CreateProcess(NULL, "C:\\Progra~1\\Intern~1\\iexplore.exe", NULL, NULL, TRUE, 0, NULL, NULL, &procSU, &fgProc);

        return(result);
}

void ControlHandler(DWORD request) {
        switch(request) {
                case SERVICE_CONTROL_STOP:
                        ServiceStatus.dwWin32ExitCode = 0;
                        ServiceStatus.dwCurrentState = SERVICE_STOPPED;
                        TerminateProcess(fgProc.hProcess, 0);
                        SetServiceStatus (hStatus, &ServiceStatus);
                return;

                case SERVICE_CONTROL_SHUTDOWN:
                        ServiceStatus.dwWin32ExitCode = 0;
                        ServiceStatus.dwCurrentState = SERVICE_STOPPED;
                        TerminateProcess(fgProc.hProcess, 0);
                        SetServiceStatus (hStatus, &ServiceStatus);
                return;

                default:
                break;
        }

        // Report current status
        SetServiceStatus (hStatus, &ServiceStatus);

        return;
}

Then it launches an internet explorer window first time i start the service, if I add it into the:

  switch(exitCode) {
                        case STILL_ACTIVE:
                                Sleep(SLEEP_TIME);
                                break;
                        default:

loop then it launches a new IE window every SLEEP_TIME.

Thanks again everyone

Gareth
0
 
Julian HansenCommented:
;)

Ok,

Lets start with getting the service going. The comments by Alex above are quite correct - if you have not installed your service it can't be started.

I have posted some code here

http://www.managedprofile.com/downloads/simplesvc.zip

This is a stripped down version of a service I developed some time back - it is a skeleton (should be fully functional haven't actually tried to compile it and run it) that includes all the install / remove and run options - including being able to run the program in non-service (console) mode - which is quite nice for testing.

You can also modify the LogMsg function to output info ot a file and then use that at various points to trace the execution of your service code.
0
 
g_tunleyAuthor Commented:
OK further investigation has shown that something is definitely screwy with my code...

If I don't put anything in the InitService() subroutine then the service won't start.

Also if I close for instance the IE window if I get it to launch IE then it stops the service.

Me thinks I need to try and figure this out...
0
 
g_tunleyAuthor Commented:
OK...

When I try to compile the simple service code you sent I get the following:

bcc32 -D_DEBUG -g100 -j25 -Od -r- -k -y -v -vi- -tWC -c -IC:\CBuilderX\include -o"C:\Documents and Settings\uk004521\cbproject\moveattachments\windows\Debug_Build\moveattachments.obj"   moveattachments.cpp
Borland C++ 5.6.4 for Win32 Copyright (c) 1993, 2002 Borland
moveattachments.cpp:
"moveattachments.cpp": E2209 Unable to open include file 'stdafx.h' at line 1
"moveattachments.cpp": E2141 Declaration syntax error at line 17
"moveattachments.cpp": E2451 Undefined symbol 'RUN_AS_SERVICE' in function main(int,char * *) at line 40
"moveattachments.cpp": E2268 Call to undefined function 'printf' in function main(int,char * *) at line 46
"moveattachments.cpp": E2268 Call to undefined function 'GetFileVersion' in function main(int,char * *) at line 57
"moveattachments.cpp": E2451 Undefined symbol 'RUN_STANDALONE' in function main(int,char * *) at line 71
"moveattachments.cpp": W8004 'bRunMode' is assigned a value that is never used in function main(int,char * *) at line 91
"moveattachments.cpp": E2268 Call to undefined function 'dimof' in function InstallService() at line 112
"moveattachments.cpp": W8057 Parameter 'dwArgc' is never used in function __stdcall ServiceMain(unsigned long,char * *) at line 219
"moveattachments.cpp": W8057 Parameter 'lpszArgv' is never used in function __stdcall ServiceMain(unsigned long,char * *) at line 219
"moveattachments.cpp": E2268 Call to undefined function 'vprintf' in function LogMsg(char *,...) at line 229
*** 8 errors in Compile ***
 BCC32 exited with error code: 1
Build cancelled due to errors

I seem to be missing the stdafx.h library? Going to try and download it off the net and try again...

Gareth
0
 
grg99Commented:
One perhaps fatal quibble-- what happens if the file is still being written when you try to move it?  

0
 
Julian HansenCommented:
stdafx.h is the standard general include file generated for all Microsoft Projects - it is not required and you can remove the include - remember to turn off precompiled headers for the project options. Alternatively create an empty stdafx.h and include it.

I will compile and fix errors before uploading - code was provided as a guide not as working solution ;)


Moving a file while writing to it - won't work function should fail. Same as if you did a move from the cmd shell. Processing of other files should continue though.

0
 
g_tunleyAuthor Commented:
Hi Julian,

I guessed it was untested code :-) I assumed though that the errors related ot the missing stdafx.h file!

Debugging it myself atm...

Gareth
0
 
Julian HansenCommented:
Just finished with debug - will upload in next 5 min
0
 
itsmeandnobodyelseCommented:
Here you get the MSDN sample that I modified to my own purposes some time ago. It contains only of service.c and service.h and do the whole installing/removing/starting stuff.

You would have to provide a c or cpp file implementing functions

  - serviceStart
  - serviceStop
  - setStartMode

as described in service.h/service.cpp

Regards, Alex


// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (C) 1993-1996  Microsoft Corporation.  All Rights Reserved.
//
//  MODULE:   service.c
//
//  PURPOSE:  Implements functions required by all services
//            windows.
//
//  FUNCTIONS:
//    main(int argc, char** argv);
//    service_ctrl(DWORD dwCtrlCode);
//    service_main(DWORD dwArgc, char** lpszArgv);
//    CmdInstallService();
//    CmdRemoveService();
//    CmdDebugService(int argc, char** argv);
//    ControlHandler ( DWORD dwCtrlType );
//    GetLastErrorText( char* lpszBuf, DWORD dwSize );
//
//  COMMENTS:
//
//  AUTHOR: Craig Link - Microsoft Developer Support
//  MODIFIED: itsmeandnobodyelse (EE)
//
#include <windows.h>

#include <winerror.h>
#include <stdio.h>
#include <stdlib.h>
#include <process.h>
#include <tchar.h>
#include <winsvc.h>

#include "service.h"



// internal variables
SERVICE_STATUS          ssStatus;       // current status of the service
SERVICE_STATUS_HANDLE   sshStatusHandle;
SERVICE_DESCRIPTION     sdDescription;  // service description structure

DWORD                   dwErr = 0;
BOOL                    bDebug  = FALSE;
BOOL                    bDetach = FALSE;
TCHAR                   szErr[256];
TCHAR                   szPath[1024];
TCHAR                   szServiceName[256];
TCHAR                   szServiceDisplayName[256];
TCHAR                   szServiceDescription[1024];
TCHAR                   szServiceDependencies[256];
TCHAR                   szServiceUser[256];
TCHAR                   szServicePassword[256];

// internal function prototypes
void WINAPI service_ctrl(DWORD dwCtrlCode);
void WINAPI service_main(DWORD dwArgc, char** lpszArgv);
void CmdInstallService();
void CmdRemoveService();
void CmdDebugService(int argc, char** argv);
void CmdDetachService(int argc, char** argv);
BOOL WINAPI ControlHandler ( DWORD dwCtrlType );
char* GetLastErrorText( char* lpszBuf, DWORD dwSize );

//
//  FUNCTION: main
//
//  PURPOSE: entrypoint for service
//
//  PARAMETERS:
//    argc - number of command line arguments
//    argv - array of command line arguments
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//    main() either performs the command line task, or
//    call StartServiceCtrlDispatcher to register the
//    main service thread.  When the this call returns,
//    the service has stopped, so exit.
//
void _CRTAPI1 main(int argc, char** argv)
{
    int     i, ipos, ilen, maxSize;
    char*   pszSrc  = NULL;
    char*   pszDest = NULL;
    char    szArg[256];

    SERVICE_TABLE_ENTRY dispatchTable[] =
    {
//        { TEXT(szServiceName), (LPSERVICE_MAIN_FUNCTION)service_main },
        { NULL, (LPSERVICE_MAIN_FUNCTION)service_main },
        { NULL, NULL }
    };

    strcpy(szServiceName, SZSERVICENAME);
    strcpy(szServiceDisplayName, SZSERVICEDISPLAYNAME);
    strcpy(szServiceDescription, SZSERVICEDESCRIPTION);
    strcpy(szServiceDependencies, SZDEPENDENCIES);

    szServiceUser[0]        = '\0';
    szServicePassword[0]    = '\0';

    if ( GetModuleFileName( NULL, szPath, sizeof(szPath) ) == 0 )
    {
        _tprintf(TEXT("Unable to start %s - %s\n"), TEXT(szServiceDisplayName), GetLastErrorText(szErr, 256));
        return;
    }


    _tprintf(TEXT("Starting %s.\n"), TEXT(szPath));

/*
    ipos = 0;
    for (i = 2; i < argc; i++)
    {
       ilen = strlen(argv[i]);
       if (ipos + ilen + 1 >= sizeof(szServiceDependencies))
          break;
       strcpy(&szServiceDependencies[ipos], argv[i]);
       ipos += ilen + 1;    
    }
    szServiceDependencies[ipos] = '\0';
*/

    ipos = 0;
    for (i = 2; i < argc; i++)
    {  
        pszDest = NULL;
        maxSize = 0;
        ilen    = strlen(argv[i]);
        strcpy(szArg, argv[i]);
        _strlwr(szArg);

        pszSrc  = strchr(argv[i], '=');
           
        if (NULL == pszSrc++)
            continue;

        if (strstr(szArg, SZSERVICE_ABBREVIATION) == szArg)
        {
            maxSize = sizeof(szServiceName);
            pszDest = szServiceName;
        }                                      
        else if (strstr(szArg, SZDISPLAY_ABBREVIATION) == szArg)
        {
            maxSize = sizeof(szServiceDisplayName);
            pszDest = szServiceDisplayName;
        }                                      
        else if (strstr(szArg, SZDESCRIPTION_ABBREVIATION) == szArg)
        {
            maxSize = sizeof(szServiceDescription);
            pszDest = szServiceDescription;
        }                                      
        else if (strstr(szArg, SZUSER_ABBREVIATION) == szArg)
        {
            maxSize = sizeof(szServiceUser);
            pszDest = szServiceUser;
        }                                      
        else if (strstr(szArg, SZPASSWORD_ABBREVIATION) == szArg)
        {
            maxSize = sizeof(szServicePassword);
            pszDest = szServicePassword;
        }                                      
        else if (strstr(szArg, SZDEPENDENCIES_ABBREVIATION) == szArg)
        {
            ipos = strlen(szServiceDependencies);
            if (ipos > 0)
                ipos++;
            pszDest  = szServiceDependencies + ipos;
            maxSize  = sizeof(szServiceDependencies) - ipos;
        }                                      
        else
            continue;

        ilen = strlen(pszSrc);
        if (ilen <= 0 || ilen >= maxSize)
            continue;

        strcpy(pszDest, pszSrc);
    }

    ilen = strlen(szServiceDependencies);
    if (ilen > 0)
        szServiceDependencies[++ilen] = '\0';


    sdDescription.lpDescription = szServiceDescription;


    dispatchTable[0].lpServiceName = TEXT(szServiceName);


    if ( (argc > 1) &&
         ((*argv[1] == '-') || (*argv[1] == '/')) )
    {
        if ( _stricmp( SZINSTALL_STARTMODE, argv[1]+1 ) == 0 )
        {
            _tprintf(TEXT("Calling install...\n"));
            CmdInstallService();
        }
        else if ( _stricmp(SZREMOVE_STARTMODE, argv[1]+1 ) == 0 )
        {
            _tprintf(TEXT("Calling remove...\n"));
            CmdRemoveService();
        }
        else if ( _stricmp(SZDEBUG_STARTMODE, argv[1]+1 ) == 0 )
        {
            _tprintf(TEXT("Calling debug...\n"));
            bDebug = TRUE;
            CmdDebugService(argc, argv);
        }
        else if ( _stricmp( SZDETACH_STARTMODE, argv[1]+1 ) == 0 )
        {
            _tprintf(TEXT("Calling detached...\n"));
            bDetach = TRUE;
            CmdDetachService(argc, argv);
        }
        else
        {
            _tprintf(TEXT("Goto dispatch???\n"));
            goto dispatch;
        }
        exit(0);
    }

    // if it doesn't match any of the above parameters
    // the service control manager may be starting the service
    // so we must call StartServiceCtrlDispatcher
    dispatch:
        // this is just to be friendly
        printf( "%s -install          to install the service\n", SZAPPNAME );
        printf( "%s -remove           to remove the service\n", SZAPPNAME );
        printf( "%s -debug <params>   to run as a console app for debugging\n", SZAPPNAME );
        printf( "\nStartServiceCtrlDispatcher being called.\n" );
        printf( "This may take several seconds.  Please wait.\n" );

        if (!StartServiceCtrlDispatcher(dispatchTable))
            AddToMessageLog(TEXT("StartServiceCtrlDispatcher failed."));
}



//
//  FUNCTION: service_main
//
//  PURPOSE: To perform actual initialization of the service
//
//  PARAMETERS:
//    dwArgc   - number of command line arguments
//    lpszArgv - array of command line arguments
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//    This routine performs the service initialization and then calls
//    the user defined serviceStart() routine to perform majority
//    of the work.
//
void WINAPI service_main(DWORD dwArgc, char** lpszArgv)
{

    // register our service control handler:
    //
    sshStatusHandle = RegisterServiceCtrlHandler( TEXT(szServiceName), service_ctrl);

    if (!sshStatusHandle)
        goto cleanup;

    // SERVICE_STATUS members that don't change in example
    //
    ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
    ssStatus.dwServiceSpecificExitCode = 0;


    // report the status to the service control manager.
    //
    if (!ReportStatusToSCMgr(
        SERVICE_START_PENDING, // service state
        NO_ERROR,              // exit code
        3000))                 // wait hint
        goto cleanup;


    serviceStart( dwArgc, lpszArgv );

cleanup:

    // try to report the stopped status to the service control manager.
    //
    if (sshStatusHandle)
        (void)ReportStatusToSCMgr(
                            SERVICE_STOPPED,
                            dwErr,
                            0);

    return;
}



//
//  FUNCTION: service_ctrl
//
//  PURPOSE: This function is called by the SCM whenever
//           ControlService() is called on this service.
//
//  PARAMETERS:
//    dwCtrlCode - type of control requested
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//
void WINAPI service_ctrl(DWORD dwCtrlCode)
{
    // Handle the requested control code.
    //
    switch(dwCtrlCode)
    {
        // Stop the service.
        //
        // SERVICE_STOP_PENDING should be reported before
        // setting the Stop Event - hServerStopEvent - in
        // serviceStop().  This avoids a race condition
        // which may result in a 1053 - The Service did not respond...
        // error.
        case SERVICE_CONTROL_STOP:
            ReportStatusToSCMgr(SERVICE_STOP_PENDING, NO_ERROR, 0);
            serviceStop();
            return;

        // Update the service status.
        //
        case SERVICE_CONTROL_INTERROGATE:
            break;

        // invalid control code
        //
        default:
            break;

    }

    ReportStatusToSCMgr(ssStatus.dwCurrentState, NO_ERROR, 0);
}



//
//  FUNCTION: ReportStatusToSCMgr()
//
//  PURPOSE: Sets the current status of the service and
//           reports it to the Service Control Manager
//
//  PARAMETERS:
//    dwCurrentState - the state of the service
//    dwWin32ExitCode - error code to report
//    dwWaitHint - worst case estimate to next checkpoint
//
//  RETURN VALUE:
//    TRUE  - success
//    FALSE - failure
//
//  COMMENTS:
//
BOOL ReportStatusToSCMgr(DWORD dwCurrentState,
                         DWORD dwWin32ExitCode,
                         DWORD dwWaitHint)
{
    static DWORD dwCheckPoint = 1;
    BOOL fResult = TRUE;


    if ( (!bDebug) && (!bDetach) ) // when debugging we don't report to the SCM
    {
        if (dwCurrentState == SERVICE_START_PENDING)
            ssStatus.dwControlsAccepted = 0;
        else
            ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;

        ssStatus.dwCurrentState     = dwCurrentState;
        ssStatus.dwWin32ExitCode    = dwWin32ExitCode;
        ssStatus.dwWaitHint         = dwWaitHint;

        if ( ( dwCurrentState == SERVICE_RUNNING ) ||
             ( dwCurrentState == SERVICE_STOPPED ) )
            ssStatus.dwCheckPoint = 0;
        else
            ssStatus.dwCheckPoint = dwCheckPoint++;


        // Report the status of the service to the service control manager.
        //
        if (!(fResult = SetServiceStatus( sshStatusHandle, &ssStatus))) {
            AddToMessageLog(TEXT("SetServiceStatus"));
        }
    }
    return fResult;
}



//
//  FUNCTION: AddToMessageLog(char* lpszMsg)
//
//  PURPOSE: Allows any thread to log an error message
//
//  PARAMETERS:
//    lpszMsg - text for message
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//
void AddToMessageLog(char* lpszMsg)
{
    TCHAR   szMsg[256];
    HANDLE  hEventSource;
    char*  lpszStrings[2];


    if ( (!bDebug) && (!bDetach))
    {
        dwErr = GetLastError();

        // Use event logging to log the error.
        //
        hEventSource = RegisterEventSource(NULL, TEXT(szServiceName));

        _stprintf(szMsg, TEXT("%s error: %d"), TEXT(szServiceName), dwErr);
        lpszStrings[0] = szMsg;
        lpszStrings[1] = lpszMsg;

        if (hEventSource != NULL) {
            ReportEvent(hEventSource, // handle of event source
                EVENTLOG_ERROR_TYPE,  // event type
                0,                    // event category
                0,                    // event ID
                NULL,                 // current user's SID
                2,                    // strings in lpszStrings
                0,                    // no bytes of raw data
                lpszStrings,          // array of error strings
                NULL);                // no raw data

            (void) DeregisterEventSource(hEventSource);
        }
    }
}




///////////////////////////////////////////////////////////////////
//
//  The following code handles service installation and removal
//


//
//  FUNCTION: CmdInstallService()
//
//  PURPOSE: Installs the service
//
//  PARAMETERS:
//    none
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//
void CmdInstallService()
{
    SC_HANDLE   schService;
    SC_HANDLE   schSCManager;
    TCHAR       szExecPath[1024];
    TCHAR*      pszUser;
    TCHAR*      pszPassword;
    DWORD       dwStartType  = SERVICE_DEMAND_START;

    if (strlen(szServiceUser) > 0 && strlen(szServicePassword) > 0)
    {
        pszUser     = szServiceUser;
        pszPassword = szServicePassword;
        dwStartType = SERVICE_AUTO_START;
    }
    else
    {
        pszUser     = NULL;
        pszPassword = NULL;
    }

    schSCManager = OpenSCManager(
                        NULL,                   // machine (NULL == local)
                        NULL,                   // database (NULL == default)
                        SC_MANAGER_ALL_ACCESS   // access required
                        );

    if ( schSCManager )
    {
        _tprintf(TEXT("Calling CreateService using path <%s>\n"), TEXT(szPath));
        strcpy(szExecPath, szPath);
        schService = CreateService(
            schSCManager,               // SCManager database
            TEXT(szServiceName),        // name of service
            TEXT(szServiceDisplayName), // name to display
            SERVICE_ALL_ACCESS,         // desired access
            SERVICE_WIN32_OWN_PROCESS,  // service type
            dwStartType,                // start type
            SERVICE_ERROR_NORMAL,       // error control type
            szExecPath,                     // service's binary
            NULL,                       // no load ordering group
            NULL,                       // no tag identifier
            TEXT(szServiceDependencies),       // dependencies
            TEXT(pszUser),                // LocalSystem account if NULL
            TEXT(pszPassword));           // no password if NULL

        if ( schService )
        {
            _tprintf(TEXT("%s installed as %s.\n"), TEXT(szServiceName), TEXT(szServiceDisplayName) );
           
            ChangeServiceConfig2(schService, SERVICE_CONFIG_DESCRIPTION, &sdDescription);

            if (dwStartType == SERVICE_AUTO_START)
                StartService(schService, 0, NULL);

            CloseServiceHandle(schService);
        }
        else
        {
            _tprintf(TEXT("CreateService failed - %s\n"), GetLastErrorText(szErr, 256));
        }

        CloseServiceHandle(schSCManager);
    }
    else
        _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
}



//
//  FUNCTION: CmdRemoveService()
//
//  PURPOSE: Stops and removes the service
//
//  PARAMETERS:
//    none
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//
void CmdRemoveService()
{
    SC_HANDLE   schService;
    SC_HANDLE   schSCManager;

    schSCManager = OpenSCManager(
                        NULL,                   // machine (NULL == local)
                        NULL,                   // database (NULL == default)
                        SC_MANAGER_ALL_ACCESS   // access required
                        );
    if ( schSCManager )
    {
        schService = OpenService(schSCManager, TEXT(szServiceName), SERVICE_ALL_ACCESS);

        if (schService)
        {
            // try to stop the service
            if ( ControlService( schService, SERVICE_CONTROL_STOP, &ssStatus ) )
            {
                _tprintf(TEXT("Stopping %s."), TEXT(szServiceDisplayName));
                Sleep( 1000 );

                while( QueryServiceStatus( schService, &ssStatus ) )
                {
                    if ( ssStatus.dwCurrentState == SERVICE_STOP_PENDING )
                    {
                        _tprintf(TEXT("."));
                        Sleep( 1000 );
                    }
                    else
                        break;
                }

                if ( ssStatus.dwCurrentState == SERVICE_STOPPED )
                    _tprintf(TEXT("\n%s stopped.\n"), TEXT(szServiceName) );
                else
                    _tprintf(TEXT("\n%s failed to stop.\n"), TEXT(szServiceName) );

            }

            // now remove the service
            if( DeleteService(schService) )
                _tprintf(TEXT("%s removed.\n"), TEXT(szServiceName) );
            else
                _tprintf(TEXT("DeleteService failed - %s\n"), GetLastErrorText(szErr,256));


            CloseServiceHandle(schService);
        }
        else
            _tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(szErr,256));

        CloseServiceHandle(schSCManager);
    }
    else
        _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
}




///////////////////////////////////////////////////////////////////
//
//  The following code is for running the service as a console app
//


//
//  FUNCTION: CmdDebugService(int argc, char**  argv)
//
//  PURPOSE: Runs the service as a console application
//
//  PARAMETERS:
//    argc - number of command line arguments
//    argv - array of command line arguments
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//
void CmdDebugService(int argc, char**  argv)
{
    DWORD dwArgc;
    char** lpszArgv;

#ifdef UNICODE
    lpszArgv = CommandLineToArgvW(GetCommandLineW(), &(dwArgc) );
#else
    dwArgc   = (DWORD) argc;
    lpszArgv = argv;
#endif

    _tprintf(TEXT("Debugging %s.\n"), TEXT(szServiceDisplayName));

    SetConsoleCtrlHandler( ControlHandler, TRUE );

    setStartMode(PSM_DEBUG);
    serviceStart( dwArgc, lpszArgv );
}


//
//  FUNCTION: CmdDetachService(int argc, char**  argv)
//
//  PURPOSE: Runs the service as a detached console application
//
//  PARAMETERS:
//    argc - number of command line arguments
//    argv - array of command line arguments
//
//  RETURN VALUE:
//    none
//
//  COMMENTS:
//
void CmdDetachService(int argc, char**  argv)
{
    DWORD dwArgc;
    char** lpszArgv;

#ifdef UNICODE
    lpszArgv = CommandLineToArgvW(GetCommandLineW(), &(dwArgc) );
#else
    dwArgc   = (DWORD) argc;
    lpszArgv = argv;
#endif

    setStartMode(PSM_DETACH);
    serviceStart( dwArgc, lpszArgv );
}


//
//  FUNCTION: ControlHandler ( DWORD dwCtrlType )
//
//  PURPOSE: Handled console control events
//
//  PARAMETERS:
//    dwCtrlType - type of control event
//
//  RETURN VALUE:
//    True - handled
//    False - unhandled
//
//  COMMENTS:
//
BOOL WINAPI ControlHandler ( DWORD dwCtrlType )
{
    switch( dwCtrlType )
    {
        case CTRL_BREAK_EVENT:  // use Ctrl+C or Ctrl+Break to simulate
        case CTRL_C_EVENT:      // SERVICE_CONTROL_STOP in debug mode
            _tprintf(TEXT("Stopping %s.\n"), TEXT(szServiceName));
            serviceStop();
            return TRUE;
            break;

    }
    return FALSE;
}

//
//  FUNCTION: GetLastErrorText
//
//  PURPOSE: copies error message text to string
//
//  PARAMETERS:
//    lpszBuf - destination buffer
//    dwSize - size of buffer
//
//  RETURN VALUE:
//    destination buffer
//
//  COMMENTS:
//
char* GetLastErrorText( char* lpszBuf, DWORD dwSize )
{
    DWORD dwRet;
    char* lpszTemp = NULL;

    dwRet = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_ARGUMENT_ARRAY,
                           NULL,
                           GetLastError(),
                           LANG_NEUTRAL,
                           (char*)&lpszTemp,
                           0,
                           NULL );

    // supplied buffer is not long enough
    if ( !dwRet || ( (long)dwSize < (long)dwRet+14 ) )
        lpszBuf[0] = TEXT('\0');
    else
    {
        lpszTemp[lstrlen(lpszTemp)-2] = TEXT('\0');  //remove cr and newline character
        _stprintf( lpszBuf, TEXT("%s (0x%x)"), lpszTemp, GetLastError() );
    }

    if ( lpszTemp )
        LocalFree((HLOCAL) lpszTemp );

    return lpszBuf;
}

//
//  FUNCTION: GetExecPath()
//
//  PURPOSE: Gets the executable path
//
//  PARAMETERS:
//    none
//
//  RETURN VALUE:
//    character buffer containing full path of executable
//
TCHAR*  GetExecPath()
{
    return szPath;
}

//
//  FUNCTION: SetDebug()
//
//  PURPOSE: Sets the debug flag to suppress reports to Service Control Manager
//
//  PARAMETERS:
//    trueFalse - debug ON or OFF
//
//  RETURN VALUE:
//    none
//
void  SetDebug(BOOL trueFalse)
{
    bDebug = trueFalse;
}

//
//  FUNCTION: IsDebug()
//
//  PURPOSE:  Checks for the debug flag which must be set when the service is started as console program.
//            The debug flag will suppress reports to Service Control Manager
//
//  PARAMETERS:
//    none
//
//  RETURN VALUE:
//    BOOL flag if debug mode
//
BOOL  IsDebug()
{
    return bDebug;
}

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (C) 1993-1996  Microsoft Corporation.  All Rights Reserved.
//
//  MODULE: service.h
//
//  AUTHOR: Craig Link
//  MODIFIED: itsmeandnobodyelse (EE)
//
//
//  Comments:  The use of this header file and the accompanying service.c
//  file simplifies the process of writting a service.  You as a developer
//  simply need to follow the TODO's outlined in this header file, and
//  implement the serviceStart() and serviceStop() functions.
//  
//  There is no need to modify the code in service.c.  Just add service.c
//  to your project and link with the following libraries...
//
//  libcmt.lib kernel32.lib advapi.lib shell32.lib
//
//  This code also supports unicode.  Be sure to compile both service.c and
//  and code #include "service.h" with the same Unicode setting.
//
//  Upon completion, your code will have the following command line interface
//
//  <service exe> -?                to display this list
//  <service exe> -install          to install the service
//  <service exe> -remove           to remove the service
//  <service exe> -debug <params>   to run as a console app for debugging
//
//  Note: This code also implements Ctrl+C and Ctrl+Break handlers
//        when using the debug option.  These console events cause
//        your ServiceStop routine to be called
//
//        Also, this code only handles the OWN_SERVICE service type
//        running in the LOCAL_SYSTEM security context.
//
//        To control your service ( start, stop, etc ) you may use the
//        Services control panel applet or the NET.EXE program.
//
//        To aid in writing/debugging service, the
//        SDK contains a utility (MSTOOLS\BIN\SC.EXE) that
//        can be used to control, configure, or obtain service status.
//        SC displays complete status for any service/driver
//        in the service database, and allows any of the configuration
//        parameters to be easily changed at the command line.
//        For more information on SC.EXE, type SC at the command line.
//

#ifndef SERVICE_H
#define SERVICE_H


#ifdef __cplusplus
extern "C" {
#endif

typedef unsigned long       DWORD;
typedef int                 BOOL;

//////////////////////////////////////////////////////////////////////////////
//// TODO: change to desired strings or set names in service.c before
////       including this file
////
// name of the executable
#ifndef SZAPPNAME
#   define SZAPPNAME                "TService"
#endif
// internal name of the service
#ifndef SZSERVICENAME
#   define SZSERVICENAME            "TestService"
#endif
// displayed name of the service
#ifndef SZSERVICEDISPLAYNAME
#   define SZSERVICEDISPLAYNAME     "Test Service"
#endif
// description of the service
#ifndef SZSERVICEDESCRIPTION
#   define SZSERVICEDESCRIPTION     "Test Service Description"
#endif
// list of service dependencies - "dep1\0dep2\0\0"
#ifndef SZDEPENDENCIES
#   define SZDEPENDENCIES           ""
#endif





//////////////////////////////////////////////////////////////////////////////

#define SZINSTALL_STARTMODE          "install"
#define SZREMOVE_STARTMODE           "remove"
#define SZDEBUG_STARTMODE            "debug"
#define SZDETACH_STARTMODE           "detach"
#define SZSERVICE_ABBREVIATION       "serv"
#define SZDISPLAY_ABBREVIATION       "disp"
#define SZDESCRIPTION_ABBREVIATION   "desc"
#define SZUSER_ABBREVIATION          "user"
#define SZPASSWORD_ABBREVIATION      "pass"
#define SZDEPENDENCIES_ABBREVIATION  "dep"


typedef enum ProcessStartModeEnum
{  
    PSM_SERVICE,
    PSM_DEBUG,
    PSM_DETACH,
    PSM_MAX  

} ProcessStartMode;

//////////////////////////////////////////////////////////////////////////////
//// TODO: serviceStart()must be defined by in your code.
////       The service should use ReportStatusToSCMgr to indicate
////       progress.  This routine must also be used by StartService()
////       to report to the SCM when the service is running.
////
////       If a ServiceStop procedure is going to take longer than
////       3 seconds to execute, it should spawn a thread to
////       execute the stop code, and return.  Otherwise, the
////       ServiceControlManager will believe that the service has
////       stopped responding
////
void serviceStart(DWORD dwArgc, char** lpszArgv);
void serviceStop();
void setStartMode(ProcessStartMode startMode);

//////////////////////////////////////////////////////////////////////////////



//////////////////////////////////////////////////////////////////////////////
//// The following are procedures which
//// may be useful to call within the above procedures,
//// but require no implementation by the user.
//// They are implemented in service.c

//
//  FUNCTION: ReportStatusToSCMgr()
//
//  PURPOSE: Sets the current status of the service and
//           reports it to the Service Control Manager
//
//  PARAMETERS:
//    dwCurrentState - the state of the service
//    dwWin32ExitCode - error code to report
//    dwWaitHint - worst case estimate to next checkpoint
//
//  RETURN VALUE:
//    TRUE  - success
//    FALSE - failure
//
BOOL ReportStatusToSCMgr(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint);


//
//  FUNCTION: AddToMessageLog(char* lpszMsg)
//
//  PURPOSE: Allows any thread to log an error message
//
//  PARAMETERS:
//    lpszMsg - text for message
//
//  RETURN VALUE:
//    none
//
void AddToMessageLog(char* lpszMsg);


//
//  FUNCTION: GetExecPath()
//
//  PURPOSE: Gets the executable path
//
//  PARAMETERS:
//    none
//
//  RETURN VALUE:
//    character buffer containing full path of executable
//
char*  GetExecPath();

//
//  FUNCTION: IsDebug()
//
//  PURPOSE:  Checks for the debug flag which must be set when the service is started as console program.
//            The debug flag will suppress reports to Service Control Manager
//
//  PARAMETERS:
//    none
//
//  RETURN VALUE:
//    BOOL flag if debug mode
//
BOOL  IsDebug();

//
//  FUNCTION: SetDebug()
//
//  PURPOSE: Sets the debug flag to suppress reports to Service Control Manager
//
//  PARAMETERS:
//    trueFalse - debug ON or OFF
//
//  RETURN VALUE:
//    none
//
void  SetDebug(BOOL trueFalse);

//////////////////////////////////////////////////////////////////////////////


#ifdef __cplusplus
}
#endif

#endif
0
 
Julian HansenCommented:
Just uploaded compilable version to

http://www.managedprofile.com/downloads/simplesvc.zip

Service will install, remove through command line as well as run in stand alone mode.

When service starts it will MessageBeep every 5 seconds and write a message to the file c:\service.log

0
 
Julian HansenCommented:
move code is available at

http://www.managedprofile.com/downloads/movefiles.zip.

Shows a recursive function for recursively moving files from one folder to another.

0
 
grg99Commented:
...  and just to be a bit safer,  it might be a good idea to make sure the user can't plonk their own MOVE.EXE or MOVE.BAT file somewhere in the search path.

0
 
Julian HansenCommented:
grg99

>>  it might be a good idea to make sure the user can't plonk their own MOVE.EXE or MOVE.BAT file somewhere in the search path.

I think I missed something - I am not sure how this would be a problem if you are not using the commandline move function.
0
 
grg99Commented:
Sorry for the confusion, It's not a problem if you've moved on from your original system( "move ..." )    code.

0
 
g_tunleyAuthor Commented:
Hi all,

Don't know if it's my compiler or what but I still can't get the service to compile. I downloaded the file: http://www.managedprofile.com/downloads/simplesvc.zip but I am still getting compile errors.

In the meantime I have modified your movefiles code to take inputs from a .ini file and am using that scheduled via the windows task scheduler to sort out the problem temporarily.

Thanks again everyone for all your help!!!

Gareth
0
 
Julian HansenCommented:
What compile errors are you getting?
0
 
g_tunleyAuthor Commented:
[C++ Error] service.cpp(13): E2209 Unable to open include file 'stdafx.h'
[C++ Error] service.cpp(29): E2141 Declaration syntax error
[C++ Error] service.cpp(56): E2451 Undefined symbol 'RUN_AS_SERVICE'
[C++ Error] service.cpp(63): E2268 Call to undefined function 'printf'
[C++ Error] service.cpp(75): E2268 Call to undefined function 'GetFileVersion'
[C++ Error] service.cpp(92): E2451 Undefined symbol 'RUN_STANDALONE'
[C++ Warning] service.cpp(116): W8004 'bRunMode' is assigned a value that is never used
[C++ Error] service.cpp(141): E2268 Call to undefined function 'dimof'
[C++ Error] service.cpp(292): E2268 Call to undefined function 'vprintf'
0
 
Julian HansenCommented:
Apologies - my error - seems the version on the website was the old code try this.

http://www.managedprofile.com/downloads/simplesvc2.zip

0
 
g_tunleyAuthor Commented:
Ah thank you! Just the 1 error now:

27:     typedef enum RUN_TYPES
28:     {
29:      RUN_STANDALONE,
30:      RUN_AS_SERVICE
31:     };

[C++ Error] service.cpp(31): E2146 Need an identifier to declare

I think it is expecting a variable after the } ? which I guess would be bRunMode ?

Thanks again!!!

Gareth
0
 
g_tunleyAuthor Commented:
Sorted.

Changed the lines to:

typedef enum
{
      RUN_STANDALONE,
      RUN_AS_SERVICE
} RUN_TYPES;

And it now compiles...

Gareth
0
 
Julian HansenCommented:
;) Thank you
0
 
g_tunleyAuthor Commented:
Thank you! Very much appreciated!

Service is now installed with file moving code added and appears to be working fine!!!

Now if only Siebel was this easy to sort out!

Gareth
0

Featured Post

VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

  • 10
  • 10
  • 3
  • +1
Tackle projects and never again get stuck behind a technical roadblock.
Join Now