Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

How to: Run Dynamic Script... Through a Pipe!


  • Please log in to reply
50 replies to this topic
Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
How to: Run Dynamic Script... Through a Pipe!
Dynamic scripting has various uses. Examples include:
[*:2mvn5eww]ACConsole is like a command prompt window where you can type or paste in commands to execute.
[*:2mvn5eww]expression and variable? - In this thread are examples of executing dynamic code/expressions by running a temporary script file. Throughout the thread are various methods of returning the result from the temporary script, back to the "master" script.
[*:2mvn5eww]Execute AHK code dynamically! dynamically executes any given command.If, for whatever reason, you want to avoid writing a temporary script file to disk, a named pipe can be used as a "file."

There were a couple of obstacles making it difficult to pipe a script to AutoHotkey; the comments in the script explain what they are and the solutions.

Type a line of code in the InputBox, and the script will "execute" it. (The InputBox is the only reason it is limited to one line.)
#NoEnv
ptr := A_PtrSize ? "Ptr" : "UInt"
char_size := A_IsUnicode ? 2 : 1

InputBox, Script, Script, Enter a line of script to execute.,,, 120,,,,, MsgBox :D

; To prevent "collision", pipe_name could be something mostly "unique", like:
;   pipe_name := A_TickCount
pipe_name := "testpipe"

; Before reading the file, AutoHotkey calls GetFileAttributes(). This causes
; the pipe to close, so we must create a second pipe for the actual file contents.
; Open them both before starting AutoHotkey, or the second attempt to open the
; "file" will be very likely to fail. The first created instance of the pipe
; seems to reliably be "opened" first. Otherwise, WriteFile would fail.
pipe_ga := CreateNamedPipe(pipe_name, 2)
pipe    := CreateNamedPipe(pipe_name, 2)
if (pipe=-1 or pipe_ga=-1) {
    MsgBox CreateNamedPipe failed.
    ExitApp
}

Run, %A_AhkPath% "\\.\pipe\%pipe_name%"

; Wait for AutoHotkey to connect to pipe_ga via GetFileAttributes().
DllCall("ConnectNamedPipe", ptr, pipe_ga, ptr, 0)
; This pipe is not needed, so close it now. (The pipe instance will not be fully
; destroyed until AutoHotkey also closes its handle.)
DllCall("CloseHandle", ptr, pipe_ga)
; Wait for AutoHotkey to connect to open the "file".
DllCall("ConnectNamedPipe", ptr, pipe, ptr, 0)

; Standard AHK needs a UTF-8 BOM to work via pipe.  If we're running on
; Unicode AHK_L, 'Script' contains a UTF-16 string so add that BOM instead:
Script := (A_IsUnicode ? chr(0xfeff) : chr(239) chr(187) chr(191)) . Script

char_size := (A_IsUnicode ? 2:1)
if !DllCall("WriteFile", ptr, pipe, "str", Script, "uint", (StrLen(Script)+1)*char_size, "uint*", 0, ptr, 0)
    MsgBox WriteFile failed: %ErrorLevel%/%A_LastError%

DllCall("CloseHandle", ptr, pipe)


CreateNamedPipe(Name, OpenMode=3, PipeMode=0, MaxInstances=255) {
    global ptr
    return DllCall("CreateNamedPipe","str","\\.\pipe" Name,"uint",OpenMode
        ,"uint",PipeMode,"uint",MaxInstances,"uint",0,"uint",0,"uint",0,ptr,0,ptr)
}
Covered by Lexikos' default copyright license.

The above script demonstrates an effective solution to one planned feature:

The ability to run AutoHotkey.exe and pass a single command (or perhaps group of commands) to execute.


Piping in General

While a pipe can act like a file, there are some major limitations: pipes do not support seeking or GetFileSize(). FileRead uses GetFileSize() internally, so cannot be used with pipes. Many other applications will attempt to read the "file" (pipe), but will fail. (Notepad and Wordpad included.)

You may have more luck with other applications. I have seen a few scripts that use temporary .vbs files, but I have yet to try running one through a pipe.

Interestingly, Loop, Read and FileReadLine do work with pipes.

Update 2010-07-23: Added support for Unicode and 64-bit builds of AutoHotkey.
Update 2010-08-01: Fixed some weird typo. char_size := (...) had become Script_size := *(...).


rickly
  • Guests
  • Last active:
  • Joined: --
