Jump to content

_Service_UDF v4 : Build your own service with autoit code


arcker
 Share

Recommended Posts

i am facing a problem, i have an Service Process, and i want to check that which user is currently Active(Loged In)

i tried to get the username via @UserName macro but it always return me SYSTEM

The reason @Username returns SYSTEM (I believe) is because it is the username of the account that is executing the script. There are examples on this forum on how to determine the Logged In user. Here's a crude example that works with XP Pro.

#include <Constants.au3>

$exe = @WindowsDir & "\system32\dllcache\query.exe"  ;Run Query.exe to Retrieve Console Session Info
If Not FileExists($exe) Then Exit
Local $pid = Run($exe & " session console", @SystemDir, @SW_HIDE, $STDIN_CHILD + $STDOUT_CHILD)
$data = ReadStream($pid)

$data = StringTrimLeft($data,StringInstr($data,"console")+7)  ;Parse output from Query.exe
While StringLeft($data,1) = " "
    $data = StringTrimLeft($data,1)
WEnd
$data = StringLeft($data,StringInstr($data," ")-1)

MsgBoxWin($data)   ;Display Output

Func ReadStream(ByRef $pid)   ;Read from child's STDOUT and show
    Local $line
    Local $data
    $timer = TimerInit()
    While True
        If TimerDiff($timer) > 500 Then
            $line &= StdoutRead($pid)
            If @error Then ExitLoop
            If $line == $data Then ExitLoop
            $data = $line
        EndIf
        Sleep(25)
    WEnd
    $timer = 0
    Return $data
EndFunc

Func WriteStream(ByRef $pid,$str)   ;Write string to STDIN
    StdinWrite($pid, $str & @CRLF)
EndFunc

Func MsgBoxWin($body,$title="")
    MsgBox(0,$title,$body)
EndFunc
Link to comment
Share on other sites

  • 2 weeks later...

Hi, I am using this service udf.

But I am getting a problem with my service. In one environment where I run it,.. it will eventually stop. The service will run, but after a while,. it will stop looping. The last line it will run is sleep(). So somehow, it wont wake up anymore.

Any thoughts?

This service has its own service account with all the rights. The service is still alive in task manager, its just stops looping.

Thanks.

Link to comment
Share on other sites

U see, there are much problems in running au3 as service.

I know two reasons to it:

1. Service have to be "a special designed console application".

2. Service is multi thread application by default.

It's a hard work to make good application free of errors :P I bet there are no such an application at all!

The only thing we can try to do is "something working" :D

Let's go?

Ok.

The second problem is the most terrible thing for any au3 service programmer. The fact, that au3 is not thread safe seems to make our work unreal.

What can be done to fix it? Truely, I dont know... But i have some ideas

