hi,

There is a source code about windows services on Iczelion's web page, named "beepserv".
I tried to run that code but it didnt work.
Each time I run "beepserv.exe", StartServiceCtrlDispatcher function returns zero, that is, it gives error.
How will I get it run properly?

Here is the code:

beepserv.asm


.386
.model flat,stdcall
include    windows.inc
include    kernel32.inc
include    user32.inc
include    advapi32.inc
include    beepserv.inc

;Why doesn't C/C++ have
;this feature
includelib  advapi32.lib
includelib  kernel32.lib
includelib  user32.lib

;just a space saving macro
LOAD MACRO  dest, src
  mov eax, src
  mov dest, eax
ENDM

.data
;The name of the service
SERVICE_NAME      BYTE          "BeepService",0
;The beep interval in ms.
Delay          dd          2000

;Flags holding current state of service
fPaused      BOOL        FALSE
fRunning    BOOL        FALSE

szBuffer          db          MAX_PATH dup(0)
ERROR_MESSAGE      db          "In StartServiceCtrlDispatcher",0

;Thread for the actual work
hThread      HANDLE      NULL

;Event used to hold ServiceMain from completing
evTerminate    HANDLE      NULL


.data?
;Handle used to communicate status info with
;the SCM. Created by RegisterServiceCtrlHandler
hStatus DWORD      ?
sStatus  SERVICE_STATUS  <>
;
sTable  SERVICE_TABLE_ENTRY  < 0, 0 >
;This next one is unused but here for the zeros
;Because the table must be null terminated.
;Each exe can contain multiple services
;just add them here as tables.
sTable2  SERVICE_TABLE_ENTRY  < 0, 0 >


.code
start:
  ;Register with the SCM
  mov  sTable.lpServiceProc, offset ServiceMain
  LOAD sTable.lpServiceName, offset SERVICE_NAME
  INVOKE StartServiceCtrlDispatcher, ADDR sTable

  .IF eax == 0
      INVOKE ErrorHandler, ADDR ERROR_MESSAGE
  .ENDIF

  INVOKE ExitProcess, eax

;This is the function that does all of the
;work.  In our case we beep (ohhh exciting).
;But much more productive work could be done
;here. ;)
Thread proc param:DWORD
  INFINITE_LP:
      INVOKE Beep, 200, 200
      INVOKE Sleep, Delay
  jmp INFINITE_LP
  xor eax, eax
  ret
Thread endp


;Initializes the service by starting its thread
Init proc
  LOCAL id:DWORD
  INVOKE CreateThread, 0, 0, Thread, 0, 0, ADDR id
  mov  hThread, eax
  .IF eax != 0
      mov  fRunning, 1
      mov  eax, 1
  .ENDIF
  ret
Init endp

;Resumes a paused service
Resume proc
  mov  fPaused, FALSE
  INVOKE ResumeThread, hThread
  ret
Resume endp

;Pauses the service
Pause proc
  mov fPaused, TRUE
  INVOKE SuspendThread, hThread
  ret
Pause endp


;Stops the service by allowing ServiceMain to
;complete
Stop proc
  mov fRunning, FALSE
  ;This will release ServiceMain
  INVOKE SetEvent, evTerminate
  ret
Stop endp



ErrorHandler proc err:DWORD
  INVOKE MessageBoxA, NULL, ADDR szBuffer, ADDR SERVICE_NAME, MB_OK or MB_ICONERROR
  INVOKE ExitProcess, err
  ret
ErrorHandler endp

;This function consolidates the activities of
;updating the service status with
;SetsStatus.  More overhead for the programmer
;because Micro$oft doesn't make quality code.
SendStatus proc dwCurrentState:DWORD, dwWin32ExitCode:DWORD,  \
                    dwServiceSpecificExitCode:DWORD, dwCheckPoint:DWORD, \
                    dwWaitHint:DWORD
  LOCAL success:BOOL

  mov sStatus.dwServiceType, SERVICE_WIN32_OWN_PROCESS
  LOAD sStatus.dwCurrentState, dwCurrentState
  .IF dwCurrentState == SERVICE_START_PENDING
      mov sStatus.dwControlsAccepted, 0
  .ELSE
      mov sStatus.dwControlsAccepted, \
                        SERVICE_ACCEPT_STOP or \
                        SERVICE_ACCEPT_PAUSE_CONTINUE or \
                        SERVICE_ACCEPT_SHUTDOWN
  .ENDIF
  .IF dwServiceSpecificExitCode == 0
      LOAD sStatus.dwWin32ExitCode, dwWin32ExitCode
  .ELSE
      mov sStatus.dwWin32ExitCode, \
        ERROR_SERVICE_SPECIFIC_ERROR
  .ENDIF

  LOAD sStatus.dwServiceSpecificExitCode, dwServiceSpecificExitCode
  LOAD sStatus.dwCheckPoint, dwCheckPoint
  LOAD sStatus.dwWaitHint, dwWaitHint

  ;Pass the status record to the SCM
  INVOKE SetServiceStatus, hStatus, ADDR sStatus
  mov eax, 1
  ret
