AutoIt script plugin

Recommended Posts

Demonstration of a simple Autoit script plug-in architecture.

I have a fascination for distributed computing. Collecting the power of spare CPU cycles within a organization or on computers administrated by people willing to donate spare resources (and electricity).

When I took a look at Blue Security "opt out spam client", I realized that AutoIt could be a very strong contender if you need to build a quick distributed computing system.

The first step towards a simple distributed computing framework utilizing AutoIts compact core language and a selection of powerful functions, was do collect the code fragments needed to support a plug-in architecture.

This is what I came up with:

• mainProg.au3: Basically a simple installation program. It has to contain the Autoit compiler, your application scripts and probably some default plug-ins.
• runner.au3: The application script. It will be modified and compiled by mainProg.au3 to use the plug-ins (default, user-modified or downloaded from the net).
• plugin.au3: A default plug-in used to demonstrate "prof of concept".
I also defined a api provided by the runner (implemented in runner.au3). The api can be used by plug-ins.
• apiMsg( $argMsg ): A message function. •$apiVersion: A global variable containing a version number.
To let the runner know how to use the plug-in the plug-in has to implement a specific interface. In this case it is rather simple:

* IRun(): Called by runner to run plug-in code.

* ITerminate(): Called by runner to lett plug-in clean up and terminate (ex: external processes)

So here goes. A the simplest working plug-in sample. NOTE: You have to modify paths to reflect your development machine (necessary places should be marked with TODO:)

; PURPOSE:  Demonstarting a Autoit Plug-in architecture.
;            This is the plugin used by runner.au3. It could be
; FILENAME:   plugin.au3
; CREATED BY: Uten
; HOME:
; CHANGE LOG:
;----------------------------------------------------------------------
; Uten    14 mar 2006: Published
;
;----------------------------------------------------------------------
Func IRun()
apiMsg("Hello from plug-in" & @CRLF & "I have done my work.:D")
EndFunc
Func ITerminate()
msgbox(0,"Plug-in sample, in plug-in", "Terminating" & @CRLF & "NOTE: $apiVersion:=" &$apiVersion & @CRLF & "I have cleand up my act")
EndFunc

; PURPOSE:  Demonstarting a Autoit Plug-in architecture.
;            This is the real application. Define api to be used by
;            plug-ins here.
; FILENAME:   runner.au3
; CREATED BY: Uten
; HOME:
; CHANGE LOG:
;----------------------------------------------------------------------
; Uten    14 mar 2006: Published
;
;----------------------------------------------------------------------

Global $apiVersion="1.0"; Global variable accessable from the Func apiMsg($argMsg)
msgbox(0, "Plug-in sample, runner prog", $argMsg) EndFunc ; Call to a function that must exist in the plugin! IRun() sleep(2000) ; Call ITerminate to lett the plugin clean up. ITerminate() Exit ; PURPOSE: Demonstrating a Autoit Plug-in architecture. ; This is the main application whos purpose is to configure ; and build the real application. ; FILENAME: mainProg.au3 ; CREATED BY: Uten ; HOME: ; CHANGE LOG: ;---------------------------------------------------------------------- ; Uten 14 mar 2006: Published ; ;---------------------------------------------------------------------- dim$mainProg = "\runner.au3"
dim $plugin = "plugin.au3" dim$targetAtoit = "\_au3.exe"; Use uniqe name to avoid conflicts.
;----------------------------------------------------------------------
; Installer section
; fileinstall will package the files at compiletime and unpack them at
; run time.
; NOTE: fileinstall must have absolute path at compiletime. Variables
;      are not evaluated.
; TODO: Modify location of AutoIt3.exe
fileinstall("C:\Program Files\AutoIt3\beta\AutoIt3.exe", @TempDir & _
$targetAtoit, 1) fileinstall("runner.au3", @TempDir &$mainProg,1)
; NOTE: You could download "plugin" files from the net rather than
; writing the default, or lett the user write one, like this.
fileinstall("plugin.au3", $plugin,0);0->Do not overwrite? ;---------------------------------------------------------------------- ; Modify necesary filesto relect new plugins, locations and such ; Add "plugin" include files to the end of the "main" prog. The "main" ; prog should be fresh each time.$file = FileOpen(@TempDir & $mainProg, 1) FileWrite($file, "#include "& Chr(34)  & @ScriptDir & "\" & _
$plugin & Chr(34) & @CRLF) FileClose($file)
;----------------------------------------------------------------------
; Compile the new program.
dim $cmd= chr(34) & @TempDir &$targetAtoit & chr(34) & _
" /AutoIt3ExecuteScript " & chr(34) &  @TempDir & _
$mainProg & chr(34) ;---------------------------------------------------------------------- ; Run the new program. This will appear in taskmanager as a new PID. RunWait($cmd, @TempDir)
;----------------------------------------------------------------------
; Clean up unpacked files if you want to. They will be unpacked again
; next time.
FileDelete(@TempDir & $mainProg) FileDelete(@TempDir &$targetAtoit)

