Announcement

Collapse
No announcement yet.

Anybody want an NT Service Template?

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Anybody want an NT Service Template?

    I used this serveral times.
    Works greate
    Code:
    '------------------------------------------------------------------------------
    '
    '   NT SERVICE Template for 32-bit PB/DLL
    '   Copyright (c) 2001 by ClarkSoft.Com Inc
    '
    '  Let me know if you find any errors or have any suggestions
    '  [email protected]
    '
    '  DISCLAIMER:
    '  This code may be distributed on the condition that no fee is charged for
    '  such distribution with the exception of reasonable shipping and media
    '  charged. In addition, the code in this program may be incorporated
    '  into your own programs and the resulting programs may be distributed
    '  without payment of royalties.  The author of this code is not
    '  responsible for the success or failure of any resultant usage
    '  of the code giving within this file.  You are free to modify any code
    '  found within this file, but the author is not responsible for any changes
    '  to the base code file.
    '
    '-------------------------------------------------------------------------------
    
    $COMPILE EXE "EXENAME.EXE"
    
    $RESOURCE "RESOURCE.PBR"            ' Embed the resources into the exe
    
    #DIM ALL                            ' Force ALL vars to have to be declared
    
    GLOBAL hWindow AS LONG              ' You should set this in your WINMAINX to the handle
                                        ' of your main window.
    
    $INCLUDE "WIN32API.INC"
    ' My includes actually have the WinAPI includes in them.
    '$INCLUDE "MYINC.INC"       ' This was the main Bas file for my executable
    
    '  ---------------------
    '  CONSTANT DECLARATIONS
    '  ---------------------
    %EVENTLOG_AUDIT_FAILURE                 = 16
    %EVENTLOG_AUDIT_SUCCESS                 = 8
    %EVENTLOG_ERROR_TYPE                    = 1
    %EVENTLOG_INFORMATION_TYPE              = 4
    %EVENTLOG_SUCCESS                       = 0
    %EVENTLOG_WARNING_TYPE                  = 2
    
    '  This constant is used to set logging status.
    %EVENTLOG_LEVEL = %EVENTLOG_AUDIT_FAILURE + %EVENTLOG_AUDIT_SUCCESS + %EVENTLOG_ERROR_TYPE + _
                      %EVENTLOG_INFORMATION_TYPE + %EVENTLOG_SUCCESS + %EVENTLOG_WARNING_TYPE
    %SERVICE_WIN32_OWN_PROCESS              = &H10&
    %SERVICE_WIN32_SHARE_PROCESS            = &H20&
    %SERVICE_WIN32                          = %SERVICE_WIN32_OWN_PROCESS + %SERVICE_WIN32_SHARE_PROCESS
    %SERVICE_DEMAND_START                   = &H3&
    %SERVICE_ERROR_NORMAL                   = &H1&
    %WAIT_OBJECT_0                          = 0&
    
    '  -------------------------
    '  FUNCTION/SUB DECLARATIONS
    '  -------------------------
    DECLARE FUNCTION Install() AS LONG
    DECLARE FUNCTION InitService() AS LONG
    DECLARE FUNCTION Uninstall() AS LONG
    DECLARE FUNCTION WinErrorMessage(BYVAL ErrNumber AS LONG) AS STRING
    DECLARE FUNCTION SendStatus(BYVAL CurrentStatus AS DWORD, BYVAL ExitCode AS DWORD, ServiceSpecificExitCode AS LONG, BYVAL Checkpoint AS DWORD, BYVAL WaitHint AS DWORD) AS LONG
    DECLARE FUNCTION ServiceThread(ID AS LONG) AS LONG
    DECLARE SUB Handler(BYVAL ControlValue AS DWORD)
    DECLARE SUB LogNTEvent(Message AS STRING, LogCode AS LONG)
    DECLARE SUB ServiceMain(BYVAL dwArgs AS DWORD, BYVAL lpszArgv AS DWORD)
    DECLARE SUB Terminate(ErrCode AS DWORD)
    DECLARE SUB StopService()
    DECLARE SUB PauseService()
    DECLARE SUB ResumeService()
    
    '  ----------------
    '  GLOBAL VARIABLES
    '  ----------------
    GLOBAL gascServiceName                      AS ASCIIZ * 8
    GLOBAL gascServiceDisplayName               AS ASCIIZ * 256
    GLOBAL gdwdhStatus                          AS DWORD
    GLOBAL gdwdServiceState                     AS DWORD
    GLOBAL gdwdServiceStatus                    AS DWORD
    GLOBAL glnghInstance                        AS LONG
    GLOBAL glnghPrvInst                         AS LONG
    GLOBAL glnghTerminateEvent                  AS LONG
    GLOBAL glnghServiceThread                   AS LONG
    GLOBAL glngThreadResult                     AS LONG
    GLOBAL glngRunningService                   AS LONG
    GLOBAL glngPauseService                     AS LONG
    GLOBAL glngCmdShow                          AS LONG
    GLOBAL ptrCmdLine                           AS ASCIIZ PTR
    
    '  FUNCTION:  WinMain
    FUNCTION WINMAIN(BYVAL hCurInst AS LONG, BYVAL hPrvInst AS LONG, CmdLine AS ASCIIZ PTR, BYVAL CmdShow AS LONG) EXPORT AS LONG
        DIM Result              AS LONG
        DIM CmdParm             AS STRING
        DIM udtSTE              AS SERVICE_TABLE_ENTRY
    
        '  Set the service name and display name here.
        gascServiceName             = "SERVICE NAME HERE"
        gascServiceDisplayName      = "DISPLAY NAME HERE"
        glngHInstance               = hCurInst
        glnghPrvInst                = hPrvInst
        glngCmdShow                 = CmdShow
        ptrCmdLine                  = CmdLine
    
        ' Get the Command Line parms
        CmdParm = UCASE$(EXTRACT$(@CmdLine, ANY " ,." + CHR$(9)))
        ' Get rid of any seperators
        CmdParm = REMOVE$(CmdParm, ANY "-\/")
    
        ' If the exe was executed with the Install parm then we just install and quit
        IF CmdParm = "INSTALL" THEN
            '  Install the service.
            Result = Install()
            IF ISFALSE Result THEN
                MSGBOX "An error occured while trying to install this service
            END IF
        ' If the exe was executed with the UnInstall parm then we just uninstall and quit
        ELSEIF CmdParm = "UNINSTALL" THEN
            '  Uninstall the service.
            Result = Uninstall()
            IF ISFALSE Result THEN
                MSGBOX "An error occured while trying to uninstall this service
            END IF
            
        ' No parms were given that we want so start up as service
        ' This should only happen when the service control manager starts us
        ELSE
    
            udtSTE.lpServiceName = VARPTR(gascServiceName)
            udtSTE.lpServiceProc = CODEPTR(ServiceMain)
    
            Result = StartServiceCtrlDispatcher(udtSTE)
    
            IF Result = 0 THEN
                ExitProcess GetLastError()
            END IF
    
        END IF
    
        FUNCTION = 0
    
    END FUNCTION
    
    ' Install the service into windows
    FUNCTION Install() AS LONG
        DIM dwdRet                          AS DWORD
        DIM lngDQEnd                        AS LONG
        DIM lngDQStart                      AS LONG
        DIM lngHSCManager                   AS LONG
        DIM lngHService                     AS LONG
        DIM ascEXE                          AS ASCIIZ * %MAX_PATH
    
        ON ERROR GOTO Error_Install
    
        '  Assume a failure for now.
        FUNCTION = %FALSE
    
        lngHSCManager = OpenSCManager(BYVAL %NULL, BYVAL %NULL, %SC_MANAGER_CREATE_SERVICE)
    
        IF lngHSCManager <> %NULL THEN
            '  OK, we have a handle to the SCM.
            '  Get the full EXE file path.
            dwdRet = GetModuleFileName(glngHInstance, ascEXE, %MAX_PATH)
            IF dwdRet <> 0 THEN
                '  Install the service.
                lngHService = CreateService(lngHSCManager, gascServiceName, gascServiceDisplayName, _
                                            %SERVICE_ALL_ACCESS, %SERVICE_WIN32_OWN_PROCESS, _
                                            %SERVICE_AUTO_START, %SERVICE_ERROR_NORMAL, _
                                            ascEXE, BYVAL %NULL, BYVAL %NULL, _
                                            BYVAL %NULL, BYVAL %NULL, BYVAL %NULL)
                '  Close any service handles.
                IF lngHService <> %NULL THEN
                    '  Success!
                    FUNCTION = %TRUE
                    CloseServiceHandle lngHService
                END IF
                CloseServiceHandle lngHSCManager
            END IF
        END IF
    
        FUNCTION = %TRUE
    
        EXIT FUNCTION
    
    Error_Install:
    
        FUNCTION = -1& * ERR
    
        ON ERROR RESUME NEXT
    
        '  Close any service handles.
        IF lngHService <> %NULL THEN
            CALL CloseServiceHandle(lngHService)
        END IF
    
        IF lngHSCManager <> %NULL THEN
            CALL CloseServiceHandle(lngHSCManager)
        END IF
    
    END FUNCTION
    
    ' Unistall the service
    FUNCTION Uninstall() AS LONG
        DIM lngHSCManager               AS LONG
        DIM lngHService                 AS LONG
    
        ON ERROR GOTO Error_Uninstall
    
        '  Assume a failure for now.
        FUNCTION = %FALSE
    
        lngHSCManager = OpenSCManager(BYVAL %NULL, BYVAL %NULL, %SC_MANAGER_CREATE_SERVICE)
    
        IF lngHSCManager <> %NULL THEN
            '  OK, we have a handle to the SCM.
            '  Now open our service.
            lngHService = OpenService(lngHSCManager, gascServiceName, %SERVICE_ALL_ACCESS)
            IF lngHService <> %NULL THEN
                '  Delete the service.
                IF DeleteService(lngHService) <> %NULL THEN
                    '  Success!
                    FUNCTION = %TRUE
                END IF
                CloseServiceHandle lngHService
            END IF
            CloseServiceHandle lngHSCManager
        END IF
    
        EXIT FUNCTION
    
    Error_Uninstall:
    
        FUNCTION = -1& * ERR
    
        ON ERROR RESUME NEXT
    
        '  Close any service handles.
        IF lngHService <> %NULL THEN
            CloseServiceHandle lngHService
        END IF
    
        IF lngHSCManager <> %NULL THEN
            CloseServiceHandle lngHSCManager
        END IF
    
    END FUNCTION
    
    '  Start up the service
    FUNCTION InitService() AS LONG
        DIM lngRet                      AS LONG
        DIM ID                          AS LONG
        DIM udtSTE                      AS SERVICE_TABLE_ENTRY
        DIM lpThreadAttributes          AS SECURITY_ATTRIBUTES
    
        ' Start the main thread for this service
        glnghServiceThread = CreateThread(lpThreadAttributes, 0, CODEPTR(ServiceThread), 0, 0, ID)
    
        ' Did the thread start OK
        IF glnghServiceThread = 0 THEN
            FUNCTION = %FALSE
            EXIT FUNCTION
        ELSE
            ' Set the global to running
            glngRunningService = %TRUE
    
            FUNCTION = %TRUE
            EXIT FUNCTION
        END IF
    
    END FUNCTION
    
    SUB PauseService()
    
        ' Set the global indicating that we are not paused
        glngPauseService = %TRUE
    
        ' Let er rip
        SuspendThread glnghServiceThread
    
    END SUB
    
    SUB ResumeService()
    
        ' Set the global indicating that we are not paused
        glngPauseService = %FALSE
    
        ' Let er rip
        ResumeThread glnghServiceThread
    
    END SUB
    
    SUB StopService()
    
        ' Set the global flag indicating that the service is not running
        glngRunningService = %FALSE
    
        ' Set the event so the service will stop
        SetEvent glnghTerminateEvent
    
    END SUB
    
    FUNCTION SendStatus(BYVAL CurrentStatus AS DWORD, BYVAL ExitCode AS DWORD, ServiceSpecificExitCode AS LONG, BYVAL Checkpoint AS DWORD, BYVAL WaitHint AS DWORD) AS LONG
        DIM udtSS               AS SERVICE_STATUS
    
        '  Reset the global service status value.
        gdwdServiceStatus = CurrentStatus
    
        '  Setup the UDT.
        udtSS.dwServiceType = %SERVICE_WIN32_OWN_PROCESS
        udtSS.dwCurrentState = CurrentStatus
    
        ' If we are the process of starting, then don't accept control events
        IF CurrentStatus = %SERVICE_START_PENDING THEN
            udtSS.dwControlsAccepted = 0
        ELSE
            ' Take what was given
            udtSS.dwControlsAccepted = %SERVICE_ACCEPT_STOP + %SERVICE_ACCEPT_PAUSE_CONTINUE + %SERVICE_ACCEPT_SHUTDOWN
        END IF
        
        ' If a specific ServiceSpecificExitCode is defined, setup the Win32 exit code properly
        IF ServiceSpecificExitCode = 0 THEN
            udtSS.dwWin32ExitCode = ExitCode
        ELSE
            udtSS.dwWin32ExitCode = %ERROR_SERVICE_SPECIFIC_ERROR
        END IF
        
        ' Specific Exit Code
        udtSS.dwServiceSpecificExitCode = ServiceSpecificExitCode
    
        udtSS.dwCheckPoint = Checkpoint
        udtSS.dwWaitHint = WaitHint
    
        IF SetServiceStatus(gdwdHStatus, udtSS) = 0 THEN
            ' Something went wrong so stop the service
            StopService
            FUNCTION = %FALSE
        ELSE
            FUNCTION = %TRUE
        END IF
    
    END FUNCTION
    
    SUB ServiceMain(BYVAL dwArgs AS DWORD, BYVAL lpszArgv AS DWORD)
        LOCAL Result                AS LONG
        LOCAL lpEventAttributes     AS SECURITY_ATTRIBUTES
    
        ' Register with the SCM
        gdwdHStatus = RegisterServiceCtrlHandler(gascServiceName, CODEPTR(Handler))
        ' Did it work
        IF gdwdHStatus = 0 THEN
            ' No, so terminate
            Terminate GetLastError()
            EXIT SUB
        END IF
    
        '  Service has been registered and startup is pending
        IF ISFALSE SendStatus(%SERVICE_START_PENDING, %NO_ERROR, 0, 1, 5000) THEN
            Terminate GetLastError()
            EXIT SUB
        END IF
    
        ' Create the termination event
        glnghTerminateEvent = CreateEvent(lpEventAttributes, %TRUE, %FALSE, "")
        IF glnghTerminateEvent = 0 THEN
            Terminate GetLastError()
            EXIT SUB
        END IF
    
        '  Service startup is still pending
        IF ISFALSE SendStatus(%SERVICE_START_PENDING, %NO_ERROR, 0, 2, 1000) THEN
            Terminate GetLastError()
            EXIT SUB
        END IF
    
        ' Start the service
        Result = InitService()
        IF ISFALSE Result THEN
            ' Oops
            Terminate GetLastError()
            EXIT SUB
        END IF
    
        ' Service is now running
        IF ISFALSE SendStatus(%SERVICE_RUNNING, %NO_ERROR, 0, 0, 0) THEN
            Terminate GetLastError()
            EXIT SUB
        END IF
    
        ' Wait for the signal to end
        WaitForSingleObject glnghTerminateEvent, %INFINITE
    
        Terminate 0
    
        EXIT SUB
    
    END SUB
    
    SUB Terminate(ErrCode AS DWORD)
    
        ' If the Terminate Event has already been created then destroy it
        IF glnghTerminateEvent <> 0 THEN
            CloseHandle glnghTerminateEvent
        END IF
    
        IF gdwdHStatus <> 0 THEN
            ' Send a message to the SCM and tell them that we are stopping
            SendStatus %SERVICE_STOPPED, ErrCode, 0&, 0&, 0&
        END IF
    
        ' If the thread has started, then kill it
        IF glnghServiceThread <> 0 THEN
            ' Not normally here in a service
            ' However, this program was written as an executable first and converted into a service
            ' There is a global hWindow that is set in the WINMAINX when we start
            ' We need to destroy the window so the WINMAINX will come back to us
            ' Is it a valid window
            IF IsWindow(hWindow) THEN
                ' Yes so destroy it
                DestroyWindow hWindow
                ' This will cause the Message Loop in your WINMAINX to exit and return
            END IF
    
            ' Close the thread
            CloseHandle glnghServiceThread
        END IF
    
    END SUB
    
    SUB Handler(BYVAL ControlValue AS DWORD)
    '  This procedure (by its' name) handles all service requests.
        LOCAL Result                AS LONG
    
        ON ERROR RESUME NEXT
    
        ' There is no Start option because the ServiceMain takes care of starting the service
        SELECT CASE ControlValue
            CASE %SERVICE_CONTROL_STOP
                ' Set the global Status
                gdwdServiceState = %SERVICE_STOP_PENDING
    
                ' Tell the SCM that we are stopping
                SendStatus %SERVICE_STOP_PENDING, %NO_ERROR, 0&, 1, 5000
    
                ' Stop the service
                StopService
            CASE %SERVICE_CONTROL_PAUSE
                ' Are we running and not paused
                IF (ISTRUE glngRunningService) AND (ISFALSE glngPauseService) THEN
                    ' Tell the SCM that we are pausing
                    SendStatus %SERVICE_PAUSE_PENDING, %NO_ERROR, 0, 1, 1000
    
                    ' Pause it
                    PauseService
    
                    ' Set the current state
                    gdwdServiceState = %SERVICE_PAUSED
                END IF
            CASE %SERVICE_CONTROL_CONTINUE
                ' Are we running and paused
                IF (ISTRUE glngRunningService) AND (ISTRUE glngPauseService) THEN
                    ' Tell the SCM that we are un pausing
                    SendStatus %SERVICE_CONTINUE_PENDING, %NO_ERROR, 0, 1, 1000
    
                    ' Resume the service
                    ResumeService
    
                    ' Set the current state
                    gdwdServiceState = %SERVICE_RUNNING
                END IF
            CASE %SERVICE_CONTROL_INTERROGATE
                ' Don't need to do anything
                ' We will send the current status below
            CASE %SERVICE_CONTROL_SHUTDOWN
                ' We don't do anything with a shutdown
                EXIT SUB
        END SELECT
    
        ' Tell the SCM the new status
        SendStatus gdwdServiceState, %NO_ERROR, 0, 0, 0
    
    END SUB
    
    FUNCTION ServiceThread(ID AS LONG) EXPORT AS LONG
        LOCAL Result                    AS LONG
        LOCAL Msg                       AS tagMsg
    
        ' Run until we are killed
        'Result = WINMAINX(BYVAL glngHInstance, BYVAL glnghPrvInst, BYVAL ptrCmdLine, BYVAL glngCmdShow)
        ' I had written a program and compiled it as an exe
        ' To make it a service, I just renamed the WINMAIN to WINMAINX and then
        ' call it like you see above
    
    END FUNCTION

    ------------------
    Ben Clark
    [email protected]
    If at first you don't succeed, destroy all evidence that you tried.

  • #2
    Ben,

    How did you come up with this code?

    http://www.execpc.com/~jrbock

    Specifically,
    http://www.execpc.com/~jrbock/pbntservice.html

    I don't care if you use my code as a basis for yours and improve on it. But I'd appreciate an acknowledgement.

    Jason

    ------------------

    Comment


    • #3
      First, I would appreciate it if you would accuse me of
      stealing in private.
      Second, as far as I know, I don't have ANY of your code
      in my pessesion.
      Third, I converted most of it from a copy I had written
      years ago in C++, which I believe I modeled after an
      example in a book called 'System Services' or something
      simalar.
      I do not, have not and never will take the credit for
      someones work.
      I would appreciate it if you would pay a little closer
      attention to the code before making accusations.
      If by some chance it appears similar to yours, I assure
      you it is by chance only.


      ------------------
      Ben Clark
      [email protected]
      If at first you don't succeed, destroy all evidence that you tried.

      Comment


      • #4
        Looks pretty similar to me

        ------------------
        -Greg
        -Greg
        [email protected]
        MCP,MCSA,MCSE,MCSD

        Comment


        • #5
          I hope you guys used the same book... Even a lot of the comments are identical.

          Very weird. Given your reactions I have to assume you happened to start out with the same sample code.
          Please compare books before you start arguing. It may be better to take the arguing to private email but since it's here now, we'd like to at least know the outcome.


          Peter.


          ------------------
          [email protected]
          [email protected]

          Comment


          • #6
            Now we're at the NT service topic:
            Does somebody know a reliable way to show an icon in the windows taskbar system tray?
            (this means showing an icon when a user logs on, removing it when he logs off,
            and showing it again at next logon....)


            ------------------
            Peter.
            mailto[email protected][email protected]</A>
            Regards,
            Peter

            "Simplicity is a prerequisite for reliability"

            Comment


            • #7
              Ben,

              Why are you so defensive? Where did I accuse you of stealing code?

              I've had my code on my web site for at least 2 years. I made my template because I was writing some NT services in PB, and I wanted a framework in place that would make it easier for me. I didn't copy my code from anywhere - I made it all up from scratch (except for the disclaimer, which I copied from another piece of code that I saw). I have never seen the book that you mentioned.

              The structure and flow of your code is very similar to what I have. Whether you "stole" it from me or not - I could care less. All I ask for is some kind of acknowledgement. I know of no one else who had such a framework in place before I wrote mine. Other people on this site have mentioned this code sample before and mentioned that they grabbed my code and made tweaks to it.

              Jason


              ------------------

              Comment


              • #8
                I won't comment on the similarity/differences of the service
                template above with other code samples. I just want to say
                that by its very nature (limited area) most service template
                code is likely to have *many* similarities with other code
                even if that code has been written from scratch.

                Cheers

                Florent



                ------------------

                Comment


                • #9
                  I am defensive because you have in so many words called me a
                  thief.
                  If you want to email me and discuss this, I would be more then
                  happy. I did go look at your code and you are correct, they
                  do appear to be very simalar. But you have to understand that
                  an NT Service HAS to follow a very rigid structure.
                  Anyone who would right an NT Service would end up with code
                  very similar to yours and mine.
                  As far as variable names and such, I use, as well as many other
                  people, have a standard method of naming object which help make
                  it easier to read.
                  Examples:
                  Most global vars start with g.
                  Global are usually then followed with lng = Long
                  int = Integer
                  etc...

                  Given the above information, it would be almost impossible not
                  to end up with code which appears to be very similar.
                  I do not disagree with your absurvation.
                  What I do disagree with is your method of pointing it out.
                  You, in so many words, accused me of taking credit for your
                  code. If you truly believed that, I would have at least expected
                  you to responde with your email address and a request for a
                  conversation.
                  It could have very well been that I had a copy of your code
                  and didn't event know it. I see it happen all the time.
                  People post entries on this and many other forums stating
                  ' Here is some code, I don't remember where I got it' etc....

                  I apologize if I over reacted, however, being accused of
                  theft tends to make me very angry.

                  I still would be very glad to discuss this further.
                  We can continue to use this forumn or you can email me
                  and I will gladly answer. I will even email you my phone
                  number.

                  If you truly believe that I copied your code, we can even ask
                  the forumn admin people to remove it from the forum.

                  Ben Clark
                  [email protected]


                  ------------------
                  Ben Clark
                  [email protected]
                  If at first you don't succeed, destroy all evidence that you tried.

                  Comment

                  Working...
                  X