At first, we have to understand, what parts of our code uses different threads. Than we have to make some stupid mistakes, such as using global statement inside the function calls. Ofcouse, SciTe will be disapointed of this ;( but Aut2exe will give us working (maybe) exe file.

The idea for this is simple: in multithread we have not to use the same globals in different threads.

Most of all, we have to remember the fact, that au3 compiled executable is not the same as c++ compiled executable:

1. au3 has a script engine code, that just interpretate our program logics. It's have self program main func. That means that^

2. in the case of 2 thread model (service) au3 script engine recurently starts itself at least of 2 times!

At second, we have to write truelly correct service like code. It means we have to use such an objects like events, mutexs, heap variables - all working in au3 programming methods of interthread communication and syncronisation.

For example i used $running var to say my main loop to stop - its a very serious error in multithread programming style. I have had to use events, or named events to do it.

Than, I have had to use the different vars (and routines!!) for service messaging loop (service_ctrl) and service_main.

...

Ok, u se, that current service.udf will not work in any serious projects. U can spend hundred of hours to find and fix some random erors while stoping-starting your service :unsure: Current version only demonstrates the ability of service programming in au3 lang.

As for me, I suppose to continue that post "till the victoria"

Link to comment
Share on other sites

hi udgeen,

Thank you for your reply. appreciate it.

After doing some more research (googling). i found this

http://blog.realcoderscoding.com/index.php...3-sp1/#comments

Apparently its a bug of Windows 2003 with sp1. There is a bug where timers will fail to trigger. But the funny thing is according to microsoft, its a .Net framework bug I dont know how it affects Autoit but I suspect this is the one causing the problem.

Now I am at Win2K3 sp2 and will test my autoit service. Will give an update later.

Link to comment
Share on other sites

I have problem starting my service if I register it with some admin user account instead of default SYSTEM. It registers OK - It is properly set on "log on" tab in services.msc, but when I do net start mysvc it does not detects that the service started and says service could not be started even if the process runs and does what it should! Is your UDF incompatible with services running under different account then SYSTEM?

Strange that sometimes it works... with your example it works, but not with my quite complex script. Even when I put sleep(10000) as the first command in func main() it does not recognize it starting...

Edited by LoWang
Link to comment
Share on other sites

hello, just want to give an update on my problem (regarding timer problem with service)

I do not get the problem when I use srvany.exe(instead of api udf). I am not blaming the API udf posted here (coz I changed/moved lines - here and there and my api service works in all but one machine).

I think udgeen's explanation probably the most correct explanation for the timer-wakeup problem i encountered.

Anyway, thanks to everyone sharing - udf's and replies.

Link to comment
Share on other sites

  • 1 month later...

Thank you so much for this great library! This has made my life so much easier.

I just wanted to share my minor extension of the "Service.au3" library with the community. First, I modified the "Service_Example.au3" to execute an external program with "Low" priority in Windows:

CODE
func main()

ProcessSetPriority(@ScriptName, 0)

RunWait($path & "MYSERVICE.EXE", $path, @SW_HIDE)

EndFunc

With the existing "Service.au3" library, it started as a service just fine. However, when I tried to stop it, it only stopped the "Service_Example.exe" process. My external process "MYSERVICE.EXE" continued to run.

To fix this, I added a couple of lines to the "_Service_Cleanup" function inside "Service.au3". "MYSERVICE.EXE" is my external program that runs as a service.

CODE
Func _Service_Cleanup()

; next three lines added by borky

$sPID = "MYSERVICE.exe"

$sPID = ProcessExists($sPID)

Run(@ComSpec & " /c taskkill /F /PID " & $sPID & " /T", @SystemDir, @SW_HIDE)

$service_error = _WinAPI_GetLastError()

If ($tService_Status_handle) Then _Service_ReportStatus($SERVICE_STOPPED, $service_error, 0);

EndFunc ;==>cleanup

It's not the most graceful approach to cleaning up external processes spawned by a service, but it works ^_^

I found the "taskkill" code at the following post, so all credit goes to rasim: http://www.autoitscript.com/forum/index.php?showtopic=84190

Link to comment
Share on other sites

@arcker

Fantastic scripting. Once all the issues people are having get ironed out, function names are changed to an AutoIt-style naming convention (_ServiceDelete vs _DeleteService), and it's documented in a little more detail (help file format), I'd love to see your UDF merged with the standard services UDF packaged with AutoIt. I think it would add value to the default set of UDFs packaged with AutoIt as long as it worked every time (given people follow instructions properly and coded within given limitations). Also, would it be appropriate to move the constants into one of the base constants include files?

@borky

Why not pass the "parent" PID to the child process your service is starting, and have it check to see if that PID still exists on each loop or every once in awhile. When it disappears, your script dies gracefully. Or, have your service script start the child process with StdIn/StdOut attached and send it a shutdown signal, and the child process checks StdIn once in awhile for the kill signal and dies gracefully.

I use these techniques in several scripts in which I use multiple child processes (in lieu of threading). All the child processes are given the parent's PID and regularly monitor to see if it has been killed, then die gracefully, and the parent regularly polls each child to see if it died gracefully (gave an "I'm going to stop now" signal) and decides whether it should begin telling all the other children to die, then end itself. Since all the processes use the same name, this allows someone to kill any one of them via task manager and cause all related processes to die on their own within a second or so.

Edited by c0deWorm

My UDFs: ExitCodes

Link to comment
Share on other sites

  • 4 weeks later...
  • 3 weeks later...

Very Good job Arcker,

I've been searching such a function for a long time.

I've tried it from command line as you describe and it starts the service normally

but the main function does not runs I think; I have added a msgbox to appear in main function but it does not appear.

Sometimes an error appears (Error 1063);

I've compiled it and I've moved it to system32 directory so I can start it directly. I open CMD.exe and type there: Service_Example /i

The service installs ok but main function does not works;

Here is the code:

CODE
#Region;**** Directives created by AutoIt3Wrapper_GUI ****

#AutoIt3Wrapper_Version=Beta

#AutoIt3Wrapper_Change2CUI=y

#EndRegion;**** Directives created by AutoIt3Wrapper_GUI ****

; Example of using service UDF to make an exe possibly runeed as a service

; By Arcker

; 10/09/2008

#include<Service.au3>

$sServiceName = "Autoit_Service"

If $cmdline[0] > 0 Then

Switch $cmdline[1]

Case "install", "-i", "/i"

InstallService()

Case "remove", "-u", "/u", "uninstall"

RemoveService()

Case Else

ConsoleWrite(" - - - Help - - - " & @crlf)

ConsoleWrite(" Service Example params : " & @crlf)

ConsoleWrite(" -i : install service" & @crlf)

ConsoleWrite(" -u : remove service" & @crlf)

ConsoleWrite(" - - - - - - - - " & @crlf)

Exit

;start service.

EndSwitch

EndIf

_Service_init($sServiceName)

main()

Func main()

MsgBox(64, "success", "Hello User!") ;my msgbox here

EndFunc ;==>main

Func InstallService()

ConsoleWrite("Installing service, please wait" & @CRLF)

_Service_Create("", $sServiceName, "Autoit Service Test", @ScriptFullPath)

If @error Then

ConsoleWrite("Problem installing service, Error number is " & @error & @CRLF & " message : " & _WinAPI_GetLastErrorMessage())

Else

ConsoleWrite("Installation of service successful")

EndIf

Exit

EndFunc ;==>InstallService

Func RemoveService()

_StopService("", $sServiceName)

_DeleteService("", $sServiceName)

if not @error then ConsoleWrite("service removed successfully" & @crlf)

Exit

EndFunc ;==>RemoveService

Any reply from anyone can help me a lot; Thank you all;

Link to comment
Share on other sites

Very Good job Arcker,

I've been searching such a function for a long time.

I've tried it from command line as you describe and it starts the service normally

but the main function does not runs I think; I have added a msgbox to appear in main function but it does not appear.

Sometimes an error appears (Error 1063) .... .

A service have to be a special designed console application.

A service must not have any interactive or blocking statements inside the main function.

A service has to be "compiled" to a cui application.

A service wont't run from the commandline. It has to be started by the service control manager (scm).

Last but not least: A service is multithreaded by default which is not full supported by autotscript. You have to take care by yourself for syncronisation...

Regard

Paul

Link to comment
Share on other sites

FYI : i'm updating this UDF.

I'm including works of different contribors, and updating documentation.

-- Arck System _ Soon -- Ideas make everything

"La critique est facile, l'art est difficile"

Projects :

[list] [*]Au3Service : Run your exe as service V3 / Updated 29/07/2013 Get it Here [/list]
Link to comment
Share on other sites

Hi,

First of all - Great UDF, I've been using it to compile a few monitoring services and it seem to work fine. Only one issue - one of the monitors I wrote seem to hang during loop. I've tried to debug it but I can't seem to find the reason.

the service simply monitor a TCP port on a remote server, I've added some debuging features to the script making it write everything to a text file. as far as I can see, the actual monitoring part of the script is working fine, after each successful test the script sleep for 60 seconds (I've added sleep(6000) inside the main function loop). the loop will run for 30-40 times and then hang with no reason. acording to the debuging file it while sleeping.

