Are there any known gotchas with using the Runshell command in a service running as the system account on windows 7 64 bit, using the 64 bit compiler?
Here is the code I am using:
DebugTrace(@on,"c:\si\trace.txt")
;Sample service script template
;Initialze variables for the SvcSetAccept function
SERVICE_ACCEPT_STOP = 1 ;The service can be stopped
SERVICE_ACCEPT_PAUSE_CONTINUE = 2 ;The service can be paused and continued
SERVICE_ACCEPT_SHUTDOWN = 4 ;The service is notified when system shutdown occurs
SERVICE_ACCEPT_LOGOFF = 32768;The service is notified when user logoff occurs
;Initialize variables for the SvcSetState function
SERVICE_STATE_STOPPED = 1 ;The service is not running
SERVICE_STATE_START_PENDING = 2 ;The service is initializing
SERVICE_STATE_STOP_PENDING = 3 ;The service is stopping
SERVICE_STATE_RUNNING = 4 ;The service is running
SERVICE_STATE_CONTINUE_PENDING = 5 ;The service continue is pending
SERVICE_STATE_PAUSE_PENDING = 6 ;The service pause is pending
SERVICE_STATE_PAUSED = 7 ;The service is paused
;Initialize variables for the SvcWaitForCmd function
SERVICE_CONTROL_NOT_SERVICE = -1 ;Script not running as a service
SERVICE_CONTROL_TIMEOUT = 0 ;Timeout occurred or no codes to process
SERVICE_CONTROL_STOP = 1 ;Requests the service to stop
SERVICE_CONTROL_PAUSE = 2 ;Requests the service to pause
SERVICE_CONTROL_CONTINUE = 3 ;Requests the paused service to resume
SERVICE_CONTROL_SHUTDOWN = 5 ;Requests the service to perform
;cleanup tasks, because the system is shutting down ;is shutting down
SERVICE_CONTROL_USER128 = 128 ;User command 128
SERVICE_CONTROL_USER129 = 129 ;User command 129
SERVICE_CONTROL_USER130 = 130 ;User command 130
SERVICE_CONTROL_USER131 = 131 ;User command 131
; ;More user commands as needed
SERVICE_CONTROL_USER255 = 255 ;User command 255
SERVICE_CONTROL_LOGOFF = 32768 ;logoff notification
;Create working directory if it does not exist
if !DirExist("c:\SI") then DirMake("c:\SI")
inicontrolfile="c:\SI\SI.ini"
;As directory had to be created then create the outline SI.ini file
IniWritePvt("Control","Request",0,inicontrolfile) ; 0=No Request
IniWritePvt("Control","RunEXE","",inicontrolfile) ; ""=No Action
IniWritePvt("Control","RunPath","",inicontrolfile) ; ""=No Action
IniWritePvt("Control","RunString","",inicontrolfile) ; ""=No Action
IniWritePvt("Control","ExitCode","",inicontrolfile) ; ""=No Exit Code
IniWritePvt("Control","ServiceError","",inicontrolfile) ; ""=No Service Error
IniWritePvt("Debug","iniexe","",inicontrolfile)
IniWritePvt("Debug","inicmd","",inicontrolfile)
IniWritePvt("Debug","inistring","",inicontrolfile)
IniWritePvt("Debug","inipath","",inicontrolfile)
;Setup debugging prompt strings....
debugcodes="0: Timeout|1: Stop|2: Pause|3: Continue|5: Shutdown|128: User Cmd 128|129:User Cmd 129|32768: Logoff"
;Tell system that we want all notifications
flag=svcSetAccept( SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_LOGOFF )
if flag== -1
DoingDebug=@TRUE
Pause("Debug Mode","Not currently running as a service")
else
DoingDebug=@FALSE
;Set up error handling
IntControl(12,2+8,0,0,0) ;Tell WinBatch to not honor terminate and
;not complain on Windows exit
IntControl(38,1,"C:\SI\errorlog.txt",0,0) ;Route fatal errors to a log file
endif
;Now for the main service loop
;in this service we respond to all control messages
;and check an ini file for work every 5 seconds
;BoxOpen("Initializing","main service loop")
SvcSetState(SERVICE_STATE_RUNNING)
while @TRUE
if DoingDebug==@FALSE
code=SvcWaitForCmd(5000) ; Timeout in 5 seconds
else
;For Debugging. Prompt tester to see what code should be presented here
code=AskItemList("Service Debug",debugcodes,"|",@UNSORTED,@SINGLE)
if code=="" then continue
code=ItemExtract(1,code,":")
endif
switch code
case SERVICE_CONTROL_TIMEOUT
;Timeout occurred
;BoxText("TimeoutOccured")
;Read INI values just in case a request is detected
inivalue=IniReadPvt("Control","Request",0,inicontrolfile)
inipath=IniReadPvt("Control","RunPath","",inicontrolfile)
inistring=IniReadPvt("Control","RunString","",inicontrolfile)
iniexe=IniReadPvt("Control","RunEXE","",inicontrolfile)
if inivalue!=0 ;Values have been read, an action has been requested, so reset back to starting values
IniWritePvt("Control","Request",0,inicontrolfile)
IniWritePvt("Control","RunPath","",inicontrolfile)
IniWritePvt("Control","RunString","",inicontrolfile)
IniWritePvt("Control","RunEXE","",inicontrolfile)
IniWritePvt("Control","ExitCode","",inicontrolfile)
IniWritePvt("Control","ServiceError","",inicontrolfile) ;May not be necessary when more error handling is added
gosub DoIniWork
endif
break
case SERVICE_CONTROL_STOP
;Stop command received
;BoxText("Stop command received")
SvcSetState(SERVICE_STATE_STOP_PENDING)
;do stop cleanup work here
timedelay(5)
SvcSetState(SERVICE_STATE_STOPPED)
exit ;Goodbye
break
case SERVICE_CONTROL_PAUSE
;Pause command received
;BoxText("Pause command received")
SvcSetState(SERVICE_STATE_PAUSE_PENDING)
;do pause cleanup work here
SvcSetState(SERVICE_STATE_PAUSED)
break
case SERVICE_CONTROL_CONTINUE
;Continue command received
;BoxText("Continue command received")
SvcSetState(SERVICE_STATE_CONTINUE_PENDING)
;do resume from pause state initilization here
SvcSetState(SERVICE_STATE_RUNNING)
break
case SERVICE_CONTROL_SHUTDOWN
;Shutdown notification received
;Approx. 20 seconds to process
;BoxText("Shutdown notification received")
SvcSetState(SERVICE_STATE_STOP_PENDING)
;do stop cleanup work here
SvcSetState(SERVICE_STATE_STOPPED)
exit ;Goodbye
break
case SERVICE_CONTROL_USER128
;User command 128 received
;BoxText("User command 128 received")
break
case SERVICE_CONTROL_USER129
;User command 129 received
;BoxText("User command 129 received")
break
case SERVICE_CONTROL_LOGOFF
;Logoff command received
;BoxText("Logoff command received")
break
case code
;Unrecognized command received
;BoxText("Unrecognized command received")
break
endswitch
endwhile
;Note. The preceeding loop never exits
exit ; Just a formality
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;The DoIniWork subroutine is designed to allow a WinBatch script or
;other process running under the user account to "flag" the service
;to perfrom useful work of a kind the user is not privileged to do.
;In this case, perhaps just setting a registry key.
:DoIniWork
inicmd=StrCat(inipath,"\",inistring)
;Check that target executable exists, so need to remove any command line switches
FileToCheck=StrTrim(ItemExtract("1", inicmd, "/"))
IniWritePvt("Debug","iniexe",iniexe,inicontrolfile)
IniWritePvt("Debug","inicmd",inicmd,inicontrolfile)
IniWritePvt("Debug","inistring",inistring,inicontrolfile)
IniWritePvt("Debug","inipath",inipath,inicontrolfile)
IniWritePvt("Debug","FileToCheck",FileToCheck,inicontrolfile)
fex=FileExist(FileToCheck)
If fex
;target file found, so do something with it
IniExitCode=RunShell(iniexe,inicmd,inipath,@HIDDEN,@GETEXITCODE)
IniWritePvt("Control","ExitCode",IniExitCode,inicontrolfile)
Else
;target file not found
ErrorString=StrCat("Specified file not found: ",FileToCheck)
IniWritePvt("Control","ServiceError",ErrorString,inicontrolfile)
EndIf
return
#########################
and here is the debugtrace output:
code=SvcWaitForCmd(5000)
(30170) VALUE INT => 0
else
(30170) END OPERATOR
switch code
(30170) CASE TRUE==>0
inivalue=IniReadPvt("Control","Request",0,inicontrolfile)
(30170) VALUE STRING => "0"
inipath=IniReadPvt("Control","RunPath","",inicontrolfile)
(30170) VALUE STRING => ""
inistring=IniReadPvt("Control","RunString","",inicontrolfile)
(30170) VALUE STRING => ""
iniexe=IniReadPvt("Control","RunEXE","",inicontrolfile)
(30170) VALUE STRING => ""
if inivalue!=0
(30170) END OPERATOR
break
(30170) END OPERATOR
endwhile
(30170) END OPERATOR
while @TRUE
(30170) WHILE==>TRUE
if DoingDebug==@FALSE
(30170) IF DO==>TRUE
code=SvcWaitForCmd(5000)
(35194) VALUE INT => 0
else
(35194) END OPERATOR
switch code
(35194) CASE TRUE==>0
inivalue=IniReadPvt("Control","Request",0,inicontrolfile)
(35194) VALUE STRING => "0"
inipath=IniReadPvt("Control","RunPath","",inicontrolfile)
(35194) VALUE STRING => ""
inistring=IniReadPvt("Control","RunString","",inicontrolfile)
(35194) VALUE STRING => ""
iniexe=IniReadPvt("Control","RunEXE","",inicontrolfile)
(35194) VALUE STRING => ""
if inivalue!=0
(35194) END OPERATOR
break
(35194) END OPERATOR
endwhile
(35194) END OPERATOR
while @TRUE
(35194) WHILE==>TRUE
if DoingDebug==@FALSE
(35194) IF DO==>TRUE
code=SvcWaitForCmd(5000)
(40217) VALUE INT => 0
else
(40217) END OPERATOR
switch code
(40217) CASE TRUE==>0
inivalue=IniReadPvt("Control","Request",0,inicontrolfile)
(40217) VALUE STRING => "128"
inipath=IniReadPvt("Control","RunPath","",inicontrolfile)
(40217) VALUE STRING => "E:\Apps"
inistring=IniReadPvt("Control","RunString","",inicontrolfile)
(40217) VALUE STRING => "dragon.cmd"
iniexe=IniReadPvt("Control","RunEXE","",inicontrolfile)
(40217) VALUE STRING => "C:\Windows\System32\Cmd.exe"
if inivalue!=0
(40217) IF DO==>TRUE
IniWritePvt("Control","Request",0,inicontrolfile)
(40217) VALUE INT => 1
IniWritePvt("Control","RunPath","",inicontrolfile)
(40217) VALUE INT => 1
IniWritePvt("Control","RunString","",inicontrolfile)
(40217) VALUE INT => 1
IniWritePvt("Control","RunEXE","",inicontrolfile)
(40217) VALUE INT => 1
IniWritePvt("Control","ExitCode","",inicontrolfile)
(40217) VALUE INT => 1
IniWritePvt("Control","ServiceError","",inicontrolfile)
(40217) VALUE INT => 1
gosub DoIniWork
(40217) GOSUB
inicmd=StrCat(inipath,"\",inistring)
(40217) VALUE STRING => "E:\Apps\dragon.cmd"
FileToCheck=StrTrim(ItemExtract("1", inicmd, "/"))
(40217) VALUE STRING => "E:\Apps\dragon.cmd"
IniWritePvt("Debug","iniexe",iniexe,inicontrolfile)
(40217) VALUE INT => 1
IniWritePvt("Debug","inicmd",inicmd,inicontrolfile)
(40233) VALUE INT => 1
IniWritePvt("Debug","inistring",inistring,inicontrolfile)
(40233) VALUE INT => 1
IniWritePvt("Debug","inipath",inipath,inicontrolfile)
(40233) VALUE INT => 1
IniWritePvt("Debug","FileToCheck",FileToCheck,inicontrolfile)
(40233) VALUE INT => 1
fex=FileExist(FileToCheck)
(40233) VALUE INT => 1
If fex
(40233) IF DO==>TRUE
################
From the information logged by the program, all the target locations are correct, and the files all exist, but it would appear that the RunShell command is hanging, as no result is returned in the DebugTrace output, and the service cannot be stopped from the command line, and has to be killed using task manager (before any actual RunShell operations are requested, the service starts and stops fine from the command line).
I wondered whether there might be some sort of error dialog being produced which cannot be seen by a user as on Win 7, services cannot interact with the desktop. I also wondered whether there might be a UAC issue, or I'm missing something blatantly obvious that you will be able to point out to me.
Any suggestions for better error trapping in this sort of non-interactive environment would also be welcomed.
Thanks!
Ed