This will have many useful functions, but I noticed that you have
implemented it using 'named pipes' which are not available on win9x
systems. Is there any chance that it can be done using 'plain' pipes (as
used in corrupt's cmdret_ahk functions). I tried a few stabs at modifying
your code to use 'createpipe', and about the only success I had was that
I did not CRASH the system.

This stuff is way over my head, but if you can modify it to work on win9x
systems, it would be greatly appreciated.

majkinetor
  • Moderators
  • 4512 posts
  • Last active: May 20 2019 07:41 AM
  • Joined: 24 May 2006
Thx lexikos.

This is great
Posted Image

Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
Cool! Thanks, Lexikos, for sharing it!

Here is a three-line GUI for your pipes, to help experimenting. Alt-Win-Z pops up the GUI. I included it in my master script, for quick script testing or calculations.
Gui Add, Edit, r5 w160 vScript, MsgBox `% %A_Space%
Gui Add, Button, w70, &Run
Gui Add, Button, w70 x+20 yp, &Cancel

#!z::Gui Show

ButtonRun:
   Gui Submit

   Script = %Script% ; AHK needs leading UTF-8 BOM "", otherwise rewinds, breaking the pipe

   pipe_name := A_Now   ; unique to prevent collision
   pip2 := CreateNamedPipe(pipe_name, 2) ; AHK calls GetFileAttributes() = close pipe. Create 2nd pipe
   pipe := CreateNamedPipe(pipe_name, 2) ; 1st instance seems to reliably be "opened" first
   if (pipe=-1 or pip2=-1) {
       MsgBox CreateNamedPipe failed.
       ExitApp
   }

   Run %A_AhkPath% "\\.\pipe\%pipe_name%"

   DllCall("ConnectNamedPipe","uint",pip2,"uint",0) ; Wait for AHK to connect via GetFileAttributes()
   DllCall("CloseHandle","uint",pip2)               ; Not needed any more
   DllCall("ConnectNamedPipe","uint",pipe,"uint",0) ; Wait for AHK to connect to open the "file"

   If !DllCall("WriteFile","uint",pipe,"str",Script,"uint",StrLen(Script)+1,"uint*",0,"uint",0)
       MsgBox WriteFile failed: %ErrorLevel%/%A_LastError%

   DllCall("CloseHandle","uint",pipe)
Return

ButtonCancel:
   Gui Destroy
Return

CreateNamedPipe(Name, OpenMode=3, PipeMode=0, MaxInstances=255) {
    Return DllCall("CreateNamedPipe","str","\\.\pipe\" Name,"uint",OpenMode
        ,"uint",PipeMode,"uint",MaxInstances,"uint",0,"uint",0,"uint",0,"uint",0)
}


Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006

Is there any chance that it can be done using 'plain' pipes

Not without modifications to AutoHotkey. Named pipes work because AutoHotkey opens it as if it were any other file.

One of the ideas I had: a custom build of AutoHotkeySC (for compiled scripts) that accepts script content directly via a pipe. This way compiled scripts would be able to:
[*:3tl07qed]Execute "dynamic" script.
[*:3tl07qed]Run embedded child scripts. For instance, to simulate multi-threading for specific tasks without loading any unnecessary code into the child script.(All without packaging AutoHotkey.exe with the script.)

I have no immediate use for it, so probably won't be the one to work on it.

Here is a three-line GUI for your pipes, to help experimenting.

I like how concise you made the comments. 8)
I use Titan's ACConsole + a RAM disk. Since I frequently :roll: close it by accident, I like to keep the temporary file as a backup.

SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005
Many thanks lexikos

Interestingly, Loop, Read and FileReadLine do work with pipes.


Would IniRead be posssible ?

It takes my script around 7000ms to load 1000 entries from a PLS file with IniRead.. I was wondering if I should opt for string parsing .

?

@Laszlo: thanks for the GUI version, Sir.

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
Like many other applications, IniRead closes the pipe before the script writes to it. I guess it uses GetFileSize(), and thinks the pipe is "empty." (A_LastError is ERROR_NO_DATA=232 after WriteFile; meaning "The pipe is being closed.")

String parsing would likely be more efficient (given that you only need to open and traverse the file once), but I don't see what it has to do with pipes...

SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005

I don't see what it has to do with pipes...


Forgive my ignorance.. I thought a pipe was some kind storage media.
I just remembered that in old DOS days I used
Echo Y | Del *.* in batch files.

:)

derRaphael
  • Members
  • 872 posts
  • Last active: Mar 19 2013 04:42 PM
  • Joined: 23 Nov 2007
this is awesome

now with this, you can nearly truly embed virtually any ahk script (exept those, who write themselves :))

This is a pretty cool way to create Dynamic abbrevations without using any files to rewrite and reopen and re.. blah

Sending it to a salten md5hashnamedpipe and executed 'em there
it works :)

Thank you very much
DerRaphael

TheAHKpad - written in AHK for AHK :)
http://www.autohotke...p=161815#161815

derRaphael
  • Members
  • 872 posts
  • Last active: Mar 19 2013 04:42 PM
  • Joined: 23 Nov 2007
By The way ...

would it be possible using a pipe to load an icon from previously converted to hex with the all-mighty read/writebin funktion?

and if so, how can this made usable?

would be nice when this'd work with Menu, Tray, Icon \path\to\pipe

theoretically:
1st load into skript via bin read,
2nd save in skript as viable
3rd write back from var to pipe
4th *do some magic whoop-a-whoop*
5th load the icon via menu, tray, icon OR DllCall(LoadImage,pipe)

step 5 - both possibilities 'd be nice

Gui, Add, Picture would be nice to

this 'd enable true icon embedding for ahk scripts

:D

DerRaphael

postScriptum: I tried rather stupid modifying the initial postet script, but it endet up in showing the standard icon a loooooooooooooooooong time and doin' nothin' (seemed to hang somewhere)



; .. .. /snip

pipe_name := "testpipe"


pipe_ga := CreateNamedPipe(pipe_name, 2)
pipe    := CreateNamedPipe(pipe_name, 2)
if (pipe=-1 or pipe_ga=-1) {
    MsgBox CreateNamedPipe failed.
    ExitApp
}

;Menu, Tray, Icon, "\\.\pipe\%pipe_name%"
	hIcon := DllCall("LoadImage", uint, 0 
	 , str, "\\.\pipe\%pipe_name%"
	 , uint, 1, int, 0, int, 0, uint, 0x10)
DllCall("ConnectNamedPipe","uint",pipe_ga,"uint",0)

res := BinRead(pipe,data)
;if !DllCall("WriteFDllile","uint",pipe,"str",Icon,"uint",StrLen(Icon)+1,"uint*",0,"uint",0)
;    MsgBox WriteFile failed: %ErrorLevel%/%A_LastError%

;DllCall("ConnectNamedPipe","uint",pipe_ga,"uint",0)

DllCall("CloseHandle","uint",pipe_ga)

;DllCall("ConnectNamedPipe","uint",pipe,"uint",0)

;Script = %Script%

;if !DllCall("WriteFile","uint",pipe,"str",Icon,"uint",StrLen(Icon)+1,"uint*",0,"uint",0)
;    MsgBox WriteFile failed: %ErrorLevel%/%A_LastError%

DllCall("CloseHandle","uint",pipe)
	SendMessage, 0x80, 1, hIcon

Gui, Add, Button, gGuiEscape,RUN!
Gui, Show

return

CreateNamedPipe(Name, OpenMode=3, PipeMode=0, MaxInstances=255) {
    return DllCall("CreateNamedPipe","str","\\.\pipe\" Name,"uint",OpenMode
        ,"uint",PipeMode,"uint",MaxInstances,"uint",0,"uint",0,"uint",0,"uint",0)
}

GuiClose:
GuiEscape:
Escape::
ExitApp

;.. .. /snip

; There go some more Lines here merily copied from 
; http://www.autohotkey.com/forum/topic4546.html (1st code example)
; and my DataThingy (done with read bin, readFile, String2MessageBx, 
; copyMsg, strip out whatever unneeded, paste in continuation section  
; The Gosub, Data Call sets a new value for dataVariable


Pretty clueless right now. Dont know if i messed up windows calls, or my data structure, or the "loadImage" call, or some of it, or all

Thank you
DerRaphael

polyethene
  • Members
  • 5519 posts
  • Last active: May 17 2015 06:39 AM
  • Joined: 26 Oct 2012
Haven't tried it out but it looks great! Thanks for the information and examples.

autohotkey.com/net Site Manager

 

Contact me by email (polyethene at autohotkey.net) or message tidbit


Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
Loading a file through a pipe in the same script is generally difficult, if possible, since you have to read and write at the same time. It might be possible to start an asynchronous (aka overlapped) write with the win32 WriteFile function. Then again, it might fail since the pipe won't have opened yet.

Regardless, Menu,Tray,Icon (and the GUI commands I've tried) won't work with pipes. (I tried piping from a separate script.)

Menu & Gui use LoadImage(), so that probably won't work either.

derRaphael
  • Members
  • 872 posts
  • Last active: Mar 19 2013 04:42 PM
  • Joined: 23 Nov 2007
regardless of pipe and here offtopic, is there any other way, when i have the binary image data assigned as hex in a var to put that into memory and make loadImage believe it came from file?

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006

and make loadImage believe it came from file?

Specifically, no, at least not without a RAM disk.

More generally, you'd probably be interested in:
Include bitmaps in your scripts!

khan
  • Guests
  • Last active:
  • Joined: --
I can't run a posted script because script have unicode characters.
so, I changed a little.
Script = %Script% 
to
Script:=chr(0xef) . chr(0xbb) . chr(0xbf) . Script
Anyway, Thanks for sharing it. :)