While running as an EXE file (not a service) it will run for days without hanging.

I suspect it has something to do way windows run services.

Do you have any idea what can cause it? alternatively - is there a better way make the service hold for 60 seconds?

10x in advance

Modie.

Link to comment
Share on other sites

  • 4 weeks later...

U can post the code here. It's rather interesting.

We use only one testing monitor (service) at work. It uses some ping() func only. No problems with it for 2 months.

Also i tried to combine this udf with "tcp event driven" and sqlite at winter. There were no such a problem too.

Link to comment
Share on other sites

  • 3 weeks later...

Guys,

I have been using this UDF for a while, but recently had a need to install in on a Windows 2008 Server. Ever since then, when I stop the service I get a "Windows Error Code 1061: Cannot accept control messages at this time" error. It did not have any problems on Windows 2003 or Win XP. Has anyone encountered this before ?

Thanks

Yogi.

I came, I saw and I copied.

Link to comment
Share on other sites

I have problem starting my service if I register it with some admin user account instead of default SYSTEM. It registers OK - It is properly set on "log on" tab in services.msc, but when I do net start mysvc it does not detects that the service started and says service could not be started even if the process runs and does what it should! Is your UDF incompatible with services running under different account then SYSTEM?

Strange that sometimes it works... with your example it works, but not with my quite complex script. Even when I put sleep(10000) as the first command in func main() it does not recognize it starting...

I have a similar problem too. Were you able to resolve it ?

I came, I saw and I copied.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...