SendStatus endp


;Dispatches events received from the service
;control manager
CtrlHandler proc controlCode:DWORD
  LOCAL currentState:DWORD
  LOCAL success:BOOL

  mov  currentState,      0
  mov  eax, controlCode

  .IF eax == SERVICE_CONTROL_STOP
      LOAD currentState, SERVICE_STOP_PENDING
      ;Tell the SCM what's happening
      INVOKE SendStatus, SERVICE_STOP_PENDING, NO_ERROR, 0, 1, 5000
      ;Not much to do if not successful
      ;Stop the service
      call Stop
      ret

  ;Pause the service
  .ELSEIF eax == SERVICE_CONTROL_PAUSE
      .IF fRunning != 0 && fPaused == 0
        ;Tell the SCM what's happening
        INVOKE SendStatus, SERVICE_PAUSE_PENDING, NO_ERROR, 0, 1, 1000
        call Pause
        mov  currentState, SERVICE_PAUSED;
        jmp SCHandler
      .ENDIF

  ;Resume from a pause
  .ELSEIF eax == SERVICE_CONTROL_CONTINUE
      .IF fRunning != 0 && fPaused == 0
          ;Tell the SCM what's happening
          INVOKE SendStatus, SERVICE_CONTINUE_PENDING, NO_ERROR, 0, 1, 1000
          call Resume
          mov currentState, SERVICE_RUNNING
          jmp SCHandler
      .ENDIF

  ;Update current status
  .ELSEIF eax == SERVICE_CONTROL_INTERROGATE
    ;it will fall to bottom and send status
    ;Do nothing in a shutdown. Could do cleanup
    ;here but it must be very quick.

  .ELSEIF eax == SERVICE_CONTROL_SHUTDOWN
    ;Do nothing on shutdown
      ret

  .ENDIF

SCHandler:
    INVOKE SendStatus, currentState, NO_ERROR, 0, 0, 0
    ret
CtrlHandler endp

;Handle an error from ServiceMain by cleaning up
;and telling SCM that the service didn't start.
terminate proc error:DWORD

  ;if evTerminate has been created, close it.
  .IF evTerminate != 0
      push evTerminate
      call CloseHandle
  .ENDIF

    ;Send a message to the scm to tell about
    ;stopage
  .IF hStatus != 0
      INVOKE SendStatus, SERVICE_STOPPED, error, 0, 0, 0
  .ENDIF

        ;If the thread has started kill it off
    .IF hThread != 0
      push hThread
      call CloseHandle
  .ENDIF

  ;Do not need to close hStatus
terminate endp


;ServiceMain is called when the SCM wants to
;start the service. When it returns, the service
;has stopped. It therefore waits on an event
;just before the end of the function, and
;that event gets set when it is time to stop.
;It also returns on any error because the
;service cannot start if there is an eror.
ServiceMain proc argc:DWORD, argv:DWORD
  LOCAL success:BOOL
  LOCAL temp:DWORD

  ;immediately call Registration function
  INVOKE RegisterServiceCtrlHandler, ADDR SERVICE_NAME,  CtrlHandler
  mov  hStatus, eax

  .IF eax == 0
      call GetLastError
      push eax
      call terminate
      ret
  .ENDIF

  ;Notify SCM of progress
  INVOKE SendStatus, SERVICE_START_PENDING, NO_ERROR, 0, 1, 5000

  ;create the termination event
  INVOKE CreateEvent, 0, TRUE, FALSE, 0
  mov evTerminate, eax

  .IF eax == 0
      call GetLastError
      push eax
      call terminate
      ret
  .ENDIF

  ;Notify SCM of progress
  INVOKE SendStatus, SERVICE_START_PENDING, NO_ERROR, 0, 2, 1000

  ;Notify SCM of progress
  INVOKE SendStatus, SERVICE_START_PENDING, NO_ERROR, 0, 3, 5000

  ;Start the service itself
  call Init
  mov success, eax
  .IF eax == 0
      call GetLastError
      push eax
      call terminate
      ret
  .ENDIF

  ;Notify SCM of progress
  INVOKE SendStatus, SERVICE_RUNNING, NO_ERROR, 0, 0, 0

  ;Wait for stop signal, and then terminate
  INVOKE WaitForSingleObject, evTerminate, INFINITE
  push 0
  call terminate
  ret

ServiceMain endp

end start




Posted on 2011-01-14 16:28:22 by hakand