AutoHotkey Homepage AutoHotkey Community
Let's help each other out
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

How to: Run Dynamic Script... Through a Pipe!
Goto page 1, 2, 3  Next
 
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions
View previous topic :: View next topic  
Author Message
Lexikos



Joined: 17 Oct 2006
Posts: 2472
Location: Australia, Qld

PostPosted: Thu Nov 22, 2007 12:54 pm    Post subject: How to: Run Dynamic Script... Through a Pipe! Reply with quote

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

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","uint",pipe_ga,"uint",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","uint",pipe_ga)
; Wait for AutoHotkey to connect to open the "file".
DllCall("ConnectNamedPipe","uint",pipe,"uint",0)

; AutoHotkey reads the first 3 bytes to check for the UTF-8 BOM "". If it is
; NOT present, AutoHotkey then attempts to "rewind", thus breaking the pipe.
Script := chr(239) chr(187) chr(191) Script

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)


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)
}

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.


Last edited by Lexikos on Tue Nov 27, 2007 10:28 am; edited 1 time in total
Back to top
View user's profile Send private message
rickly
Guest





PostPosted: Thu Nov 22, 2007 4:19 pm    Post subject: Reply with quote

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: 3592
Location: Belgrade

PostPosted: Thu Nov 22, 2007 4:43 pm    Post subject: Reply with quote

Thx lexikos.

This is great
_________________
Back to top
View user's profile Send private message MSN Messenger
Laszlo



Joined: 14 Feb 2005
Posts: 3942
Location: Pittsburgh

PostPosted: Thu Nov 22, 2007 7:38 pm    Post subject: Reply with quote

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
View user's profile Send private message
Lexikos



Joined: 17 Oct 2006
Posts: 2472
Location: Australia, Qld

PostPosted: Fri Nov 23, 2007 2:11 am    Post subject: Reply with quote

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. Cool
I use Titan's ACConsole + a RAM disk. Since I frequently Rolling Eyes close it by accident, I like to keep the temporary file as a backup.
Back to top
View user's profile Send private message
SKAN



Joined: 26 Dec 2005
Posts: 5574

PostPosted: Fri Nov 23, 2007 9:25 pm    Post subject: Re: How to: Run Dynamic Script... Through a Pipe! Reply with quote

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
View user's profile Send private message
Lexikos



Joined: 17 Oct 2006
Posts: 2472
Location: Australia, Qld

PostPosted: Sat Nov 24, 2007 1:37 am    Post subject: Reply with quote

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
View user's profile Send private message
SKAN



Joined: 26 Dec 2005
Posts: 5574

PostPosted: Sat Nov 24, 2007 2:46 am    Post subject: Reply with quote

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.

Smile
Back to top
View user's profile Send private message
DerRaphael



Joined: 23 Nov 2007
Posts: 379
Location: Heidelberg, Germany

PostPosted: Mon Nov 26, 2007 9:27 pm    Post subject: Reply with quote

this is awesome

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

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 Smile

Thank you very much
DerRaphael

TheAHKpad - written in AHK for AHK Smile
http://www.autohotkey.com/forum/viewtopic.php?p=161815#161815
Back to top
View user's profile Send private message
DerRaphael



Joined: 23 Nov 2007
Posts: 379
Location: Heidelberg, Germany

PostPosted: Tue Nov 27, 2007 12:16 am    Post subject: Reply with quote

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

Very Happy

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
View user's profile Send private message
Titan



Joined: 11 Aug 2004
Posts: 5009
Location: imaginationland

PostPosted: Tue Nov 27, 2007 12:20 am    Post subject: Reply with quote

Haven't tried it out but it looks great! Thanks for the information and examples.
_________________

RegExReplace("irc.freenode.net/autohotkey", "^(?=(.(?=[\0-r\[]*((?<=\.).))))(?:[c-\x73]{2,8}(\S))+((2)|\b[^\2-]){2}\D++$", "$u3$1$3$4$2")
Back to top
View user's profile Send private message Visit poster's website
Lexikos



Joined: 17 Oct 2006
Posts: 2472
Location: Australia, Qld

PostPosted: Tue Nov 27, 2007 1:17 am    Post subject: Reply with quote

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
View user's profile Send private message
DerRaphael



Joined: 23 Nov 2007
Posts: 379
Location: Heidelberg, Germany

PostPosted: Tue Nov 27, 2007 1:53 am    Post subject: Reply with quote

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
View user's profile Send private message
Lexikos



Joined: 17 Oct 2006
Posts: 2472
Location: Australia, Qld

PostPosted: Tue Nov 27, 2007 6:29 am    Post subject: Reply with quote

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
View user's profile Send private message
khan
Guest





PostPosted: Tue Nov 27, 2007 8:09 am    Post subject: Reply with quote

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. Smile
Back to top
Display posts from previous:   
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions All times are GMT
Goto page 1, 2, 3  Next
Page 1 of 3

 
Jump to:  
You can post new topics in this forum
You can reply to topics in this forum


Powered by phpBB © 2001, 2005 phpBB Group