Problem with Dynamic Script Function

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
1100++
Posts: 78
Joined: 10 Feb 2018, 19:05

Problem with Dynamic Script Function

13 Jun 2019, 15:29

I'm trying to write a (fast) function that will run AutoHotkey scripts dynamically from memory.

Code: Select all

RunDynamicScript(script, scriptname := "", WorkingDir := "") {
	static scriptdir := A_MyDocuments "\AutoHotkey\Dynamic Scripts", defaults := "#NoEnv`n#SingleInstance Off`nSetBatchLines -1`nSendMode Input`n`n"
		. "Menu Tray, Tip, % ""AutoHotkey Dynamic Script (PID: "" DllCall(""GetCurrentProcessId"", ""UInt"") "")""`n`n", pid := DllCall("GetCurrentProcessId", "UInt"), count := 0
	If !FileExist(scriptdir) {
		FileCreateDir %scriptdir%
		If (ErrorLevel = true)
			return -4
	} scriptno := ++count
	If (scriptname = "")
		scriptname := "AutoHotkey Dynamic Script"
	else if scriptname contains \,/,:,*,?,",<,>,|	; "
		scriptname := "AutoHotkey Dynamic Script"
	DllCall("QueryPerformanceCounter", "Int64*", timestamp), scriptpath := scriptdir "\" scriptname " #" scriptno " " pid " " Format("{:x}", timestamp) ".ahk"
	If (scriptfile := DllCall("CreateFile", "Str", scriptpath, "UInt", 0xC0000000, "UInt", 1, "Ptr", 0, "UInt", 2, "UInt", 0x4000100, "Ptr", 0, "Ptr")) = -1
		return -1
	If FileOpen(scriptfile, "h", "UTF-8").Write(defaults script) = 0
		return -2
	Run "%A_AhkPath%" /CP65001 "%scriptpath%", %WorkingDir%, UseErrorLevel, scriptpid
	return ErrorLevel = 0 ? scriptpid : -3	;, DllCall("CloseHandle", "Ptr", scriptfile)
}
However, when I try using it, I get an error like this one:

---------------------------
AutoHotkey Dynamic Script #1 3932 7aa2ab4c13.ahk
---------------------------
Script file not found:
C:\Users\Main\Documents\AutoHotkey\Dynamic Scripts\AutoHotkey Dynamic Script #1 3932 7aa2ab4c13.ahk
---------------------------
OK
---------------------------



If I add a few miscellaneous lines of code in, I get something like this:

---------------------------
AutoHotkey Dynamic Script #1 7836 7a85764b5c.ahk
---------------------------
Error at line 0.

Script file "C:\Users\Main\Documents\AutoHotkey\Dynamic Scripts\AutoHotkey Dynamic Script #1 7836 7a85764b5c.ahk" cannot be opened.

The program will exit.
---------------------------
OK
---------------------------



Does anyone know what the problem is?
toralf
Posts: 868
Joined: 27 Apr 2014, 21:08
Location: Germany

Re: Problem with Dynamic Script Function

13 Jun 2019, 15:52

Just guessing: Have you tried to close the file before you run it?
ciao
toralf
1100++
Posts: 78
Joined: 10 Feb 2018, 19:05

Re: Problem with Dynamic Script Function

13 Jun 2019, 16:08

On my first attempt, I closed the file handle with CloseHandle(). I commented it out to see what effect that would have. It's still present in the code I posted.
toralf
Posts: 868
Joined: 27 Apr 2014, 21:08
Location: Germany

Re: Problem with Dynamic Script Function

14 Jun 2019, 01:05

I see it in your code. But it is after the run command and I meant file.close() since there might be more things happen when a file object gets closed vs a handle gets closed.
ciao
toralf
toralf
Posts: 868
Joined: 27 Apr 2014, 21:08
Location: Germany

Re: Problem with Dynamic Script Function

14 Jun 2019, 01:22

Furthermore expressions are resolved from left to right when concatenated with a comma. Hence, the closehandle() would never be executed.
ciao
toralf
1100++
Posts: 78
Joined: 10 Feb 2018, 19:05

Re: Problem with Dynamic Script Function

14 Jun 2019, 08:35

File.Close() wouldn't do anything.

From the documentation for FileOpen():
Flag h

Indicates that Filename is a file handle to wrap in an object. Sharing mode flags are ignored and the file or stream represented by the handle is not checked for a byte order mark. The file handle is not closed automatically when the file object is destroyed and calling Close has no effect. Note that Seek, Tell and Length should not be used if Filename is a handle to a nonseeking device such as a pipe or a communications device.
As for putting multi-statments in return statements, I do it all the time and it works perfectly fine.
just me
Posts: 9424
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Problem with Dynamic Script Function

