Jump to content

Recommended Posts

Posted (edited)

Hi Folks,

this is a simple example of how to generate a function with and call it during runtime (+ pass data from/to it). It is accomplished by using AutoItObject and inspired by Access AutoIt

#include <AutoItGenFuncEx.au3>

$iSleepTime = 500

$oFunction = _AutoItGenFuncEx_CreateFunction("$iTreshold", _;           <------ What are the Parameters?
        '' _;                                                           <------ What is the Sourcecode?
         & @LF & 'For $i = 1 to 100' _
         & @LF & '  IF $i <= $iTreshold then ' _
         & @LF & '    Sleep(10)' _
         & @LF & '  Else' _
         & @LF & '    Sleep($iSleepTime) ; slower at the end.' _
         & @LF & '  EndIf' _
         & @LF & '' _
         & @LF & '  ToolTip("Counter:" & $i)' _
         & @LF & 'Next', _
        "") ;                                                   <------ What should be returned?

If IsObj($oFunction) Then
    $oFunction.Call(Random(90, 95, 1))
EndIf

There are many discussions about running "dynamic" autoit code on the internet - and there are a lot of alternatives for this. And almost always, it is absolutely not necessary to do such a thing as generating function code at runtime, because you're better off with a different program design choice. My need for this came from a very special use case.

People have tried to approach this problem in several ways each with it's own set of advantages and limitations - very simple tasks can be accomplished with Execute(), Call(), Eval(), Assign(), or running source with /AutoItScriptExecuteLine or /AutoItScriptExecute, more advanced tasks can usually be tackled with multiple scripts communicating with each other via files, console streams, windows messages, tcp, dllcallbacks, etc.

The most advanced discussions I've found on this included to get 'autoit function pointers', adding threads to the autoit script or copying the function into a new process, which, to my knowledge, have never been really successful, stable, or easy to use. - and this UDF is no exception because it still comes with it's limitations: For example you can't use ByRef parameters at all and global variables from the main program are just copies - but not actual references.

Threads / Alternatives: 

Dynamic functions

Is it possible to find out the memory address of a internal function and retreive the struct of it?

_DllCall.au3 Callback - no external library (dll) required

DotNet.au3 DotNet.au3 UDF to access .NET Framework from AutoIt

Lua.au3 Embedding Lua scripts in AutoIt

AssembleIt _AssembleIt() for FASM.au3 now with "Debugger"

Standalone Example

The goal of the standalone script is to showcase the idea & inspire people to explore AutoItObjects and the RoT.

  Reveal hidden contents

Simple UDF "AutoItGenFunc"

The goal of the UDF is to have an easy way for beginners and lazy people to use way to allow "complex" AutoIt Coding to be executed during Runtime.

  Reveal hidden contents

Extended UDF "AutoItGenFuncEx"

The goal of the extended UDF is to have an easy to use way for beginners and lazy people to allow even more "complex" AutoIt Coding (callbacks) to be executed during Runtime.

  Reveal hidden contents

 

Download within the corresponding Spoilers, or find version history below:

Update 17.02.2021:

- added parameter to keep functions alive, see Example #5
- Global Variables are now changeable and copied back into the main script, see Updated Example #4.
- added $oFunction.Source which stores the AutoIt-Source of the generated file for debugging purposes.
- better handling of global vars and funcs
- added logic for also resolving global vars and functions in included files.
- added #AutoItGenFuncEx_SkipInclude directive to indicate that a include should not be "wrapped", thus not being callable from a generated function.

AutoItGenFuncEx_v3.zipFetching info...

 

Update 15.02.2021:

- access global variables (read-only) and callback functions from main program.
- added #AutoItGenFuncEx_AllowCallbacks directive to indicate that functions in the main script should be wrapped into callback-enabled functions. See Example #4.

AutoItGenFuncEx_v2.zipFetching info...

Initial Post:

generate function.zipFetching info...

AutoItGenFunc.zipFetching info...

I did not find any easier solution for sharing data and functions between scripts yet, so this is my dirty hack. Maybe it's useful for someone with the same problem.

I would also be happy if someone would point out alternatives or improvements. I feel like there is potential.

GIF 15.02.2021 02-59-04.gif

Edited by SEuBo
v3, Example 4 updated, Example 5 added
Posted

I am missing the why I would do it this way and not compile directly your code with everything in it.

anyway check the eval, execute, assign, call in the helpfile

Example of usage besides the help example it interprets a simple multiline string as a micro language (no loop constructs supported)

#include <MsgBoxConstants.au3>

func moreDynamic($str)
   consolewrite($str)
EndFunc

;Assign function to variable
$f=MsgBox
$f($MB_SYSTEMMODAL, "Title","dynamic function")
$f=moreDynamic
$f("just some text")

;Call function
$strMultiLine = "msgbox 0 title fromstring'"  & @Crlf
$strMultiLine &= "moredynamic texttoshowdynamic" & @Crlf
$strMultiLine &= "consolewrite texttoshowdynamic2console" & @Crlf


$arrLines=stringsplit($strMultiLine, @crlf)
dim $i
for $i=0 to ubound($arrLines)-1
   $fArray=stringsplit($arrLines[$i], " ")
   $parCount=ubound($farray)-1
consolewrite($fArray[1] & $parcount & @crlf)

   if $parCount = 0 then call($fArray[1])
   if $parCount = 1 then call($fArray[1],$fArray[2],$fArray[3])
   if $parCount = 2 then call($fArray[1],$fArray[2])
   if $parCount = 3 then call($fArray[1],$fArray[2],$fArray[3])
   if $parCount = 4 then call($fArray[1],$fArray[2],$fArray[3],$fArray[4])
