_Service_UDF v4 : Build your own service with autoit code


360 posts in this topic

Posted (edited)

I can't be responsible for any impact of your script or services you would run in production.


| _Services_UDF v4
|
| By Arcker
| Run your script as a service
| Req. AutoIt 3.3.8.1 min, last beta recommended ( tested in 3.3.9.21 ) 
|_______________________________________

++ Preparing v5

!! Checked on 27/04/2015
<> Updated on 10/23/2013

<> Updated 07/29/2013
<> Best practices Updated 24/02/2012

 -- Removed GUI code, and old codes.
== Warning : 26/02/2012
<> New Version (v3 ) : 29/07/2013
|
Thx to ShminkyBoy, wraithdu, Udgeen and HolmesShelock for their great contributions.

Special thx to bitboy,
 

Downloads

see end of this post : ServiceExample_v4, Services.au3, ServicesConstants.au3

Presentation

Here is my _Service _ UDF

With this UDF, you can compile your autoit script and run it as service.

Any comments are welcome.

Hope you'll like it,

 
1 - changelog

Update V4 : 23/10/2013,27/04/2015

Give it a try if you run older script with older version, you'll really see the difference.

A lot of bugs are corrected, ( stop issue ( not perfect but much better ) and _Main not executed sometimes )

With the help of recent users i''ve recoded the whole engine to make it act really as a service.

Update : V3 on 29/07/2013

All of the script is "hidden" in services.au3
Please don't touch it, every code you post must be from this original Services.au3
The bug concerning Win7 ( 1061 when stopping service ) is fixed.
All of the GUI related script has been removed. I don't support GUI creation, and since Win7, it's not more possible.
Remember to compile your script as CUI to avoid msgbox errors that would stop the service to be responsive.
The last version was a fusion of differents functions, and this was not a good idea at all.
Now it's back like v1.
The race condition kill service has been removed too. If your script fails when stopping it, please report it.

 
Engine script has been reworked. The only function you need now is Main.

24/02/2012 added threadsafe advice
27/05/2011 Thread safe functions added. ServiceExample_ThreadSafe uploaded to this method.
added HolmesShelock examples.
11/01/2010 Updated with best practices


 2 - Features

- Transform your application by adding few functions to make it compatible running as a windows service

3 - Known Issues.



4 - Howto.


Update v2 : 02/03/2012

Now, see the updated example in page 1 to see the update. Respect the comments in code by not removing anything, and you service should run fine .... until stop :)

Todo :
I'll maybe add an option to execute a thread that just the service to be stopped. I've to do this in C++ then use trancexx udf, but maybe it's possible, and will improve stop service feature

  • Compile the service example to see how to do then you can use the main function for your proper script
  • Choose an appropriate location and copy the script here.
  • open a command prompt, and execute the compiled script with option –I ( must be admin right since vista )
  • start / execute "services.msc" . You will see a service called "Autoit_service". you can execute "sc query" in a command prompt too.
  • Launch it : it will run the previous compile script. The main function will be executed after the service_init.
  • stop and relaunch the service as you wish.



Examples / Session Change / uses v1 (lock, logon, etc) By HolmesShelock. Awesome work.
Edit : this example doesn't use v2 or v3 method

SessionChange Service.zip

Example output to give you an idea

______________________________________
Log created: May 27, 2011 : 7:18:53 PM
______________________________________


May 27, 2011 : 7:18:53 PM [3144] >> Service started: Sessionchange
May 27, 2011 : 7:19:05 PM [3144] >> cbSize = 8 ,dwSessionId = 0


May 27, 2011 : 7:19:05 PM [3144] >> Session locked
May 27, 2011 : 7:19:12 PM [3144] >> cbSize = 8 ,dwSessionId = 0


May 27, 2011 : 7:19:12 PM [3144] >> Console session disconnected
May 27, 2011 : 7:19:12 PM [3144] >> cbSize = 8 ,dwSessionId = 0


May 27, 2011 : 7:19:12 PM [3144] >> Session unlocked
May 27, 2011 : 7:19:12 PM [3144] >> cbSize = 8 ,dwSessionId = 1


May 27, 2011 : 7:19:12 PM [3144] >> Console session connected
May 27, 2011 : 7:19:14 PM [3144] >> cbSize = 8 ,dwSessionId = 1


May 27, 2011 : 7:19:14 PM [3144] >> Session logged on
May 27, 2011 : 7:19:38 PM [3144] >> cbSize = 8 ,dwSessionId = 1


May 27, 2011 : 7:19:38 PM [3144] >> Session locked
May 27, 2011 : 7:19:40 PM [3144] >> cbSize = 8 ,dwSessionId = 1