Try it out.

Run mainProg.exe. This will create the default version of the program. A modifiable plug-in (plugin.au3) will be created in the same folder as you run mainProg.exe from. This file will not be replaced the next time you run mainProg.exe.

Now lets modify plugin.au3:

Func ITerminate()
apiMsg("Terminating" & @CRLF & "NOTE: $apiVersion:=" &$apiVersion & @CRLF & "Ready to terminate.")
EndFunc

And run mainProg.exe again.

If your modifications were successful the user experience of mainProg.exe should change.

When you design you next killer application you might want to consider:

• How can you prevent users from tampering with plug-ins you do not want them to touch?
• How will errors in user-modified scripts be handled?
• Is it possible to have multiple plugins. Say, load all plugins from a directory. (The demonstrated architecture only suports one plugin file).
Hopefully this little sample will give you a head start and inspiration to create the next modifiable, and possibly distributed, killer application.

Happy coding

plugin_architecture.zip

Edited by Uten

Share on other sites

nice 1, but maybe i use the wrong beta - where do you find apiMsg?

btw thanks for the ideas i just found a way of leaving my progs on just 1 pc :)

Share on other sites

Outch, I have used the same file twice. . Realy sorry about that. The attached file has what you need to compile the sample. It should work with version 3.1.1 (acka. does not need beta).

I'l corect the first post asap.

Regards

Uten

Share on other sites

This is a really good idea for scripts which are used by different users with different rights or needs.

Only problem I have is that AutoIt must be installed. Which is not the case for most users........

Could there be a way like a Mainscript and then plugins in say .au3 format which can still be used by the Mainscript? (I hope you understand.....)

PS: I really like your example!!

Share on other sites

You don't need to have AutoIt installed, just run this line when running an uncompiled .au3 file