14 Jun 2019, 10:13

I didn't test it with the Run command, but seemingly File.Close() has some effect. Try

Code: Select all

...
   DllCall("QueryPerformanceCounter", "Int64*", timestamp), scriptpath := scriptdir "\" scriptname " #" scriptno " " pid " " Format("{:x}", timestamp) ".ahk"
   If (scriptfile := DllCall("CreateFile", "Str", scriptpath, "UInt", 0xC0000000, "UInt", 1, "Ptr", 0, "UInt", 2, "UInt", 0x4000100, "Ptr", 0, "Ptr")) = -1
      Return -1
   If !(AhkFile := FileOpen(scriptfile, "h", "UTF-8")) {
      DllCall("CloseHandle", "Ptr", scriptfile)
      Return -2
   }
   BytesWritten := AhkFile.Write(defaults script)
   AhkFile.Close()
   If (BytesWritten = 0) {
      DllCall("CloseHandle", "Ptr", scriptfile)
      Return -2
   }
...
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Problem with Dynamic Script Function

14 Jun 2019, 13:46

@1100++: Re. multi-statements, just FYI re. forwards compatibility:

Code: Select all

var1 := (1, 2)
var2 := MyFunc()
;MsgBox, % var1 " " var2 ;1 1 ;AHK v1
MsgBox(var1 " " var2) ;2 2 ;AHK v2

MyFunc()
{
	return (1, 2)
}
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
1100++
Posts: 78
Joined: 10 Feb 2018, 19:05

Re: Problem with Dynamic Script Function

18 Jun 2019, 17:09

Was anyone able to get my function to work?
just me
Posts: 9424
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Problem with Dynamic Script Function

19 Jun 2019, 04:44

Did you try what I suggested?

BTW: Your file is created with the attributes/flags
  • FILE_ATTRIBUTE_TEMPORARY 256 (0x100)
    The file is being used for temporary storage.
  • FILE_FLAG_DELETE_ON_CLOSE 0x04000000
    The file is to be deleted immediately after all of its handles are closed, which includes the specified handle and any other open or duplicated handles.
    If there are existing open handles to a file, the call fails unless they were all opened with the FILE_SHARE_DELETE share mode.
    Subsequent open requests for the file fail, unless the FILE_SHARE_DELETE share mode is specified.
So you might need to change the share mode to 7:

Code: Select all

   If (scriptfile := DllCall("CreateFile", "Str", scriptpath, "UInt", 0xC0000000, "UInt", 7, ...
1100++
Posts: 78
Joined: 10 Feb 2018, 19:05

Re: Problem with Dynamic Script Function

19 Jun 2019, 08:01

Changing the share mode to 7 didn't work.

I changed the code so that I only get Error at line 0. errors.

Here it is.

Code: Select all

RunDynamicScript(script, scriptname := "", WorkingDir := "") {
	static scriptdir := A_MyDocuments "\AutoHotkey\Dynamic Scripts", defaults := "#NoEnv`nSetBatchLines -1`nSendMode Input`n`n"
		. "Menu Tray, Tip, % ""Dynamic AutoHotkey Script (PID: "" DllCall(""GetCurrentProcessId"", ""UInt"") "")""`n`n", pid := DllCall("GetCurrentProcessId", "UInt"), count := 0
	If !FileExist(scriptdir) {
		FileCreateDir %scriptdir%
		If (ErrorLevel = true)
			return -6
	} scriptno := ++count
	If (scriptname = "")
		scriptname := "AutoHotkey Dynamic Script"
	else if scriptname contains \,/,:,*,?,",<,>,|	; "
		scriptname := "AutoHotkey Dynamic Script"
	DllCall("QueryPerformanceCounter", "Int64*", timestamp), scriptpath := scriptdir "\" scriptname " #" scriptno " " pid " " Format("{:x}", timestamp) ".ahk"
	If (scriptfile := DllCall("CreateFile", "Str", scriptpath, "UInt", 0xC0000000, "UInt", 7, "Ptr", 0, "UInt", 2, "UInt", 0x4000100, "Ptr", 0, "Ptr")) = -1
		return -1
	If FileOpen(scriptfile, "h", "UTF-8").Write(defaults script) = 0
		return -2
	Run "%A_AhkPath%" /force /CP65001 "%scriptpath%", %WorkingDir%, UseErrorLevel, scriptpid
	If ErrorLevel
		return -3
	If not scriptprocess := DllCall("OpenProcess", "UInt", 0x40, "Int", false, "UInt", scriptpid, "Ptr")
		return -4
	return DllCall("DuplicateHandle", "Ptr", -1, "Ptr", scriptfile, "Ptr", scriptprocess, "Ptr*", _scriptfile, "UInt", 0x80000000, "Int", false, "UInt", 1) ? scriptpid : -5
		, DllCall("CloseHandle", "Ptr", scriptprocess)
}
just me
Posts: 9424
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Problem with Dynamic Script Function

19 Jun 2019, 10:44

You did not try what I suggested. After further testing I get the error

Code: Select all

---------------------------
RunnerTest2.ahk
---------------------------
Error at line 0.

Script file "D:\AutoHotkey\AHK_L\Test\RunnerTest2.ahk" cannot be opened.

The program will exit.
---------------------------
OK   
---------------------------
if I intentionally lock the AHK script file.
toralf
Posts: 868
Joined: 27 Apr 2014, 21:08
Location: Germany

Re: Problem with Dynamic Script Function

19 Jun 2019, 16:22

I played around with your code, but didn't succeed. It works, but not as a temporary file in memory and delete on close.
The delete on close causes the script not to be executed. With the temporary file flag the file is still created.
I believe that you could also use FileAppend and FileDelete instead.

Code: Select all

Script =
(%
Loop, 5
{
  Sleep 800
  OutputDebug OutputDebug %A_Index%``n
  FileAppend, StdOut %A_Index%``n, *
  FileAppend, StdErr %A_Index%``n, **
}
)

RunDynamicScript(Script)
ExitApp

RunDynamicScript(script, scriptname := "", WorkingDir := "") {
	static scriptdir := A_ScriptDir
       , defaults := "#NoEnv`n"
                    . "SetBatchLines -1`n"
                    . "SendMode Input`n`n"
		                . "Menu Tray, Tip, % ""Dynamic AutoHotkey Script (PID: "" DllCall(""GetCurrentProcessId"", ""UInt"") "")""`n`n"
       , pid := DllCall("GetCurrentProcessId", "UInt")
       , count := 0
  scriptno := ++count
	If (scriptname = "")
		scriptname := "AutoHotkey Dynamic Script"
	else if scriptname contains \,/,:,*,?,",<,>,|	; "
		scriptname := "AutoHotkey Dynamic Script"
	DllCall("QueryPerformanceCounter", "Int64*", timestamp)
  , scriptpath := scriptdir "\" scriptname " #" scriptno " " pid " " Format("{:x}", timestamp) ".ahk"
  , StdOutpath := scriptdir "\" scriptname " #" scriptno " " pid " " Format("{:x}", timestamp) "_StdOut.log"
  
  ;                                                                              
	If (scriptfile := DllCall("CreateFile"
                 , "Str", scriptpath    ; lpFileName
                 , "UInt", 0xC0000000   ; dwDesiredAccess
                 , "UInt", 7            ; dwShareMode: 7 = Delete 4 + Read 1  * Write 2
                 , "Ptr", 0             ; lpSecurityAttributes
                 , "UInt", 2            ; dwCreationDisposition: 2 = CREATE_ALWAYS
                 , "UInt", 0x100        ; dwFlagsAndAttributes: 0x100 = Temporary (works, but has no effect) 0x04000000 = Delete on Close (doesn't work)
                 , "Ptr", 0             ; hTemplateFile
                 , "Ptr")) = -1
		return -1
	If FileOpen(scriptfile, "h", "UTF-8").Write(defaults script) = 0
		return -2
        
	Run %A_ComSpec% /c "%A_AHKPath% /ErrorStdOut "%scriptpath%" > "%StdOutpath%" 2>&1", %WorkingDir%, Hide UseErrorLevel, scriptpid
	If ErrorLevel
		return -3

  Loop,
  {
    FileRead, OutputVar, %StdOutpath%
    ToolTip During Process`n%scriptpid%`n%OutputVar%
    Process, Exist, %scriptpid%
    If not ErrorLevel
      Break
    Sleep 500
  }
  FileRead, OutputVar, %StdOutpath%
  ToolTip Process Stopped`n%OutputVar%
  Sleep 2000
  FileDelete, %StdOutpath%
    
    
	
  ; If not scriptprocess := DllCall("OpenProcess", "UInt", 0x40, "Int", false, "UInt", scriptpid, "Ptr")
		; return -4
	
  ; return DllCall("DuplicateHandle", "Ptr", -1, "Ptr", scriptfile, "Ptr", scriptprocess, "Ptr*", _scriptfile, "UInt", 0x80000000, "Int", false, "UInt", 1) ? scriptpid : -5
		; , DllCall("CloseHandle", "Ptr", scriptprocess)
}
ciao
toralf
teadrinker
Posts: 4309
Joined: 29 Mar 2015, 09:41
Contact:

Re: Problem with Dynamic Script Function

19 Jun 2019, 17:36

For me it works like this:

Code: Select all

tmpFilePath := A_Desktop . "\temp.ahk"
FileAppend, MsgBox Hi from the dynamic script!, % tmpFilePath

Run, % tmpFilePath,,, PID
DetectHiddenWindows, On
WinWait, ahk_pid %PID%

hFile := OpenTmpFile(tmpFilePath)
Process, WaitClose, % PID
DllCall("CloseHandle", Ptr, hFile)

OpenTmpFile(FilePath)  {
   static GENERIC_READ := 0x80000000, FILE_SHARE_READ := 1, FILE_SHARE_DELETE := 4
        , OPEN_EXISTING := 3, FILE_FLAG_DELETE_ON_CLOSE := 0x4000000
        , FILE_ATTRIBUTE_TEMPORARY := 0x100
   
   Return DllCall("CreateFile", Str, FilePath
                              , Int, GENERIC_READ
                              , Int, FILE_SHARE_READ|FILE_SHARE_DELETE
                              , Ptr, 0
                              , Int, OPEN_EXISTING
                              , Int, FILE_FLAG_DELETE_ON_CLOSE|FILE_ATTRIBUTE_TEMPORARY
                              , Ptr, 0, Ptr)
}
toralf
Posts: 868
Joined: 27 Apr 2014, 21:08
Location: Germany

Re: Problem with Dynamic Script Function

19 Jun 2019, 18:13

Hi teadrinker
What is the benefit over a simple Filedelete?
ciao
toralf
teadrinker
Posts: 4309
Joined: 29 Mar 2015, 09:41
Contact:

Re: Problem with Dynamic Script Function

19 Jun 2019, 18:39

Hi
I don't know, I did it just for fun. Actually, to run a dynamic script, the file is not needed at all.
just me
Posts: 9424
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Problem with Dynamic Script Function

20 Jun 2019, 05:16

teadrinker wrote:
19 Jun 2019, 18:39
Actually, to run a dynamic script, the file is not needed at all.
:facepalm: Of course :arrow: ExecScript()
teadrinker
Posts: 4309
Joined: 29 Mar 2015, 09:41
Contact:

Re: Problem with Dynamic Script Function

20 Jun 2019, 07:02

Yes, but since WScript.Shell does not support unicode symbols (try ExecScript("MsgBox ✓")), I prefer DynaRun():

Code: Select all

DynaRun(tempScript, ahkPath := "", pipeName := "")
{
   static params := [ "UInt", PIPE_ACCESS_OUTBOUND     := 0x2, "UInt", 0
                    , "UInt", PIPE_UNLIMITED_INSTANCES := 255, "UInt", 0
                    , "UInt", 0, "Ptr", 0, "Ptr", 0, "Ptr" ]
        , BOM := Chr(0xFEFF)

   (ahkPath = "" && ahkPath := A_AhkPath)
   (pipeName = "" && pipeName := "AHK_" . A_TickCount)
   
   Loop 1 {
      for k, v in ["pipeGA", "pipe"]
         %v% := DllCall("CreateNamedPipe", Str, "\\.\pipe\" pipeName, params*)
      if ( (pipe = -1 || pipeGA = -1) && error := "Can't create pipe """ . pipeName . """`nLastError: " . A_LastError )
         break
      Run, % ahkPath . " ""\\.\pipe\" . pipeName . """",, UseErrorLevel HIDE, PID
      if (ErrorLevel && error := "Can't open file:`n ""\\.\pipe\" . pipeName . """")
         break
      for k, v in ["pipeGA", "pipe"]
         DllCall("ConnectNamedPipe", Ptr, %v%, Ptr, 0)
      tempScript := BOM . tempScript
      tempScriptSize := ( StrLen(tempScript) + 1 ) << !!A_IsUnicode
      if !DllCall("WriteFile", Ptr, pipe, Str, tempScript, UInt, tempScriptSize, UIntP, 0, Ptr, 0)
         error := "WriteFile failed, LastError: " . A_LastError
   }
   for k, v in ["pipeGA", "pipe"]
      ( %v% != -1 && DllCall("CloseHandle", Ptr, %v%) )
   if error
      throw Exception(error)
   Return PID
}

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: Google [Bot], imstupidpleshelp, jaka1, OrangeCat and 158 guests