这里有windows nt service 例子的源代码:
http://codeguru.earthweb.com/system/index.shtml

解决方案 »

  1.   

    我这儿有写nt services封装类
      

  2.   

    to: prodigy
     能不能给我一份,[email protected]
      

  3.   

    这儿有封装的NTService类,可以直接使用,还有注释://-------------------------------NTService.h--------------------------------------------
    #ifndef NTService_h
    #define NTService_h
    class CNTService {
    static BOOL m_bInstance; // only one CNTService object per application

    protected: // data members
    LPCTSTR m_lpServiceName;
    LPCTSTR m_lpDisplayName;
    DWORD m_dwCheckPoint;
    DWORD m_dwErr;
    BOOL m_bDebug; // TRUE if -d was passed to the program
    SERVICE_STATUS m_ssStatus; // current status of the service
    SERVICE_STATUS_HANDLE m_sshStatusHandle;
    DWORD m_dwControlsAccepted; // bit-field of what control requests the
    // service will accept
    // (dflt: SERVICE_ACCEPT_STOP)
    PSID m_pUserSID; // the current user's security identifier
    BOOL m_bWinNT; // TRUE, if this is running on WinNT FALSE on Win95
    BOOL m_fConsoleReady; // parameters to the "CreateService()" function:
    DWORD m_dwDesiredAccess; // default: SERVICE_ALL_ACCESS
    DWORD m_dwServiceType; // default: SERVICE_WIN32_OWN_PROCESS
    DWORD m_dwStartType; // default: SERVICE_AUTO_START
    DWORD m_dwErrorControl; // default: SERVICE_ERROR_NORMAL
    LPCTSTR m_pszLoadOrderGroup; // default: NULL
    DWORD m_dwTagID; // retrieves the tag identifier
    LPCTSTR m_pszDependencies; // default: NULL
    LPCTSTR m_pszStartName; // default: NULL
    LPCTSTR m_pszPassword; // default: NULL
    public: // construction/destruction
    // If <DisplayName> is not set, then it defaults to <ServiceName>.
    CNTService(LPCTSTR ServiceName, LPCTSTR DisplayName = 0);
    ~CNTService(); private: // forbidden functions
    CNTService( const CNTService & );
    CNTService & operator=( const CNTService & ); public: // overridables
    // You have to override the following two functions.
    // "Run()" will be called to start the real
    // service's activity. You must call
    // ReportStatus(SERVICE_RUNNING);
    // before you enter your main-loop !
    // "Stop()" will be called to stop the work of
    // the service. You should break out of the mainloop
    // and return control to the CNTService class.
    //
    // In most cases these functions look like these:
    //
    // void CMyService :: Run(DWORD argc, LPTSTR * argv) {
    // ReportStatus(SERVICE_START_PENDING);
    // // do some parameter processing ...
    // ReportStatus(SERVICE_START_PENDING);
    // // do first part of initialisation ...
    // ReportStatus(SERVICE_START_PENDING);
    // // do next part of initialisation
    // // ...
    // m_hStop = CreateEvent(0, TRUE, FALSE, 0);
    // ReportStatus(SERVICE_RUNNING);
    // while( WaitForSingleObject(m_hStop, 10) != WAIT_OBJECT_0 ) {
    // // do something
    // }
    // if( m_hStop )
    // CloseHandle(m_hStop);
    // }
    //
    // void CMyService :: Stop() {
    // if( m_hStop )
    // SetEvent(m_hStop);
    // ReportStatus(SERVICE_STOP_PENDING);
    // }
    virtual void Run(DWORD argc, LPTSTR * argv) = 0;
    virtual void Stop() = 0; // Pause() and Continue() do nothing by default.
    // You can override them to handle a control request.
    // Pause() should report the status SERVICE_PAUSED
    // and Continue() should report the status SERVICE_RUNNING
    // (see ReportStatus() below).
    // Note that normally these functions will never be called. If
    // you want a service, that accepts PAUSE and CONTINUE control
    // requests, you have to to add SERVICE_ACCEPT_PAUSE_CONTINUE
    // to the m_dwControlsAccepted data member.
    virtual void Pause();
    virtual void Continue(); // Shutdown() will be called, if the service manager
    // requests for the SERVICE_CONTROL_SHUTDOWN control.
    // This control type occurs, when the system shuts down.
    // If you want to process this notification, you have to
    // add SERVICE_ACCEPT_SHUTDOWN to the m_dwControlsAccepted
    // data member (and to override this function). The default
    // implementation of Shutdown() does nothing.
    virtual void Shutdown(); // Call "RegisterService()" after you have constructed
    // a CNTService object:
    // A typical "main()" function of a service looks like this:
    //
    // int main( int argc, char ** argv ) {
    // CMyService serv;
    // exit(serv.RegisterService(argc, argv));
    // }
    //
    // Where "CMyService" is a CNTService derived class.
    // RegisterService() checks the parameterlist. The
    // following parameters will be detected:
    // -i install the service (calls
    // "InstallService()" - see below)
    //
    // -l <account>
    // <account> is the name of a user,
    // under which the service shall run.
    // This option is useful with -i only.
    // <account> needs to have the advanced
    // user-right "Log on as a service"
    // (see User-Manager)
    // <account> should have the following
    // format: "<Domain>\<user>"
    // "EuroS2Team\jko" for instance.
    // The domain "." is predefined as the
    // local machine. So one might use
    // ".\jko" too.
    //
    // -p <password>
    // The password of the user, under which
    // the service shall run. Only useful
    // with -i and -l together.
    //
    // -u uninstall the service (calls
    // "RemoveService()" - see below)
    //
    // -d debug the service (run as console
    // process; calls "DebugService()"
    // see below)
    //
    // -e end the service (if it is running)
    //
    // -s start the service (if it is not running)
    // (Note that *you* normally cannot start
    // an NT-service from the command-line.
    // The SCM can.)
    //
    // Do not use -l and -p, if your service is of type
    // SERVICE_KERNEL_DRIVER or SERVICE_FILE_SYSTEM_DRIVER.
    // Furthermore you canot use -i and -s together. Instead
    // you have to start the command twice, first you install
    // the service, then you start it.
    // If none of the flags -i, -u, -e, -s and -d is set, then the
    // program starts as an NT service (only the SCM can start it
    // this way!).
    // NOTE: If UNICODE is #define'd, then <argc> and <argv>
    // will be ignored and the original commandline
    // of the program will be used to parse the
    // arguments !
    virtual BOOL RegisterService(int argc, char ** argv);

    // "StartDispatcher()" registers one service-procedure
    // to the service control dispatcher (using the predefined
    // "ServiceMain()" function below).
    // Override this funtion, if you want to develop a
    // multithreaded NT-Service.
    virtual BOOL StartDispatcher(); // Override "InstallService()" to manipulate the
    // installation behavior.
    // This function will only be called, if the
    // "-i" flag was passed to "RegisterService()"
    // (see above)
    // After "InstallService()" has completed, you
    // should be able to see the service in the
    // "services" control-panel-applet.
    virtual BOOL InstallService();

    // RemoveService() removes a service from the system's
    // service-table.
    // It first tries to stop the service.
    // This function will be called only if the -u
    // flag was passed to the program. (see "RegisterService()"
    // above)
    // After removal of the service, it should no longer
    // appear in the "services" control-panel-applet.
    virtual BOOL RemoveService();
    // EndService() stops a running service (if the service
    // is running as a service! Does not end a service
    // running as a console program (see DebugService()
    // below))
    virtual BOOL EndService(); // Start the service. Does the same as if the
    // SCM launches the program. Note that this method
    // will create a new instance of the program.
    virtual BOOL StartupService();

    // Run a service as a console application. This makes it
    // easier to debug the service.
    // This function will be called only if the -d flag
    // was passed to the program(see "RegisterService()" above).
    // It transparently calls "Run()". You can simulate a
    // stop-request by pressing either Ctrl-C or Ctrl-Break (that
    // will call the "Stop()" method).
    virtual BOOL DebugService(int argc, char **argv,BOOL faceless=FALSE); //!! TCW MOD - added FACELESS parm to allow Win95 usage. protected: // implementation
    // Override "RegisterApplicationLog()", if you want to register
    // a different message file and/or differend supported types
    // than the default.
    // The proposed message file is the application itself.
    // The proposed types are:
    // EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE
    // This method will be called from inside "InstallService()" (see above)
    // Thus if you support errors only (for instance):
    // void CMyService :: RegisterApplicationLog(LPCTSTR filename, DWORD ) {
    // CNTService::RegisterApplicationLog(filename, EVENTLOG_ERROR_TYPE);
    // }
    // This method will never be called on Win95.
    virtual void RegisterApplicationLog(
    LPCTSTR lpszProposedMessageFile,
    DWORD dwProposedTypes
    ); // "DeregisterApplicationLog()" is called from inside "RemoveService()"
    // (see above) to clear the registry-entries made by
    // "RegisterApplicationLog()"
    virtual void DeregisterApplicationLog(); public: // helpers
    // Retrieve a human-readable error message. The message
    // will be stored in <Buf> which is of size <Size>.
    // Returns a pointer to <Buf>.
    LPTSTR GetLastErrorText(LPTSTR Buf, DWORD Size); // report status to the service-control-manager.
    // <CurState> can be one of:
    // SERVICE_START_PENDING - the service is starting
    // SERVICE_RUNNING - the service is running
    // SERVICE_STOP_PENDING - the service is stopping
    // SERVICE_STOPPED - the service is not running
    // SERVICE_PAUSE_PENDING - the service pause is pending
    // SERVICE_PAUSE - the service is paused
    // SERVICE_CONTINUE_PENDING - the service is about to continue
    BOOL ReportStatus(
    DWORD CurState, // service's state
    DWORD WaitHint = 3000, // expected time of operation in milliseconds
    DWORD ErrExit = 0 //!! TCW MOD - set to nonzero to flag *FATAL* error
    ); // AddToMessageLog() writes a message to the application event-log.
    // (use EventViewer from the menu "Administrative Tools" to watch the log).
    // The <EventType> parameter can be set to one of the following values:
    // EVENTLOG_ERROR_TYPE Error event
    // EVENTLOG_WARNING_TYPE Warning event
    // EVENTLOG_INFORMATION_TYPE Information event
    // EVENTLOG_AUDIT_SUCCESS Success Audit event
    // EVENTLOG_AUDIT_FAILURE Failure Audit event
    // See "ReportEvent()" in the help-topics for further information.
    virtual void AddToMessageLog(
    LPTSTR Message,
    WORD EventType = EVENTLOG_ERROR_TYPE,
    DWORD dwEventID = DWORD(-1)
    ); public: // default handlers
    // The following functions will be used by default.
    // You can provide other handlers. If so, you have to
    // overload several of the "virtual"s above.
    static void WINAPI ServiceCtrl(DWORD CtrlCode);
    static void WINAPI ServiceMain(DWORD argc, LPTSTR * argv);
    static BOOL WINAPI ControlHandler(DWORD CtrlType); //!! TCW MOD - added console support for Faceless Apps. Needed sometimes when something goes wrong.
    public:
    BOOL OsIsWin95() const { return ! m_bWinNT; }
    void SetupConsole();};
    // Retrieve the one and only CNTService object:
    CNTService * AfxGetService();
    #endif // NTService_h
    //-------------------------------NTService.cpp--------------------------------------------
    #include <windows.h>
    #include <tchar.h>
    #include <stdio.h>
    #include <crtdbg.h>#include <io.h> //!! TCW MOD
    #include <fcntl.h> //!! TCW MOD#include "NTService.h"
    #include "NTServiceEventLogMsg.h"
    #ifndef RSP_SIMPLE_SERVICE
    #define RSP_SIMPLE_SERVICE 1
    #endif
    #ifndef RSP_UNREGISTER_SERVICE
    #define RSP_UNREGISTER_SERVICE 0
    #endifBOOL CNTService :: m_bInstance = FALSE;static CNTService * gpTheService = 0; // the one and only instanceCNTService * AfxGetService() { return gpTheService; }
    static LPCTSTR gszAppRegKey = TEXT("SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\");
    static LPCTSTR gszWin95ServKey=TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\RunServices"); //!! TCW MOD
    /////////////////////////////////////////////////////////////////////////////
    // class CNTService -- construction/destructionCNTService :: CNTService( LPCTSTR lpServiceName, LPCTSTR lpDisplayName )
    : m_lpServiceName(lpServiceName)
    , m_lpDisplayName(lpDisplayName ? lpDisplayName : lpServiceName)
    , m_dwCheckPoint(0)
    , m_dwErr(0)
    , m_bDebug(FALSE)
    , m_sshStatusHandle(0)
    , m_dwControlsAccepted(SERVICE_ACCEPT_STOP)
    , m_pUserSID(0)
    , m_fConsoleReady(FALSE)
    // parameters to the "CreateService()" function:
    , m_dwDesiredAccess(SERVICE_ALL_ACCESS)
    , m_dwServiceType(SERVICE_WIN32_OWN_PROCESS)
    , m_dwStartType(SERVICE_AUTO_START)
    , m_dwErrorControl(SERVICE_ERROR_NORMAL)
    , m_pszLoadOrderGroup(0)
    , m_dwTagID(0)
    , m_pszDependencies(0)
    , m_pszStartName(0)
    , m_pszPassword(0)
    {
    _ASSERTE( ! m_bInstance ); OSVERSIONINFO vi;
    vi.dwOSVersionInfoSize=sizeof(vi);  // init this.
    GetVersionEx(&vi);      //lint !e534
    m_bWinNT = (vi.dwPlatformId == VER_PLATFORM_WIN32_NT); m_bInstance = TRUE;
    gpTheService = this;

    // SERVICE_STATUS members that rarely change
    m_ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
    m_ssStatus.dwServiceSpecificExitCode = 0; if( m_bWinNT ) {
    /////////////////////////////////////////////////////////////////////////
    // Providing a SID (security identifier) was contributed by Victor
    // Vogelpoel ([email protected]).
    // The code from Victor was slightly modified. // Get security information of current user
    BYTE security_identifier_buffer[ 4096 ];
    DWORD dwSizeSecurityIdBuffer = sizeof( security_identifier_buffer );
    PSID user_security_identifier = NULL; TCHAR sUserName[ 256 ];
    DWORD dwSizeUserName  =  255; TCHAR sDomainName[ 256 ];
    DWORD dwSizeDomainName = 255; SID_NAME_USE sidTypeSecurityId; ::ZeroMemory( sUserName, sizeof( sUserName ) );
    ::ZeroMemory( sDomainName, sizeof( sDomainName ) );
    ::ZeroMemory( security_identifier_buffer, dwSizeSecurityIdBuffer ); ::GetUserName( sUserName, &dwSizeUserName ); if( ::LookupAccountName(
    0,
    sUserName,
    &security_identifier_buffer,
    &dwSizeSecurityIdBuffer,
    sDomainName,
    &dwSizeDomainName,
    &sidTypeSecurityId
    )) {
    if( ::IsValidSid( PSID(security_identifier_buffer) ) ) {
    DWORD dwSidLen = ::GetLengthSid(PSID(security_identifier_buffer));
    m_pUserSID = PSID(new BYTE [dwSidLen]);
    ::CopySid(dwSidLen, m_pUserSID, security_identifier_buffer);
    _ASSERTE(::EqualSid(m_pUserSID, security_identifier_buffer));
    }
    }
    }
    /////////////////////////////////////////////////////////////////////////
    }
    CNTService :: ~CNTService() {
    _ASSERTE( m_bInstance );
    delete [] LPBYTE(m_pUserSID);
    m_bInstance = FALSE;
    gpTheService = 0;
    }/////////////////////////////////////////////////////////////////////////////
    // class CNTService -- overridables#define NEXT_ARG ((((*Argv)[2])==TEXT('\0'))?(--Argc,*++Argv):(*Argv)+2)
    BOOL CNTService :: RegisterService( int argc, char ** argv ) {
    BOOL (CNTService::* fnc)() = &CNTService::StartDispatcher;
        DWORD Argc;
        LPTSTR * Argv;#ifdef UNICODE
        Argv = CommandLineToArgvW(GetCommandLineW(), &Argc );
    #else
        Argc = (DWORD) argc;
        Argv = argv;
    #endif    while( ++Argv, --Argc ) {
    if( Argv[0][0] == TEXT('-') ) {
    switch( Argv[0][1] ) {
    case TEXT('i'): // install the service
    fnc = &CNTService::InstallService;
    break;
    case TEXT('l'): // login-account (only useful with -i)
    m_pszStartName = NEXT_ARG;
    break;
    case TEXT('p'): // password (only useful with -i)
    m_pszPassword = NEXT_ARG;
    break;
    case TEXT('u'): // uninstall the service
    fnc = &CNTService::RemoveService;
    break;
    case TEXT('s'): // start the service
    fnc = &CNTService::StartupService;
    break;
    case TEXT('e'): // end the service
    fnc = &CNTService::EndService;
    break;
    case TEXT('d'): // debug the service
    case TEXT('f'): //!! TCW MOD faceless non-service (Win95) mode
    #ifdef UNICODE
    ::GlobalFree(HGLOBAL)Argv);
    #endif m_bDebug = TRUE;
    // pass original parameters to DebugService()
    return DebugService(argc, argv,(Argv[0][1]==TEXT('f'))); //!! TCW MOD faceless non-service (Win95) mode
    }
    }
    }#ifdef UNICODE
    ::GlobalFree(HGLOBAL)Argv);
    #endif //!! TCW MOD START - if Win95, run as faceless app.
    if( fnc == &CNTService::StartDispatcher && OsIsWin95() ) {
    // act as if -f was passed anyways.
    m_bDebug = TRUE;
    return DebugService(argc, argv, TRUE);
    }
    //!! TCW MOD END - if Win95, run as faceless app. return (this->*fnc)();
    }
    BOOL CNTService :: StartDispatcher() {
        // Default implementation creates a single threaded service.
    // Override this method and provide more table entries for
    // a multithreaded service (one entry for each thread).
    SERVICE_TABLE_ENTRY dispatchTable[] =
        {
            { LPTSTR(m_lpServiceName), (LPSERVICE_MAIN_FUNCTION)ServiceMain },
            { 0, 0 }
        }; BOOL bRet = StartServiceCtrlDispatcher(dispatchTable);
    if( ! bRet ) {
    TCHAR szBuf[256];
            AddToMessageLog(GetLastErrorText(szBuf,255));
    } return bRet;
    }
    BOOL CNTService :: InstallService() {
        TCHAR szPath[1024]; SetupConsole(); //!! TCW MOD - have to show the console here for the
    // diagnostic or error reason: orignal class assumed
    // that we were using _main for entry (a console app).
    // This particular usage is a Windows app (no console),
    // so we need to create it. Using SetupConsole with _main
    // is ok - does nothing, since you only get one console. if( GetModuleFileName( 0, szPath, 1023 ) == 0 ) {
    TCHAR szErr[256];
    _tprintf(TEXT("Unable to install %s - %s\n"), m_lpDisplayName, GetLastErrorText(szErr, 256));
    return FALSE;
    } BOOL bRet = FALSE; if( OsIsWin95() ) { //!! TCW MOD - code added to install as Win95 service
    // Create a key for that application and insert values for
    // "EventMessageFile" and "TypesSupported"
    HKEY hKey = 0;
    LONG lRet = ERROR_SUCCESS;
    if( ::RegCreateKey(HKEY_LOCAL_MACHINE, gszWin95ServKey , &hKey) == ERROR_SUCCESS ) {
    lRet = ::RegSetValueEx(
    hKey, // handle of key to set value for
    m_lpServiceName, // address of value to set (NAME OF SERVICE)
    0, // reserved
    REG_EXPAND_SZ, // flag for value type
    (CONST BYTE*)szPath,// address of value data
    _tcslen(szPath) + 1 // size of value data
    );
    ::RegCloseKey(hKey);
    bRet=TRUE;
    }
    } else {
    // Real NT services go here.
    SC_HANDLE schSCManager = OpenSCManager(
    0, // machine (NULL == local)
    0, // database (NULL == default)
    SC_MANAGER_ALL_ACCESS // access required
    );
    if( schSCManager ) {
    SC_HANDLE schService = CreateService(
    schSCManager,
    m_lpServiceName,
    m_lpDisplayName,
    m_dwDesiredAccess,
    m_dwServiceType,
    m_dwStartType,
    m_dwErrorControl,
    szPath,
    m_pszLoadOrderGroup,
    ((m_dwServiceType == SERVICE_KERNEL_DRIVER ||
      m_dwServiceType == SERVICE_FILE_SYSTEM_DRIVER) &&
     (m_dwStartType == SERVICE_BOOT_START ||
      m_dwStartType == SERVICE_SYSTEM_START)) ?
    &m_dwTagID : 0,
    m_pszDependencies,
    m_pszStartName,
    m_pszPassword
    ); if( schService ) {
    _tprintf(TEXT("%s installed.\n"), m_lpDisplayName );
    CloseServiceHandle(schService);
    bRet = TRUE;
    } else {
    TCHAR szErr[256];
    _tprintf(TEXT("CreateService failed - %s\n"), GetLastErrorText(szErr, 256));
    } CloseServiceHandle(schSCManager);
     } else {
    TCHAR szErr[256];
    _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
    } if( bRet ) {
    // installation succeeded. Now register the message file
    RegisterApplicationLog(
    szPath, // the path to the application itself
    EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE // supported types
    ); AddToMessageLog(TEXT("Service installed"),EVENTLOG_INFORMATION_TYPE);
    }
    } //!! TCW MOD return bRet;
    }
    BOOL CNTService :: RemoveService() {
    BOOL bRet = FALSE; SetupConsole(); //!! TCW MOD - have to show the console here for the
    // diagnostic or error reason: orignal class assumed
    // that we were using _main for entry (a console app).
    // This particular usage is a Windows app (no console),
    // so we need to create it. Using SetupConsole with _main
    // is ok - does nothing, since you only get one console.
    if( OsIsWin95() ) { //!! TCW MOD - code added to install as Win95 service
    HKEY hKey = 0;
    LONG lRet = ERROR_SUCCESS;
    if( ::RegCreateKey(HKEY_LOCAL_MACHINE, gszWin95ServKey , &hKey) == ERROR_SUCCESS ) {
    lRet = ::RegDeleteValue(hKey, m_lpServiceName);
    ::RegCloseKey(hKey);
    bRet=TRUE;
    }
    } else {
    // Real NT services go here.
    SC_HANDLE schSCManager = OpenSCManager(
    0, // machine (NULL == local)
    0, // database (NULL == default)
    SC_MANAGER_ALL_ACCESS // access required
    );
    if( schSCManager ) {
    SC_HANDLE schService = OpenService(
    schSCManager,
    m_lpServiceName,
    SERVICE_ALL_ACCESS
    ); if( schService ) {
    // try to stop the service
    if( ControlService(schService, SERVICE_CONTROL_STOP, &m_ssStatus) ) {
    _tprintf(TEXT("Stopping %s."), m_lpDisplayName);
    Sleep(1000); while( QueryServiceStatus(schService, &m_ssStatus) ) {
    if( m_ssStatus.dwCurrentState == SERVICE_STOP_PENDING ) {
    _tprintf(TEXT("."));
    Sleep( 1000 );
    } else
    break;
    } if( m_ssStatus.dwCurrentState == SERVICE_STOPPED )
    _tprintf(TEXT("\n%s stopped.\n"), m_lpDisplayName);
     else
      _tprintf(TEXT("\n%s failed to stop.\n"), m_lpDisplayName);
    } // now remove the service
    if( DeleteService(schService) ) {
    _tprintf(TEXT("%s removed.\n"), m_lpDisplayName);
    bRet = TRUE;
    } else {
    TCHAR szErr[256];
    _tprintf(TEXT("DeleteService failed - %s\n"), GetLastErrorText(szErr,256));
    } CloseServiceHandle(schService);
    } else {
    TCHAR szErr[256];
    _tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(szErr,256));
    }   CloseServiceHandle(schSCManager);
     } else {
    TCHAR szErr[256];
    _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
    } if( bRet )
    DeregisterApplicationLog();
    } return bRet;
    }
    BOOL CNTService :: EndService() {
    BOOL bRet = FALSE; SC_HANDLE schSCManager = ::OpenSCManager(
    0, // machine (NULL == local)
    0, // database (NULL == default)
    SC_MANAGER_ALL_ACCESS // access required
    );
    if( schSCManager ) {
    SC_HANDLE schService = ::OpenService(
    schSCManager,
    m_lpServiceName,
    SERVICE_ALL_ACCESS
    ); if( schService ) {
    // try to stop the service
    if( ::ControlService(schService, SERVICE_CONTROL_STOP, &m_ssStatus) ) {
    _tprintf(TEXT("Stopping %s."), m_lpDisplayName);
    ::Sleep(1000); while( ::QueryServiceStatus(schService, &m_ssStatus) ) {
    if( m_ssStatus.dwCurrentState == SERVICE_STOP_PENDING ) {
    _tprintf(TEXT("."));
    ::Sleep( 1000 );
    } else
    break;
    } if( m_ssStatus.dwCurrentState == SERVICE_STOPPED )
    bRet = TRUE, _tprintf(TEXT("\n%s stopped.\n"), m_lpDisplayName);
                    else
                        _tprintf(TEXT("\n%s failed to stop.\n"), m_lpDisplayName);
    } ::CloseServiceHandle(schService);
    } else {
    TCHAR szErr[256];
    _tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(szErr,256));
    }        ::CloseServiceHandle(schSCManager);
        } else {
    TCHAR szErr[256];
    _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
    } return bRet;
    }
    BOOL CNTService :: StartupService() {
    BOOL bRet = FALSE; SC_HANDLE schSCManager = ::OpenSCManager(
    0, // machine (NULL == local)
    0, // database (NULL == default)
    SC_MANAGER_ALL_ACCESS // access required
    );
    if( schSCManager ) {
    SC_HANDLE schService = ::OpenService(
    schSCManager,
    m_lpServiceName,
    SERVICE_ALL_ACCESS
    ); if( schService ) {
    // try to start the service
    _tprintf(TEXT("Starting up %s."), m_lpDisplayName);
    if( ::StartService(schService, 0, 0) ) {
    Sleep(1000); while( ::QueryServiceStatus(schService, &m_ssStatus) ) {
    if( m_ssStatus.dwCurrentState == SERVICE_START_PENDING ) {
    _tprintf(TEXT("."));
    Sleep( 1000 );
    } else
    break;
    } if( m_ssStatus.dwCurrentState == SERVICE_RUNNING )
    bRet = TRUE, _tprintf(TEXT("\n%s started.\n"), m_lpDisplayName);
                    else
                        _tprintf(TEXT("\n%s failed to start.\n"), m_lpDisplayName);
    } else {
    // StartService failed
    TCHAR szErr[256];
    _tprintf(TEXT("\n%s failed to start: %s\n"), m_lpDisplayName, GetLastErrorText(szErr,256));
    } ::CloseServiceHandle(schService);
    } else {
    TCHAR szErr[256];
    _tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(szErr,256));
    }        ::CloseServiceHandle(schSCManager);
        } else {
    TCHAR szErr[256];
    _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
    } return bRet;
    }
    ////////////////////////////////////////////////////////////////////////////
    //!! TCW MOD - faceless window procedure for usage within Win95 (mostly),
    // but can be invoked under NT by using -f
    LRESULT CALLBACK _FacelessWndProc_( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) {
    if (uMsg==WM_QUERYENDSESSION || uMsg==WM_ENDSESSION || uMsg==WM_QUIT) {
    if (lParam==NULL || uMsg==WM_QUIT) {
    DestroyWindow(hwnd); // kill me
    if (AfxGetService()!=NULL)
    AfxGetService()->Stop(); // stop me.
    return TRUE;
    }
    }
    return DefWindowProc(hwnd,uMsg,wParam,lParam);
    }
    ////////////////////////////////////////////////////////////////////////////
    BOOL CNTService :: DebugService(int argc, char ** argv, BOOL faceless) {
        DWORD dwArgc;
        LPTSTR *lpszArgv;#ifdef UNICODE
        lpszArgv = CommandLineToArgvW(GetCommandLineW(), &(dwArgc) );
    #else
        dwArgc   = (DWORD) argc;
        lpszArgv = argv;
    #endif if( !faceless ) { //!! TCW MOD - no faceless, so give it a face.
    SetupConsole(); //!! TCW MOD - make the console for debugging
       _tprintf(TEXT("Debugging %s.\n"), m_lpDisplayName); SetConsoleCtrlHandler(ControlHandler, TRUE);
    } //!! TCW MOD START - if Win95, register server
    typedef DWORD (WINAPI *fp_RegServProc)(DWORD dwProcessId,DWORD dwType);
    fp_RegServProc fncptr=NULL; if( faceless /*&& OsIsWin95()*/ ) {
    WNDCLASS wndclass;
    memset(&wndclass,0,sizeof(WNDCLASS));
    wndclass.lpfnWndProc = _FacelessWndProc_;
    wndclass.hInstance = HINSTANCE(::GetModuleHandle(0));
    wndclass.lpszClassName = TEXT("RRL__FacelessWndProc_");
    ATOM atom = ::RegisterClass(&wndclass);
    HWND hwnd = ::CreateWindow(wndclass.lpszClassName,TEXT(""),0,0,0,0,0,0,0,wndclass.hInstance,0);
    HMODULE hModule = ::GetModuleHandle(TEXT("kernel32.dll"));
    // punch F1 on "RegisterServiceProcess" for what it does and when to use it.
    fncptr=(fp_RegServProc)::GetProcAddress(hModule, "RegisterServiceProcess");
    if (fncptr!=NULL)
    (*fncptr)(0, RSP_SIMPLE_SERVICE);
    }
    //!! TCW MOD END - if Win95, register server
        Run(dwArgc, lpszArgv);#ifdef UNICODE
    ::GlobalFree(HGLOBAL)lpszArgv);
    #endif if (fncptr!=NULL) //!! TCW MOD - if it's there, remove it: our run is over
    (*fncptr)(0, RSP_UNREGISTER_SERVICE); return TRUE;
    }
    void CNTService :: Pause() {
    }
    void CNTService :: Continue() {
    }
    void CNTService :: Shutdown() {
    }
    /////////////////////////////////////////////////////////////////////////////
    // class CNTService -- default handlersvoid WINAPI CNTService :: ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv) {
    _ASSERTE( gpTheService != 0 ); // register our service control handler:
    gpTheService->m_sshStatusHandle = RegisterServiceCtrlHandler(
    gpTheService->m_lpServiceName,
    CNTService::ServiceCtrl
    ); if( gpTheService->m_sshStatusHandle ) // report the status to the service control manager.
    if( gpTheService->ReportStatus(SERVICE_START_PENDING) ){
    gpTheService->Run( dwArgc, lpszArgv );} // try to report the stopped status to the service control manager.
    if( gpTheService->m_sshStatusHandle )
    gpTheService->ReportStatus(SERVICE_STOPPED);
    }
    void WINAPI CNTService :: ServiceCtrl(DWORD dwCtrlCode) {
    _ASSERTE( gpTheService != 0 ); // Handle the requested control code.
    switch( dwCtrlCode ) {
    case SERVICE_CONTROL_STOP:
    // Stop the service.
    gpTheService->m_ssStatus.dwCurrentState = SERVICE_STOP_PENDING;
    gpTheService->Stop();
    break; case SERVICE_CONTROL_PAUSE:
    gpTheService->m_ssStatus.dwCurrentState = SERVICE_PAUSE_PENDING;
    gpTheService->Pause();
    break; case SERVICE_CONTROL_CONTINUE:
    gpTheService->m_ssStatus.dwCurrentState = SERVICE_CONTINUE_PENDING;
    gpTheService->Continue();
    break; case SERVICE_CONTROL_SHUTDOWN:
    gpTheService->Shutdown();
    break; case SERVICE_CONTROL_INTERROGATE:
    // Update the service status.
    gpTheService->ReportStatus(gpTheService->m_ssStatus.dwCurrentState);
    break; default:
    // invalid control code
    break;
    }}
    BOOL WINAPI CNTService :: ControlHandler(DWORD dwCtrlType) {
    _ASSERTE(gpTheService != 0);
    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"), gpTheService->m_lpDisplayName);
    gpTheService->Stop();
    return TRUE;
    }
    return FALSE;
    }
    /////////////////////////////////////////////////////////////////////////////
    // class CNTService -- helpers//!! TCW MOD - added DWORD dwErrExit for error exit value. Defaults to zero
    BOOL CNTService :: ReportStatus(
    DWORD dwCurrentState,
    DWORD dwWaitHint,
    DWORD dwErrExit ) {
    BOOL fResult = TRUE; if( !m_bDebug ) { // when debugging we don't report to the SCM
            if( dwCurrentState == SERVICE_START_PENDING)
                m_ssStatus.dwControlsAccepted = 0;
            else
                m_ssStatus.dwControlsAccepted = m_dwControlsAccepted;        m_ssStatus.dwCurrentState = dwCurrentState;
            m_ssStatus.dwWin32ExitCode = NO_ERROR;
            m_ssStatus.dwWaitHint = dwWaitHint; //!! TCW MOD START - added code to support error exiting
    m_ssStatus.dwServiceSpecificExitCode = dwErrExit;
    if (dwErrExit!=0)
    m_ssStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
    //!! TCW MOD END - added code to support error exiting        if( dwCurrentState == SERVICE_RUNNING ||
                dwCurrentState == SERVICE_STOPPED )
                m_ssStatus.dwCheckPoint = 0;
            else
                m_ssStatus.dwCheckPoint = ++m_dwCheckPoint;        // Report the status of the service to the service control manager.
            if (!(fResult = SetServiceStatus( m_sshStatusHandle, &m_ssStatus))) {
                AddToMessageLog(TEXT("SetServiceStatus() failed"));
            }
        }    return fResult;
    }
    void CNTService :: AddToMessageLog(LPTSTR lpszMsg, WORD wEventType, DWORD dwEventID) {
    m_dwErr = GetLastError(); // use default message-IDs
    if( dwEventID == DWORD(-1) ) {
    switch( wEventType ) {
    case EVENTLOG_ERROR_TYPE:
    dwEventID = MSG_ERROR_1;
    break;
    case EVENTLOG_WARNING_TYPE:
    dwEventID = MSG_WARNING_1;
    break;
    case EVENTLOG_INFORMATION_TYPE:
    dwEventID = MSG_INFO_1;
    break;
    case EVENTLOG_AUDIT_SUCCESS:
    dwEventID = MSG_INFO_1;
    break;
    case EVENTLOG_AUDIT_FAILURE:
    dwEventID = MSG_INFO_1;
    break;
    default:
    dwEventID = MSG_INFO_1;
    break;
    }
    } // Use event logging to log the error.
    HANDLE hEventSource = RegisterEventSource(0, m_lpServiceName); if( hEventSource != 0 ) {
    LPCTSTR lpszMessage = lpszMsg; ReportEvent(
    hEventSource, // handle of event source
    wEventType, // event type
    0, // event category
    dwEventID, // event ID
    m_pUserSID, // current user's SID
    1, // strings in lpszStrings
    0, // no bytes of raw data
    &lpszMessage, // array of error strings
    0 // no raw data
    ); ::DeregisterEventSource(hEventSource);
        }
    }
    LPTSTR CNTService :: GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize ) {
        LPTSTR lpszTemp = 0;    DWORD dwRet = ::FormatMessage(
    FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_ARGUMENT_ARRAY,
    0,
    GetLastError(),
    LANG_NEUTRAL,
    (LPTSTR)&lpszTemp,
    0,
    0
    );    if( !dwRet || (dwSize < dwRet+14) )
            lpszBuf[0] = TEXT('\0');
        else {
            lpszTemp[_tcsclen(lpszTemp)-2] = TEXT('\0');  //remove cr/nl characters
            _tcscpy(lpszBuf, lpszTemp);
        }    if( lpszTemp )
            LocalFree(HLOCAL(lpszTemp));    return lpszBuf;
    }/////////////////////////////////////////////////////////////////////////////
    // class CNTService -- implementationvoid CNTService :: RegisterApplicationLog( LPCTSTR lpszFileName, DWORD dwTypes ) {
    TCHAR szKey[256];
    _tcscpy(szKey, gszAppRegKey);
    _tcscat(szKey, m_lpServiceName);
    HKEY hKey = 0;
    LONG lRet = ERROR_SUCCESS;

    // Create a key for that application and insert values for
    // "EventMessageFile" and "TypesSupported"
    if( ::RegCreateKey(HKEY_LOCAL_MACHINE, szKey, &hKey) == ERROR_SUCCESS ) {
    lRet = ::RegSetValueEx(
    hKey, // handle of key to set value for
    TEXT("EventMessageFile"), // address of value to set
    0, // reserved
    REG_EXPAND_SZ, // flag for value type
    (CONST BYTE*)lpszFileName, // address of value data
    _tcslen(lpszFileName) + 1 // size of value data
    ); // Set the supported types flags.
    lRet = ::RegSetValueEx(
    hKey, // handle of key to set value for
    TEXT("TypesSupported"), // address of value to set
    0, // reserved
    REG_DWORD, // flag for value type
    (CONST BYTE*)&dwTypes, // address of value data
    sizeof(DWORD) // size of value data
    );
    ::RegCloseKey(hKey);
    } // Add the service to the "Sources" value lRet = ::RegOpenKeyEx( 
    HKEY_LOCAL_MACHINE, // handle of open key 
    gszAppRegKey, // address of name of subkey to open 
    0, // reserved 
    KEY_ALL_ACCESS, // security access mask 
    &hKey // address of handle of open key 
    );
    if( lRet == ERROR_SUCCESS ) {
    DWORD dwSize; // retrieve the size of the needed value
    lRet = ::RegQueryValueEx(
    hKey, // handle of key to query 
    TEXT("Sources"),// address of name of value to query 
    0, // reserved 
    0, // address of buffer for value type 
    0, // address of data buffer 
    &dwSize // address of data buffer size 
    );  if( lRet == ERROR_SUCCESS ) {
    DWORD dwType;
    DWORD dwNewSize = dwSize+_tcslen(m_lpServiceName)+1;
    LPBYTE Buffer = LPBYTE(::GlobalAlloc(GPTR, dwNewSize)); lRet = ::RegQueryValueEx(
    hKey, // handle of key to query 
    TEXT("Sources"),// address of name of value to query 
    0, // reserved 
    &dwType, // address of buffer for value type 
    Buffer, // address of data buffer 
    &dwSize // address of data buffer size 
    );
    if( lRet == ERROR_SUCCESS ) {
    _ASSERTE(dwType == REG_MULTI_SZ); // check whether this service is already a known source
    register LPTSTR p = LPTSTR(Buffer);
    for(; *p; p += _tcslen(p)+1 ) {
    if( _tcscmp(p, m_lpServiceName) == 0 )
    break;
    }
    if( ! * p ) {
    // We're standing at the end of the stringarray
    // and the service does still not exist in the "Sources".
    // Now insert it at this point.
    // Note that we have already enough memory allocated
    // (see GlobalAlloc() above). We also don't need to append
    // an additional '\0'. This is done in GlobalAlloc() above
    // too.
    _tcscpy(p, m_lpServiceName); // OK - now store the modified value back into the
    // registry.
    lRet = ::RegSetValueEx(
    hKey, // handle of key to set value for
    TEXT("Sources"),// address of value to set
    0, // reserved
    dwType, // flag for value type
    Buffer, // address of value data
    dwNewSize // size of value data
    );
    }
    } ::GlobalFree(HGLOBAL(Buffer));
    } ::RegCloseKey(hKey);
    }
    }
    void CNTService :: DeregisterApplicationLog() {
    TCHAR szKey[256];
    _tcscpy(szKey, gszAppRegKey);
    _tcscat(szKey, m_lpServiceName);
    HKEY hKey = 0;
    LONG lRet = ERROR_SUCCESS; lRet = ::RegDeleteKey(HKEY_LOCAL_MACHINE, szKey); // now we have to delete the application from the "Sources" value too.
    lRet = ::RegOpenKeyEx( 
    HKEY_LOCAL_MACHINE, // handle of open key 
    gszAppRegKey, // address of name of subkey to open 
    0, // reserved 
    KEY_ALL_ACCESS, // security access mask 
    &hKey // address of handle of open key 
    );
    if( lRet == ERROR_SUCCESS ) {
    DWORD dwSize; // retrieve the size of the needed value
    lRet = ::RegQueryValueEx(
    hKey, // handle of key to query 
    TEXT("Sources"),// address of name of value to query 
    0, // reserved 
    0, // address of buffer for value type 
    0, // address of data buffer 
    &dwSize // address of data buffer size 
    );  if( lRet == ERROR_SUCCESS ) {
    DWORD dwType;
    LPBYTE Buffer = LPBYTE(::GlobalAlloc(GPTR, dwSize));
    LPBYTE NewBuffer = LPBYTE(::GlobalAlloc(GPTR, dwSize)); lRet = ::RegQueryValueEx(
    hKey, // handle of key to query 
    TEXT("Sources"),// address of name of value to query 
    0, // reserved 
    &dwType, // address of buffer for value type 
    Buffer, // address of data buffer 
    &dwSize // address of data buffer size 
    );
    if( lRet == ERROR_SUCCESS ) {
    _ASSERTE(dwType == REG_MULTI_SZ); // check whether this service is already a known source
    register LPTSTR p = LPTSTR(Buffer);
    register LPTSTR pNew = LPTSTR(NewBuffer);
    BOOL bNeedSave = FALSE; // assume the value is already correct
    for(; *p; p += _tcslen(p)+1) {
    // except ourself: copy the source string into the destination
    if( _tcscmp(p, m_lpServiceName) != 0 ) {
    _tcscpy(pNew, p);
    pNew += _tcslen(pNew)+1;
    } else {
    bNeedSave = TRUE; // *this* application found
    dwSize -= _tcslen(p)+1; // new size of value
    }
    }
    if( bNeedSave ) {
    // OK - now store the modified value back into the
    // registry.
    lRet = ::RegSetValueEx(
    hKey, // handle of key to set value for
    TEXT("Sources"),// address of value to set
    0, // reserved
    dwType, // flag for value type
    NewBuffer, // address of value data
    dwSize // size of value data
    );
    }
    } ::GlobalFree(HGLOBAL(Buffer));
    ::GlobalFree(HGLOBAL(NewBuffer));
    } ::RegCloseKey(hKey);
    }
    }//////////////////////////////////////////////////////////!! TCW MOD - function to create console for faceless apps if not already there
    void CNTService::SetupConsole() {
    if( !m_fConsoleReady ) {
    AllocConsole(); // you only get 1 console. // lovely hack to get the standard io (printf, getc, etc) to the new console. Pretty much does what the
    // C lib does for us, but when we want it, and inside of a Window'd app.
    // The ugly look of this is due to the error checking (bad return values. Remove the if xxx checks if you like it that way.
    DWORD astds[3]={STD_OUTPUT_HANDLE,STD_ERROR_HANDLE,STD_INPUT_HANDLE};
    FILE *atrgs[3]={stdout,stderr,stdin};
    for( register int i=0; i<3; i++ ) {
    long hand=(long)GetStdHandle(astds[i]);
    if( hand!=(long)INVALID_HANDLE_VALUE ) {
    int osf=_open_osfhandle(hand,_O_TEXT);
    if( osf!=-1 ) {
    FILE *fp=_fdopen(osf,(astds[i]==STD_INPUT_HANDLE) ? "r" : "w");
    if( fp!=NULL ) {
    *(atrgs[i])=*fp;
    setvbuf(fp,NULL,_IONBF,0);
    }
    }
    }
    }
    m_fConsoleReady=TRUE;
    }
    }
      

  4.   

    这是一个服务程序的例子,编译通过运行过的。#include "windows.h"
    #include "winsvc.h"void WINAPI ServiceMain(DWORD dwArgc,LPTSTR * lpszArgv);
    void WINAPI Handler(DWORD Opcode);SERVICE_STATUS_HANDLE ssh;
    SERVICE_STATUS ss;int main(int argc,char * argv[])
    {
    SERVICE_TABLE_ENTRY ste[2];
    ste[0].lpServiceName="ServiceDemo";
    ste[0].lpServiceProc=ServiceMain;
    ste[1].lpServiceName=NULL;
    ste[1].lpServiceProc=NULL;
    StartServiceCtrlDispatcher(ste);
    return 0;
    }void WINAPI ServiceMain(DWORD dwArgc,LPTSTR * lpszArgv)
    {
    ssh=RegisterServiceCtrlHandler("ServiceDemo",Handler); ss.dwServiceType=SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS;
    ss.dwCurrentState=SERVICE_START_PENDING;
    ss.dwControlsAccepted=SERVICE_ACCEPT_STOP;
    ss.dwWin32ExitCode=NO_ERROR;
    ss.dwCheckPoint=0;
    ss.dwWaitHint=0; SetServiceStatus(ssh,&ss);
    //put your coed here
    ss.dwServiceType=SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS;
    ss.dwCurrentState=SERVICE_RUNNING;
    ss.dwControlsAccepted=SERVICE_ACCEPT_STOP;
    ss.dwWin32ExitCode=NO_ERROR;
    ss.dwCheckPoint=0;
    ss.dwWaitHint=0; SetServiceStatus(ssh,&ss);
    //put your coed here
    }void WINAPI Handler(DWORD Opcode)
    {
    switch(Opcode)
    {
    case SERVICE_CONTROL_STOP:
    //put your code here
    ss.dwCurrentState=SERVICE_STOPPED;
    ss.dwWin32ExitCode=0;
    ss.dwCheckPoint=0;
    ss.dwWaitHint=0; SetServiceStatus(ssh,&ss);
    break;
    case SERVICE_CONTROL_INTERROGATE:
    SetServiceStatus(ssh,&ss);
    break;
    }
    }
      

  5.   

    服务程序编好后还需要有程序来将它安装(即OnCreate()函数的处理),启动等操作,下面分别是,服务安装一次后就可以了,就已经添加到注册表中,在控制面板的服务中可以看到的,不需要每次都安装的:OnStart() 
    {
    scm=::OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);
    if(scm!=NULL)
    {
    scv=::OpenService(scm,"ServiceDemo",SERVICE_ALL_ACCESS);
    if (scv!=NULL)
    {
    ::StartService(scv,0,NULL);
    ::CloseServiceHandle(scv);
    }
    ::CloseServiceHandle(scm);
    }
    }OnCreate() 
    {
    scm=::OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);
    if (scm!=NULL)
    {
    scv=::CreateService(scm,
    "ServiceDemo",
    "ServiceDemo",
    SERVICE_ALL_ACCESS,
    SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS,
    SERVICE_AUTO_START,
    SERVICE_ERROR_IGNORE,
    "E:\\VC_DEMO\\ServDemo\\Debug\\ServDemo.exe",
    NULL,NULL,NULL,NULL,NULL);
    if (scv!=NULL)
    {
    ::CloseServiceHandle(scv);
    }
    else
    {
    ::CloseServiceHandle(scm);
    }
    }
    }
    OnDelete() 
    {
    scm=::OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);
    if (scm!=NULL)
    {
    scv=::OpenService(scm,"ServiceDemo",SERVICE_ALL_ACCESS);
    if (scv!=NULL)
    {
    ::QueryServiceStatus(scv,&ss);
    if (ss.dwCurrentState==SERVICE_RUNNING)
    {
    ::ControlService(scv,SERVICE_CONTROL_STOP,&ss);
    }
    ::DeleteService(scv);
    ::CloseServiceHandle(scv);
    }
    ::CloseServiceHandle(scm);
    }
    }OnStop() 
    {
    scm=::OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);
    if(scm!=NULL)
    {
    scv=::OpenService(scm,"ServiceDemo",SERVICE_STOP|SERVICE_QUERY_STATUS);
    if (scv!=NULL)
    {
    ::QueryServiceStatus(scv,&ss);
    if (ss.dwCurrentState==SERVICE_RUNNING)
    {
    ::ControlService(scv,SERVICE_CONTROL_STOP,&ss);
    }
    ::CloseServiceHandle(scv);
    }
    ::CloseServiceHandle(scm);
    }
    }