Next

 

Posted (edited)

Hi,

Because your Example wouldn't allow Variable assignments, Loops, If-Statements, etc.

#include <AutoItGenFunc.au3>
$oFunction = _AutoItGenFunc_CreateFunction("", _  ;             <------ What are the Parameters?
        '' _ ;                                                  <------ What is the Sourcecode?
         & @LF & 'For $i = 1 to 100' _
         & @LF & '  IF $i < 50 then ' _
         & @LF & '    Sleep(100)' _
         & @LF & '  Else' _
         & @LF & '    Sleep(10) ; faster at the end.' _
         & @LF & '  EndIf' _
         & @LF & '' _
         & @LF & '  ToolTip("Counter:" & $i)' _
         & @LF & 'Next', _
        "") ;                                                   <------ What should be returned?

If IsObj($oFunction) Then
    $oFunction.Call()
EndIf

oh and the "why" is: I have a Complex application and users are supposed to create business objects and corresponding logic by themselfes supported by GUI easy AutoItObjects based Scripting. Logic is supposed to be entered and executed during runtime. This is my workaround until I can understand how to hook into IDispatch deep enough to dynamically load the coding directly into an AutoItObject-Object or something. This is nothing I intend to use on daily basis, but I still found it interesting enough to share, since most discussions about dynamic functions in Autoit end up with Execute(), Assign(), Eval() and it's limitations.

Cheers,

Edited by SEuBo
Posted

My point was more that you still need the AutoIt compiler to run the script thats outside on the filesystem.

The examples I gave are able to run without compiler and yes biggest missing things are loops, if statements.

But anyway #pragma compile(AutoItExecuteAllowed, true) would allow me to run any non compiled AutoIt file.

Your example looks fine but just trying to understand when to use it 

 

 

Posted (edited)

Hey!

As I said, it's a very very special usage scenario and I never had to use such a thing within the all my autoit life - up until now.

  On 2/11/2021 at 7:09 PM, junkew said:

My point was more that you still need the AutoIt compiler to run the script thats outside on the filesystem.

Expand  

https://www.autoitscript.com/autoit3/docs/intro/running.htm

Run a script using another compiled script:

Compiled.exe [/ErrorStdOut] /AutoIt3ExecuteScript file [params ...]
                Execute another AutoIt script file from a compiled AutoIt3 executable. 

Compiled.exe [/ErrorStdOut] /AutoIt3ExecuteLine "command line"
                Execute one line of code as with AutoIt3.exe above. 

This means that there is no need to have a copy of AutoIt3.exe in addition to the compiled file - the interpreter stub of the compiled file will replace it.  So as long as there is at least one compiled script available, other AutoIt scripts can be run without the need to have AutoIt3.exe on the machine., either pre-installed or added via FileInstall.

that is, if the script is compiled with #pragma compile(AutoItExecuteAllowed, true) as you stated, yes. but that's the default.

Edited by SEuBo
  • Moderators
Posted

SEuBo,

  Quote

if the script is compiled with #pragma compile(AutoItExecuteAllowed, true) as you stated, yes. but that's the default.

Expand  

No, the default is NOT to allow compiled scripts to run other scripts - from the Help file:

  Quote

However, the executable must have been compiled with the #pragma compile(AutoItExecuteAllowed, True) directive as the default setting does not permit use of the /AutoItExecuteScript or /AutoItExecuteLine parameters.

Expand  

M23

Public_Domain.png.2d871819fcb9957cf44f4514551a2935.png Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind

Open spoiler to see my UDFs:

  Reveal hidden contents

 

Posted (edited)
  On 2/11/2021 at 7:09 PM, junkew said:

Your example looks fine but just trying to understand when to use it 

Expand  

/Edit: I've updated the first post in regard to this.

The usage scenario is create & call a function which parameters and behaviour is unkown during compile time (or startup) and can also not be determined by algorithms, because it is supposed to be programmed while the application is running. I am developing an AutoIt based ERP system together with a small local business as a prototype/first customer help them reduce costs. The entire application, and all data / functions are encapsulated in Objects (- from here on I use the word "objects" equivalent to "classes". ). There are

- System objects (e.g. "Application", "Database", "ObjectBuilder", "Printer", "Mail", etc.) and
- Business objects (e.g. "Material", "Materialprice", "Customer", "Salesorder", "Material Text", "Contract", "Invoice", "ContractReleaseOrder", etc.).

The system objects are fixed and encapsulate everything that runs "in the background" and some very generic functionality. 

There will be a standard set of business objects and templates, but in general, business objects, their relations and their logics are to be created during runtime - by the user. This will be a heavily UI-guided process. Also, users are supposed to write Scripts "just-in-time", while there are looking at some data inside the application. Those Scripts shall have access to the whole Application: the screens, the data, the other objects, AutoIt Functions, the neat and the ugly bits. There is nothing worse than having a functionality somewhere that is doing exactly what you need, but you can't call it.

At the moment, the Business Objects are created manually (XML files with their definition and au3 files with method coding). After those files are added (and an #include-statement was added to a collective include), I have to restart my application. Otherwise I can not access the just created include.

And this is where my problem comes from: The Function sourcecode is entered by the user and it can only be called in the current application after restart (which is bad, when you already entered data on the screen, but also want a new script to run).

There are a lot of workarounds for stuff like this - exposing data & functions via API and defined interfaces, etc., - NOT letting users enter code and mess with stuff, - using languages that are better suited.. But I chose AutoIt because it is amazing and I want it to be the scripting language inside my AutoIt Application. 

As I said: very special use-case. Just for clarification :)

Edited by SEuBo

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
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...