Run(@AutoItExe & ' /AutoIt3ExecuteScript "' & $PathToFile & '"') Also, Uten, I like your idea for distributed computing in AutoIt. I've developed two functions that work well to pack and unpack variables for sending across a network, the internet, etc. Take a look: ;=============================================================================== ; ; Function Name: _PackVarToStr() ; Description: Packs a variable into a string form for sending to another ; script. Maintains array integrity up to 4 dimensions. ; Also supports nested arrays inside array elements. ; Parameter(s):$vPackVar = Variable to be packed into a string.
;                       NOTE: Variables of type Object or DllStructs are not yet
;                       supported.
; Return Value(s):  Returns packed string to be unpacked by _UnpackStrToVar()
; Author(s):        Ben Brightwell
;
;===============================================================================
Func _PackVarToStr(ByRef $vPackVar) Local$iNumDims = UBound($vPackVar, 0); Number of dimensions of$vPackVar
Local $sVarStr = "$[" & $iNumDims & "]$"; Return string of packed variable
Local $iCounter1 = ""; Nested Counter Local$iCounter2 = ""; Nested Counter
Local $iCounter3 = ""; Nested Counter Local$iCounter4 = ""; Nested Counter
For $i = 1 To$iNumDims
$sVarStr &= "$[" & UBound($vPackVar,$i) & "]$" Next Select Case$iNumDims == 0
If $vPackVar == "" Then$sVarStr &= "$[0]$<nil>"
Else
$sVarStr &=$vPackVar
EndIf
Case $iNumDims == 1 For$iCounter1 = 0 To UBound($vPackVar, 1) - 1 If$vPackVar[$iCounter1] == "" And UBound($vPackVar[$iCounter1], 0) == 0 Then$sVarStr &= "$[0]$<nil>"
Else
$sVarStr &= _PackVarToStr($vPackVar[$iCounter1]) EndIf Next Case$iNumDims == 2
For $iCounter1 = 0 To UBound($vPackVar, 1) - 1
For $iCounter2 = 0 To UBound($vPackVar, 2) - 1
If $vPackVar[$iCounter1][$iCounter2] == "" And UBound($vPackVar[$iCounter1][$iCounter2], 0) == 0 Then
$sVarStr &= "$[0]$<nil>" Else$sVarStr &= _PackVarToStr($vPackVar[$iCounter1][$iCounter2]) EndIf Next Next Case$iNumDims == 3
For $iCounter1 = 0 To UBound($vPackVar, 1) - 1
For $iCounter2 = 0 To UBound($vPackVar, 2) - 1
For $iCounter3 = 0 To UBound($vPackVar, 3) - 1
If $vPackVar[$iCounter1][$iCounter2][$iCounter3] == "" And UBound($vPackVar[$iCounter1][$iCounter2][$iCounter3], 0) == 0 Then
$sVarStr &= "$[0]$<nil>" Else$sVarStr &= _PackVarToStr($vPackVar[$iCounter1][$iCounter2][$iCounter3])
EndIf
Next
Next
Next
Case $iNumDims == 4 For$iCounter1 = 0 To UBound($vPackVar, 1) - 1 For$iCounter2 = 0 To UBound($vPackVar, 2) - 1 For$iCounter3 = 0 To UBound($vPackVar, 3) - 1 For$iCounter4 = 0 To UBound($vPackVar, 4) - 1 If$vPackVar[$iCounter1][$iCounter2][$iCounter3][$iCounter4] == "" And UBound($vPackVar[$iCounter1][$iCounter2][$iCounter3][$iCounter4], 0) == 0 Then$sVarStr &= "$[0]$<nil>"
Else
$sVarStr &= _PackVarToStr($vPackVar[$iCounter1][$iCounter2][$iCounter3][$iCounter4])
EndIf
Next
Next
Next
Next
EndSelect
Return $sVarStr EndFunc ;==>_PackVarToStr ;=============================================================================== ; ; Function Name: _UnpackStrToVar() ; Description: Turns a packed variable string back into a variable. ; Parameter(s):$sVarStr = A packed variable string as returned by _PackVarToStr()
; Return Value(s):  Variable of type that was passed to _PackVarToStr()
; Author(s):        Ben Brightwell
;
;===============================================================================
Func _UnpackStrToVar(ByRef $sVarStr) Local$aiNumDims = StringRegExp($sVarStr, '(?:\$$)(\d*)(?:$\$)(\#)', 1) Local$aiDimSizes[1]; To contain the size of each dimension as passed through $sVarStr Local$aiDimSize = ""; To contain the size of current dimension and string position for stripping
Local $avRetArr[1]; To be redimensioned and have$sVarStr parsed and stored into as an array
Local $avElementStr = ""; To contain each element as a string as it is parsed from$sVarStr
If IsArray($aiNumDims) Then$sVarStr = StringTrimLeft($sVarStr,$aiNumDims[1])
If $aiNumDims[0] > 0 Then ReDim$aiDimSizes[$aiNumDims[0]] For$iCounter1 = 0 To $aiNumDims[0] - 1$aiDimSize = StringRegExp($sVarStr, '(?:\$$)(\d*)(?:$\$)(\#)', 1)$aiDimSizes[$iCounter1] =$aiDimSize[0]
$sVarStr = StringTrimLeft($sVarStr, $aiDimSize[1]) Next EndIf Select Case$aiNumDims[0] == 0
If StringInStr($sVarStr, "$[") Then
$avElementStr = StringRegExp($sVarStr, '(.*?)(\#)(?:\$\[)', 1)$sVarStr = StringTrimLeft($sVarStr,$avElementStr[1])
If $avElementStr[0] <> "<nil>" Then Return$avElementStr[0]
Else
Return ""
EndIf
Else
If $sVarStr == "<nil>" Then Return "" Else Return$sVarStr
EndIf
EndIf
Case $aiNumDims[0] == 1 ReDim$avRetArr[$aiDimSizes[0]] For$iCounter1 = 0 To $aiDimSizes[0] - 1$avRetArr[$iCounter1] = _UnpackStrToVar($sVarStr); In case element holds another array (Recursive)
Next
Case $aiNumDims[0] == 2 ReDim$avRetArr[$aiDimSizes[0]][$aiDimSizes[1]]
For $iCounter1 = 0 To$aiDimSizes[0] - 1
For $iCounter2 = 0 To$aiDimSizes[1] - 1
$avRetArr[$iCounter1][$iCounter2] = _UnpackStrToVar($sVarStr); In case element holds another array (Recursive)
Next
Next
Case $aiNumDims[0] == 3 ReDim$avRetArr[$aiDimSizes[0]][$aiDimSizes[1]][$aiDimSizes[2]] For$iCounter1 = 0 To $aiDimSizes[0] - 1 For$iCounter2 = 0 To $aiDimSizes[1] - 1 For$iCounter3 = 0 To $aiDimSizes[2] - 1$avRetArr[$iCounter1][$iCounter2][$iCounter3] = _UnpackStrToVar($sVarStr); In case element holds another array (Recursive)
Next
Next
Next
Case $aiNumDims[0] == 4 ReDim$avRetArr[$aiDimSizes[0]][$aiDimSizes[1]][$aiDimSizes[2]][$aiDimSizes[3]]
For $iCounter1 = 0 To$aiDimSizes[0] - 1
For $iCounter2 = 0 To$aiDimSizes[1] - 1
For $iCounter3 = 0 To$aiDimSizes[2] - 1
For $iCounter4 = 0 To$aiDimSizes[3] - 1
$avRetArr[$iCounter1][$iCounter2][$iCounter3][$iCounter4] = _UnpackStrToVar($sVarStr); In case element holds another array (Recursive)
Next
Next
Next
Next
EndSelect
Return $avRetArr EndIf EndFunc ;==>_UnpackStrToVar Use _PackVarToStr() to pack a variable up for sending, and then on the other end, once you receive the string, use _UnpackStrToVar() to turn it back into its original variable state. If it's an array, it can even send up to a 4-dimensional array, and nested arrays inside elements of other arrays. All untouched when they come out the other side. Very useful for sending information between scripts via the Std***() and Console***() functions and via TCP/IP, etc. [u]My UDFs[/u]Coroutine Multithreading UDF LibraryStringRegExp GuideRandom EncryptorArrayToDisplayString"The Brain, expecting disaster, fails to find the obvious solution." -- neogia Share this post Link to post Share on other sites$file = FileOpen(@TempDir & $mainProg, 1) FileWrite($file, "#include "& Chr(34) & @ScriptDir & "\" & _