May 27, 2011 : 7:19:40 PM [3144] >> Console session disconnected
May 27, 2011 : 7:19:41 PM [3144] >> cbSize = 8 ,dwSessionId = 0


May 27, 2011 : 7:19:41 PM [3144] >> Console session connected
May 27, 2011 : 7:19:41 PM [3144] >> cbSize = 8 ,dwSessionId = 1

Examples demos

Synoptic

And for End Users, I've tried to explain the service control manager and the way it works with programs.

see below

Best Practice, by arcker :

/! please read this if you encounter stability issues

I've had to rework on services.au3 to reflect threadsafe.
Threadsafe means each dll call must be on "dllopened" instead of calling dll directly
for example, if you need a function in kernel32.dll, you have to open it and call it after.
That's contraignant since a lot of integrated functions in autoit are not programmed this way.
If you take my services.au3, at the end, you'll see reworked function to handle this change. I've not tried
services for a while, since i don't need it for now, but i can help you only if your provide whole script, to see how you call every functions.
I can tell that fix stability issues in callback, but i don't think it's need for your script, as far as you don't use callback.


Best Practice, by udgeen :

Good news! Probably i've found the working method of integrating service.au3 with other projects

I realised it after ASock.au3 project by Zatorg (please, sorry if im wrong) - ASock is asynck socket - tcp on event (uses ws2_32.dll).

Asock & sqlite didnt work together. The only reason for that was..._ArrayDisplay() func with gui!!! Ok. I made special sqlite.au3 without dependences. It works, but my udf seemed too be much heavy to use at another project.

Few weeks ago I returned to service.au3 and found TCP UDF, Event driven project based on ASock.au3. It seemed to work as example, but didn't work at all as service.

In my variant of service_example.au3 i posted msdn words about service_main procedure, that it must contain all global vars of project

I'll try to post that project as an another service_example.au3 in some weeks here. If it will works.

Some advice: use SysInternals Process Explorer (free gui based), or kill.exe from support tools to stop suspended service process. U can start-stop service even net start/net stop commands. Use file logging while debuging. No need to install-uninstall service after every recompilation: only stop, recompile, start. See, if it was suspended (while stopping) - kill process. The only reason not to stop service correctly i've found for today is unclean exit: opened sockets, maybe some dlls.
Edit : Arcker note: too bad, that would be too easy. unclean exit is not due to opened dll, but by some more internal exit checks or something.

FAQ.

1 - Hey, I've got error 1063 or error 0, what does that mean ?

It simply means that you intend to run your script in scite or directly by executing it.

Your script has to be installed as service and run from it. Several ways to do it :

  • You have to combine all Globals in one place: for example at the the begining. You have to make it with other udfs too... silly work
  • Then sort Global Const and other Global statements. If u see something like global $x = 1 do that way: global $x leeve at top. $x =1 insert into module's (udf or project) Init func.
  • U have to do it even with standart udf... or use only necessary functions from it in your own include. Or... maybe u have another plan?
  • Try to build your project with modified udfs - does it works now? Hmm... But it have to Maybe later after everything will work fine i'll say "msdn is wrong, microsoft lies"... But maybe i'll say that microsoft - is not so bad, because autoit works in it
  • net start "yourservicename"
  • Services.msc
  • Use API provided _Service_start, Service_stop.

 2 - I want to make a GUI !

It's not possible in a service.
Create another process and communicate with your service by using IPC.

3 - I've to let the main function that way ? That's contraignant.

Hey, a script intended to run as service is special no ? Autoit is not C++. The only way to have a "Main" in autoit is to have a function.

If you find another way, please share it :)

post-8413-12566401082517_thumb.jpg

ServiceExample_ThreadSafe.au3

 

Services.au3

ServiceExample_v4.au3

ServicesConstants.au3

Edited by arcker
2 people like this

Share this post


Link to post
Share on other sites



Posted

okkkkk

one feature requested for so long time, and no comment at all.... :)

maybe the title is not enough explicit....

Share this post


Link to post
Share on other sites

Posted

Very nice arcker! Bout time this was done.

Share this post


Link to post
Share on other sites

Posted

okkkkk

one feature requested for so long time, and no comment at all.... :)

maybe the title is not enough explicit....

Ahem.. "Silence is golden" >_<

Very nice arcker.

Share this post


Link to post
Share on other sites

Posted

Wow, exactly what I have been looking for, GOOD JOB !

Share this post


Link to post
Share on other sites

Posted

@ Thx all

I'm improving it to make it compatible with pause order and interrogate.

Bout time this was done.

