 |
AutoHotkey Community Let's help each other out
|
| View previous topic :: View next topic |
| Author |
Message |
Lexikos
Joined: 17 Oct 2006 Posts: 7299 Location: Australia
|
Posted: Thu Nov 22, 2007 11:54 am Post subject: How to: Run Dynamic Script... Through a Pipe! |
|
|
How to: Run Dynamic Script... Through a Pipe!
Dynamic scripting has various uses. Examples include:
- ACConsole is like a command prompt window where you can type or paste in commands to execute.
- 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.
- 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.)
| Code: | #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:
| Planned Features wrote: | | 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 := *(...).
Last edited by Lexikos on Sun Aug 01, 2010 3:12 am; edited 4 times in total |
|
| Back to top |
|
 |
rickly Guest
|
Posted: Thu Nov 22, 2007 3:19 pm Post subject: |
|
|
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. |
|
| Back to top |
|
 |
majkinetor
Joined: 24 May 2006 Posts: 4511 Location: Belgrade
|
Posted: Thu Nov 22, 2007 3:43 pm Post subject: |
|
|
Thx lexikos.
This is great _________________
 |
|
| Back to top |
|
 |
Laszlo
Joined: 14 Feb 2005 Posts: 4710 Location: Boulder, CO
|
Posted: Thu Nov 22, 2007 6:38 pm Post subject: |
|
|
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. | Code: | 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)
} |
|
|
| Back to top |
|
 |
Lexikos
Joined: 17 Oct 2006 Posts: 7299 Location: Australia
|
Posted: Fri Nov 23, 2007 1:11 am Post subject: |
|
|
| rickly wrote: | | 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:
- Execute "dynamic" script.
- 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.
| Laszlo wrote: | | Here is a three-line GUI for your pipes, to help experimenting. | I like how concise you made the comments.
I use Titan's ACConsole + a RAM disk. Since I frequently close it by accident, I like to keep the temporary file as a backup. |
|
| Back to top |
|
 |
SKAN
Joined: 26 Dec 2005 Posts: 8688
|
Posted: Fri Nov 23, 2007 8:25 pm Post subject: Re: How to: Run Dynamic Script... Through a Pipe! |
|
|
Many thanks lexikos
| lexikos wrote: | | 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. |
|
| Back to top |
|
 |
Lexikos
Joined: 17 Oct 2006 Posts: 7299 Location: Australia
|
Posted: Sat Nov 24, 2007 12:37 am Post subject: |
|
|
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... |
|
| Back to top |
|
 |
SKAN
Joined: 26 Dec 2005 Posts: 8688
|
Posted: Sat Nov 24, 2007 1:46 am Post subject: |
|
|
| lexikos wrote: | | 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.
 |
|
| Back to top |
|
 |
derRaphael
Joined: 23 Nov 2007 Posts: 841 Location: ~/.
|
Posted: Mon Nov 26, 2007 8:27 pm Post subject: |
|
|
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.autohotkey.com/forum/viewtopic.php?p=161815#161815 |
|
| Back to top |
|
 |
derRaphael
Joined: 23 Nov 2007 Posts: 841 Location: ~/.
|
Posted: Mon Nov 26, 2007 11:16 pm Post subject: |
|
|
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
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)
| Code: |
; .. .. /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 |
|
| Back to top |
|
 |
polyethene
Joined: 11 Aug 2004 Posts: 5248 Location: UK
|
Posted: Mon Nov 26, 2007 11:20 pm Post subject: |
|
|
Haven't tried it out but it looks great! Thanks for the information and examples. _________________ GitHub • Scripts • IronAHK • Contact by email not private message. |
|
| Back to top |
|
 |
Lexikos
Joined: 17 Oct 2006 Posts: 7299 Location: Australia
|
Posted: Tue Nov 27, 2007 12:17 am Post subject: |
|
|
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. |
|
| Back to top |
|
 |
derRaphael
Joined: 23 Nov 2007 Posts: 841 Location: ~/.
|
Posted: Tue Nov 27, 2007 12:53 am Post subject: |
|
|
| 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? |
|
| Back to top |
|
 |
Lexikos
Joined: 17 Oct 2006 Posts: 7299 Location: Australia
|
Posted: Tue Nov 27, 2007 5:29 am Post subject: |
|
|
| Quote: | | 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! |
|
| Back to top |
|
 |
khan Guest
|
Posted: Tue Nov 27, 2007 7:09 am Post subject: |
|
|
I can't run a posted script because script have unicode characters.
so, I changed a little.
| Code: |
Script = %Script%
|
to
| Code: |
Script:=chr(0xef) . chr(0xbb) . chr(0xbf) . Script
|
Anyway, Thanks for sharing it.  |
|
| Back to top |
|
 |
|
|
You can post new topics in this forum You can reply to topics in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|