$plugin & Chr(34) & @CRLF) FileClose($file)

You don't need to have AutoIt installed, just run this line when running an uncompiled .au3 file

Run(@AutoItExe & ' /AutoIt3ExecuteScript "' & $PathToFile & '"') Thank you both so much, I have been looking for a way to do modules for a program I am making, I combined the two and got this: #include <Array.au3> #include <File.au3> Dim$moduleList[1]
$moduleList=_FileListToArray(@ScriptDir & "\Modules\", "*.au3") If (Not IsArray($moduleList)) Or (@Error=1) Then
MsgBox (0,"","No modules found.")
Exit
EndIf
_ArrayReverse($moduleList) _ArrayPop($moduleList)
$file = FileOpen(@ScriptDir & '\includes.au3', 2) For$plugin In $moduleList FileWrite($file, "#include "& Chr(34)  & @ScriptDir & "\Modules\" & $plugin & Chr(34) & @CRLF) Next FileClose($file)

Run(@AutoItExe & ' /AutoIt3ExecuteScript "'&_PathFull('Main.au3')&'"')

This loads everything in the /Modules directory that ends in .au3 and puts it in includes.au3 which is included at the bottom of Main.au3. Have fun with this.

P.S. This requires the Beta

P.S.S. The modules are loaded in reverse alphabetical order.

Share on other sites

P.S. This requires the Beta

Out of pure curiousity... you say this requires "beta", if you are using the above scripts, I can see where in the latest betas they may break...what beta(s) are you using?

Common sense plays a role in the basics of understanding AutoIt... If you're lacking in that, do us all a favor, and step away from the computer.

Share on other sites

Sorry I'm late on this. Fore some reason I don't get mail notifications on several threads that I'd like to monitor. Could be my own fiddling with forum settings though

Sorry but I can't really remember. I think I used 3.1.1.102 at the time, but I have not made any notes on it.

Create an account

Register a new account