What do you mean ? If you're talking about P-Trex method, using srvany, it's not a "standart" option.

Mine is the same method as all of the C++ program and other language program.

It was not possible before because callback was not implemented.

Any suggestions / improvements are welcome.

Share this post


Link to post
Share on other sites

Posted

very good UDF and example!

Share this post


Link to post
Share on other sites

Posted

Hey man great work! I was wondering what I should do though, I keep getting Error 0. When I try and run your example script or one of my own such as this:

#include<Service.au3>
_Service_Init("TestSerice")
While 1
    ConsoleWrite("Loop"&@CRLF)
    Sleep(100)
WEnd

Share this post


Link to post
Share on other sites

Posted

because you run it in debugger...

so it is your autoit3.exe that runs.

Tomkae your script work, compile it and create a service linked to it.

my example integrates an install / uninstall option.

Share this post


Link to post
Share on other sites

Posted

I tried compiling the example script you provided above as well and sucsesfully installed the process. Should I now just re-exucaute the script without any params? Or is there something esle I should do before that. I also noticed the main() function was not run so I put it in after _Service_Init, and that is when I get error 0. Any ideas?

I apologize for my inconvienience, I am new to the whole service idea.

#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()
    While 1
        ConsoleWrite("IN MAIN"&@CRLF)
    ;doing what you want here
        Sleep(1000)
    WEnd
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

Share this post


Link to post
Share on other sites

Posted

I get error 1063 with a compiled script.

Share this post


Link to post
Share on other sites

Posted

I get error 1063 with a compiled script.

That is expected, just drop it on command prompt and add -i or whatever

Share this post


Link to post
Share on other sites

Posted

ok i'll try to reexplain.

the example script contains a sort of autoinstall / uninstall.

Please dinstinguish it from the whole service processus.

1 - Compile the example

2 - Choose an appropriate location and copy the script here.

3 - open a command prompt, and execute the compiled script with option -i

4 - start / execute "services.msc" . You will see a service called "Autoit_service". you can execute "sc query" in a command prompt too.

5 - Launch it : it will run the previous compile script. The main function will be executed after the service_init.

6 - stop and relaunch the service as you wish.

Error 1063 : follow this procedure. Don't run it from scite

Error 0 : same

Share this post


Link to post
Share on other sites

Posted

I still experience error 1063. even though it is compiled and installed. If I run it directly it errors. If I start it from the SCM it starts but I do not get the expected results.

Share this post


Link to post
Share on other sites

Posted

for me it works !! :)

you have to open a command prompt and type "Service_Example.exe /i"

type: SC QUERY

you get the result:

SERVICE_NAME: Autoit_Service

DISPLAY_NAME: Autoit Service Test

TYPE : 10

STATE : 4 R

(

WIN32_EXIT_CODE : 0 (

SERVICE_EXIT_CODE : 0 (

CHECKPOINT : 0x0

WAIT_HINT : 0x0

Share this post


Link to post
Share on other sites

Posted (edited)

for me it works !! >_<

be sure you have administrator privileges !!!!!!!!!!!!!!!!!

you have to open a command prompt and type "Service_Example.exe /i"

type: SC QUERY

you get the result:

SERVICE_NAME: Autoit_Service

DISPLAY_NAME: Autoit Service Test

TYPE : 10

STATE : 4 R

(

WIN32_EXIT_CODE : 0 (

SERVICE_EXIT_CODE : 0 (

CHECKPOINT : 0x0

WAIT_HINT : 0x0

@Arcker greate stuff, well done :)

Edited by Micha1405

Share this post


Link to post
Share on other sites

Posted (edited)

So from what I can tell, the service starts fine, but my main function never runs. my main func consists of a filewrite to c:\text.txt and a 10 second sleep.

Inside a while loop of course

Edited by spudw2k

Share this post


Link to post
Share on other sites

Posted

remember that the service runs under system account privileges.

i didn't test some function in the while loop because it's seems logical that it loop here.

plz post the code here. i'll take a look. I don't pretend that there is no bug :)

Share this post


Link to post
Share on other sites

Posted (edited)

remember that the service runs under system account privileges.

i didn't test some function in the while loop because it's seems logical that it loop here.

plz post the code here. i'll take a look. I don't pretend that there is no bug :)

I appreciate any support you can give. Great job so far.

#NoTrayIcon
#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)

Func main()
    While 1
        ;doing what you want here
        fileWrite("C:\test.txt",@ComputerName & @TAB & @ScriptName & @CRLF)
        Sleep(10000)
    WEnd
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
Edited by spudw2k

Share this post


Link to post
Share on other sites

Posted

So, when is that function 'main()' called?

Share this post


Link to post
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