AutoHotkey Community

It is currently May 26th, 2012, 10:57 am

All times are UTC [ DST ]




Post new topic Reply to topic  [ 30 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: March 26th, 2008, 8:05 am 
Offline

Joined: October 17th, 2006, 4:15 pm
Posts: 7502
Location: Australia
This function is not compatible with recent versions of AutoHotkey_L. See this post for an alternative.

FileExtract: Extracts a file from this compiled script by using a dynamic FileInstall.
FileExtract_ToMem: Extracts a FileInstall'd file into memory without writing it to disk.

I wrote this mostly to demonstrate two techniques:
  • Modify a line of script at run-time to allow variable references in the source parameter of FileInstall, and prevent Ahk2Exe from interpreting it. (Turn FileCopy into FileInstall.)
  • Use machine code (compiled C++) to achieve multi-threading. (Read from the pipe while FileInstall is writing to it.)


FileExtract.ahk:
Code:
/*
    Function: FileExtract
 
    Extracts a file from this compiled script by using a dynamic FileInstall.
 
    Syntax:
        FileExtract( Source, Dest [, Flag ] )
 
    Parameters:
        Source  - The source string used in the original FileInstall.
        Dest    - The name of the file to be created.
        Flag    - Specify 1 to overwrite existing files, otherwise omit.
 
    Remarks:
        Unlike FileInstall, FileExtract() allows variables and expressions for Source,
        and does not cause Ahk2Exe to include a file into the executable.
*/
FileExtract(Source, Dest, Flag=0) {
    static init
    if !init
        cb := RegisterCallback("FileExtract_")
        ; cb->func->mJumpToLine->mActionType := ACT_FILEINSTALL
        , NumPut(A_AhkVersion>="1.0.48" ? 160:159, NumGet(NumGet(cb+28)+4), 0, "UChar") ; Fixed for AutoHotkey v1.0.48: ACT_FILEINSTALL has changed to 160.
        , DllCall("GlobalFree", "uint", cb)
    return FileExtract_(Source, Dest, Flag)
}
FileExtract_(Source, Dest, Flag) {
    FileCopy, %Source%, %Dest%, %Flag%
    return !ErrorLevel
}
 
/*
    Function: FileExtract_ToMem
 
    Extracts a FileInstall'd file into memory.
 
    Syntax:
        FileExtract_ToMem( Source, pData, DataSize [, InitialBufferSize, InitialBuffer ] )
 
    Parameters:
        Source       [in] - The source string used in the original FileInstall.
        pData    [in/out] - A pointer to the buffer where file data is written. See remarks.
        DataSize     [in] - If pData is zero, this indicates the initial buffer size.
                    [out] - Receives the number of bytes written to the buffer.
 
    Remarks:
        pData must specify zero or a valid pointer to memory allocated with GlobalAlloc().
 
        If the caller specifies a non-zero pData, it is used as the initial data buffer.
        If the buffer is too small, the function will reallocate it and update pData.
        The function does not delete the buffer on failure unless the caller specified zero.
 
        Once the data is no longer needed, free it using GlobalFree:
 
            DllCall("GlobalFree","uint",pData)
 
        DataSize indicates the amount of data written, not the size of the buffer.
        To determine the actual size of the buffer, use GlobalSize:
 
            MemSize := DllCall("GlobalSize","uint",pData)
*/
FileExtract_ToMem(Source, ByRef pData, ByRef DataSize)
{
    static ReadPipe, ConnectNamedPipe, ReadFile, GlobalReAlloc
    if !VarSetCapacity(ReadPipe)
    {
        ; Initialize the machine code function for reading from the pipe.
        hex =
        ( LTrim Join
        558BEC81EC0004000053568B75085733FF397E080F848D000000397E040F848400000057
        FF36FF561057BB00040000EB5E8B46088B4D088BD02B560C3BD1732803C08946088B560C
        2BC23BC1730503D18956086A02FF7608FF7604FF561885C074458B4D088946048B460C03
        460433FF85C976168D9500FCFFFF2BD08A0C0288088B4D0847403BF972F2014E0C6A008D
        450850538D8500FCFFFF50FF36FF561485C0758D40EB0233C05F5E5BC9C20400
        )
        ;~ MCode() - http://www.autohotkey.com/forum/viewtopic.php?t=21172
        VarSetCapacity(ReadPipe,StrLen(hex)//2)
        Loop % StrLen(hex)//2
           NumPut("0x" . SubStr(hex,2*A_Index-1,2), ReadPipe, A_Index-1, "Char")
        ;~ end
 
        ; Resolve ReadPipe()'s dependencies for later.
        hKernel32 := DllCall("GetModuleHandle", "str", "kernel32.dll")
        astr := A_IsUnicode ? "astr" : "str"
        ConnectNamedPipe := DllCall("GetProcAddress", "uint", hKernel32, astr, "ConnectNamedPipe")
        ReadFile         := DllCall("GetProcAddress", "uint", hKernel32, astr, "ReadFile")
        GlobalReAlloc    := DllCall("GetProcAddress", "uint", hKernel32, astr, "GlobalReAlloc")
    }
 
    UserOwnsData := !!pData ; True if pData is not [blank or zero].
    if !pData
    {   ; If DataSize is non-numeric or < 1, default to 1024.
        if (DataSize+0 < 1)
            DataSize := 1024
        pData := DllCall("GlobalAlloc","uint",0,"uint",DataSize)
    }
    else
    {   ; Get the actual size of the memory block,
        DataSize := DllCall("GlobalSize","uint",pData)
    }
 
    VarSetCapacity(ReadPipeStruct, 28, 0) ; ReadPipeStruct
 
    ; Fill ReadPipeStruct with ReadPipe()'s dependencies.
    NumPut(ConnectNamedPipe, ReadPipeStruct, 16)
    NumPut(ReadFile, ReadPipeStruct, 20)
    NumPut(GlobalReAlloc, ReadPipeStruct, 24)
 
    Random, pipe_name
 
    ; Create a named pipe (with an unpredictable name) for writing the file into.
    hNamedPipe := DllCall("CreateNamedPipe", "str", "\\.\pipe\" pipe_name, "uint", 3
                    , "uint", 0, "uint", 255, "uint",0, "uint",0, "uint",0, "uint",0)
    ; Set the parameters for the pipe-reading thread.
    NumPut(hNamedPipe, ReadPipeStruct, 0)
    NumPut(pData, ReadPipeStruct, 4)
    NumPut(DataSize, ReadPipeStruct, 8)
 
    ; Create a thread to read from the pipe into memory.
    ; The thread will start immediately, but will wait for a pipe connection.
    hReadThread := DllCall("CreateThread", "uint", 0, "uint", 0, "uint", &ReadPipe
                            , "uint", &ReadPipeStruct, "uint", 0, "uint*", ReadThreadID)
 
    ; "Replace flag" *must* be specified since the pipe... exists.
    FileExtractResult := FileExtract(Source, "\\.\pipe\" pipe_name, 1)
 
    if !FileExtractResult
    {   ; Open and close a connection to the pipe to terminate the thread.
        FileAppend,, \\.\pipe\%pipe_name%
    }
 
    Loop {
        ; Wait for the thread to terminate, or any window message to be received.
        r := DllCall("MsgWaitForMultipleObjectsEx", "uint", 1, "uint*", hReadThread
                                            , "uint", -1, "uint", 0x4FF, "uint", 0x6)
        if (r = 0) || (r = -1) ; WAIT_OBJECT_0 or WAIT_FAILED
            break
        Sleep, 1 ; Allow AutoHotkey to dispatch messages.
    }
 
    DllCall("DisconnectNamedPipe", "uint", hNamedPipe)
    DllCall("CloseHandle", "uint", hNamedPipe)
    DllCall("CloseHandle", "uint", hReadThread)
 
    if FileExtractResult || UserOwnsData
    {
        ; Either it was a success and we are returning the extracted data,
        ; or it failed and we are returning the memory to the caller, since
        ; they may want to reuse it.
        pData := NumGet(ReadPipeStruct,4)
        DataSize := NumGet(ReadPipeStruct,12)
    }
    else
    {
        ; If ReadPipe() fails because of low memory, pData may have been reallocated,
        ; so always use the value in the structure.
        DllCall("GlobalFree", "uint", NumGet(ReadPipeStruct,4))
        pData := DataSize := 0
    }
    return FileExtractResult
}
FileExtractDemo.ahk - Self-compiling Example Script (not Unicode-compatible):
Code:
; Uncomment this or put FileExtract.ahk in your function library:
;#include FileExtract.ahk

if !A_IsCompiled
{   ; Self-compile-and-run.
    RunWait, %A_AhkPath%\..\Compiler\Ahk2Exe.exe /in "%A_ScriptFullPath%"
    Run % SubStr(A_ScriptFullPath, 1, InStr(A_ScriptFullPath,".",1,0)-1) ".exe"
    ExitApp
   
    ; Not executed by AutoHotkey, but interpreted by Ahk2Exe:
    FileInstall, FileExtractDemo.ahk, ~
}

Menu, Tray, MainWindow

VarSetCapacity(important, 40, Asc("="))
GuiOut(important " Actual Script")

; Initialize pData since it has no value yet:
pData := 0
; DataSize specifies the desired initial size of the buffer, or zero for "don't care".
DataSize := 0

; The script itself is either >AUTOHOTKEY SCRIPT< or >AHK WITH ICON<.
if FileExtract_ToMem(">AUTOHOTKEY SCRIPT<", pData, DataSize)
    || FileExtract_ToMem(">AHK WITH ICON<", pData, DataSize)
{   ; Show the string data in a GUI.
    GuiOut(GetStrN(pData, DataSize))
}
else
{   ; Note: At this point, AutoHotkey has probably already shown an error dialog.
    GuiOut("-- Failed! --")
}

GuiOut(important " FileExtractDemo.ahk")

; Extract a FileInstall'd file.
if FileExtract_ToMem("FileExtractDemo.ahk", pData, DataSize)
{
    GuiOut(GetStrN(pData, DataSize))
}
else
{
    GuiOut("-- Failed! --")
}

; Free the buffer.
DllCall("GlobalFree", "uint", pData)

return

GuiOut(Text, GuiNum=37) {
    static GuiText
    Gui, %GuiNum%: Default
    if (GuiText = "") {
        GuiText := Text "`n"
        Gui, Font,, Courier New
        Gui, Add, Edit, ReadOnly W600 H400, %GuiText%
    } else
        GuiControl,, Edit1, % GuiText .= Text "`n"
    Gui, Show
    Gui, +LastFound +LabelGuiOut
    ControlSend, Edit1, ^{End}
    return
    GuiOutClose:
    GuiOutEscape:
    ExitApp
}

GetStrN(Pointer, Length) {
    VarSetCapacity(String, Length)
    DllCall("lstrcpyn", "str", String, "uint", Pointer, "int", Length+1)
    return String
}

ReadPipe (the machine code function in FileExtract_ToMem) was written in C++. The source code is as follows: (note you don't need this to use the function)
Code:
#define MCODE_API extern "C" __declspec(dllexport)

//...

struct ReadPipeStruct
{
    HANDLE named_pipe;  // Pipe to read from, set by caller.
    char *buffer;       // Caller sets this to a fixed memory block returned by GlobalAlloc().
                        // ReadPipe() may reallocate the memory with GlobalReAlloc() and update this.
    DWORD buffer_size;  // Caller must set this to the size of the buffer.
                        // ReadPipe() updates this if/when it reallocates the buffer.
    DWORD data_size;    // Caller must set this to the offset within buffer to write data at.
                        // ReadPipe() sets this to the offset of the byte after the last byte of data (<= buffer_size.)

    // The following must be set by the caller, to point to the named functions.
    BOOL (WINAPI * ConnectNamedPipe)(HANDLE hNamedPipe, LPOVERLAPPED lpOverlapped);
    BOOL (WINAPI * ReadFile)(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped);
    HGLOBAL (WINAPI * GlobalReAlloc)(HGLOBAL hMem, SIZE_T dwBytes, UINT uFlags);
};

MCODE_API DWORD WINAPI ReadPipe(ReadPipeStruct *rp)
{
    DWORD nNumberOfBytesRead;

    char *&buffer = rp->buffer;
    DWORD &buffer_size = rp->buffer_size;
    DWORD &data_size = rp->data_size;

    // Caller must specify non-NULL buffer.
    if (!(buffer_size && buffer))
        return FALSE;
   
    // Wait for a connection to the pipe.
    rp->ConnectNamedPipe(rp->named_pipe, NULL);

    // Use a temporary buffer for reading so we know when lpBuffer should be resized.
    // (Don't want to reallocate the buffer only to find that the pipe has ended...)
    char read_buffer[1024];
    DWORD i;
    char *pointer;

    #define BUFFER_REMAINING (buffer_size - data_size)

    while (rp->ReadFile(rp->named_pipe, read_buffer, sizeof(read_buffer), &nNumberOfBytesRead, NULL))
    {
        if (BUFFER_REMAINING < nNumberOfBytesRead)
        {   // Double the buffer size.
            buffer_size *= 2;
            // Ensure at least the required amount will be allocated.
            if (BUFFER_REMAINING < nNumberOfBytesRead)
                buffer_size = data_size + nNumberOfBytesRead;
            // Do the actual memory reallocation.
            pointer = (char*)rp->GlobalReAlloc((HGLOBAL)buffer, buffer_size, GMEM_MOVEABLE);
            if (!pointer)
                return FALSE;
            buffer = pointer;
        }
        // Copy the data we just read into the output buffer.
        pointer = buffer + data_size;
        for (i = 0; i < nNumberOfBytesRead; ++i)
            pointer[i] = read_buffer[i];
        // Advance the buffer position.
        data_size += nNumberOfBytesRead;
    }
    return TRUE;
}


Public domain. See Lexikos' default copyright license.


Last edited by Lexikos on January 21st, 2012, 1:26 am, edited 4 times in total.

Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: March 26th, 2008, 9:43 am 
Offline

Joined: May 24th, 2006, 2:49 pm
Posts: 4511
Location: Belgrade
thx m8.

Very informative as always.

_________________
Image


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: August 25th, 2008, 6:02 pm 
Offline

Joined: June 18th, 2008, 8:36 am
Posts: 4923
Location: AHK Forum
Hi Lexikos,

Many thanks for sharing this.

Is it possible to include an app, dll or script into the exe and run it from there?

_________________
AHK_H (2alpha) AHF TT _Struct WatchDir Yaml _Input ObjTree RapidHotkey DynaRun :wink:


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: August 25th, 2008, 10:47 pm 
Offline

Joined: October 17th, 2006, 4:15 pm
Posts: 7502
Location: Australia
Short answer: No.

Long answer:
App: No.
Dll: Maybe if you have a lot of time on your hands and a good understanding of the portable executable format.
Script: Only as part of the compiled script itself...


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: August 26th, 2008, 11:17 am 
Offline

Joined: May 24th, 2006, 2:49 pm
Posts: 4511
Location: Belgrade
Dll definitely, there is C code around that should be ported to AHK but it IS nasty job.

About Apps, I am not aware of any solution. There are some posts about it on Experts Exchange so if you have acc there you may want to check it out.

_________________
Image


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: August 26th, 2008, 1:47 pm 
Offline

Joined: August 1st, 2008, 8:50 am
Posts: 19
Location: South Africa
Awesome script can you do it with a picture? or video?


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: August 26th, 2008, 10:34 pm 
Offline

Joined: October 17th, 2006, 4:15 pm
Posts: 7502
Location: Australia
You can do it with any type of file. Whether you can use the data in the "file" without writing it back to disk is beyond the scope of this thread. Such things have been demonstrated in other threads.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: August 27th, 2008, 6:32 pm 
Offline

Joined: June 18th, 2008, 8:36 am
Posts: 4923
Location: AHK Forum
Quote:
Script: Only as part of the compiled script itself...


Does it mean I could use similar to #include?

_________________
AHK_H (2alpha) AHF TT _Struct WatchDir Yaml _Input ObjTree RapidHotkey DynaRun :wink:


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: August 27th, 2008, 10:19 pm 
Offline

Joined: October 17th, 2006, 4:15 pm
Posts: 7502
Location: Australia
Actually, you could use pipes to execute a script from memory without writing it back to file. You'd need a copy of AutoHotkey.exe, though.

Otherwise, you would need to modify AutoHotkey, and even then it would not provide any benefits over #Include.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: August 27th, 2008, 10:52 pm 
Offline

Joined: June 18th, 2008, 8:36 am
Posts: 4923
Location: AHK Forum
Thanks for explanation Lexikos, I do use pipes already, very useful by the way, but I always need the AutoHotkey.exe.

I think actually pipes provide benefit over #include, because they are not treated as code but only memory.
When AutoHotkey.exe would be included it would also not read it each time from the drive as well and would be as quick running from a usb stick or so, right?

I have around 200 scripts already (I use AHK for around half year) and I think there will be more than 1000 very quickly. So I would like to compile all my scripts into one exe and be able to run them on request only.

Should it than not be faster on loading scripts?

Additionally you could have millions of scripts with always the same and short variables, rather than 1 script with million long and complex variables.

How difficult would it be to change the code of AutoHotkeySC.bin to be included in exe and be able to run pipes?

Would be also great to have an #include function for pipes. :D

_________________
AHK_H (2alpha) AHF TT _Struct WatchDir Yaml _Input ObjTree RapidHotkey DynaRun :wink:


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: August 28th, 2008, 11:04 am 
Offline

Joined: October 17th, 2006, 4:15 pm
Posts: 7502
Location: Australia
HotKeyIt wrote:
I think actually pipes provide benefit over #include, because they are not treated as code but only memory.
Perhaps you mean to say that the data which you send through the pipe is stored in the source code as text, and not loaded as part of the script as an #include would be. Obviously data sent through the pipe is treated as code on the receiving end. ;)
Quote:
When AutoHotkey.exe would be included it would also not read it each time from the drive as well
Barring a major redesign, a single AutoHotkey process cannot run multiple isolated scripts. AutoHotkey would need to be loaded into a new process for each script. It is not feasible to create a new process other than by loading the executable from the file system each time.
Quote:
and would be as quick running from a usb stick or so, right?
Windows' file caching may lessen the impact of a slow device. If that is not enough, you could create a temporary file on the hard drive.
Quote:
So I would like to compile all my scripts into one exe and be able to run them on request only.
Why?
Quote:
Additionally you could have millions of scripts with always the same and short variables, rather than 1 script with million long and complex variables.
Each script must have its own variables, even if they are named the same. Each running instance of a script would also duplicate resources, such as the code for the script "interpreter" and all commands, in addition to any C++ variables, etc.
Quote:
How difficult would it be to change the code of AutoHotkeySC.bin to be included in exe and be able to run pipes?
It is not feasible to load an executable file from any source other than the file system.

However, since the script itself is embedded in the executable using the same methods as FileInstall, it would be feasible to allow multiple independent scripts to be stored in a single executable. Any given script could then be loaded by specifying its name on the command-line, via a pipe or network socket, etc.
Quote:
Would be also great to have an #include function for pipes.
A non-compiled script can already pipe an #include to a new instance of itself via the *i (ignore failure) flag:
Code:
#Include *i \\.\pipe\pipename
There are a number of ways it could be made to work with compiled scripts. My current favourite (idea) is to introduce a new flag (similar to *i) that indicates the #include should not be processed by Ahk2Exe. This would required two modifications: First, Ahk2Exe must check for this flag. Second, #Include handling must be included in AutoHotkeySC (currently it is excluded to reduce code size.)

Unfortunately, the public source code for Ahk2Exe seems to be missing an actual implementation for /NoDecompile. An alternative would be to add two entirely new directives which would not be recognised by Ahk2Exe - #IncludeExternal and #IncludeExternalAgain or similar.


I will keep these ideas in mind for AutoHotkey_L. If you could explain the actual uses of this kind of capability, it may help to raise the priority.

In retrospect, some of this was discussed on page 2 of How to: Run Dynamic Script... Through a Pipe!.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: August 28th, 2008, 9:08 pm 
Offline

Joined: June 18th, 2008, 8:36 am
Posts: 4923
Location: AHK Forum
Lexikos wrote:
HotKeyIt wrote:
I think actually pipes provide benefit over #include, because they are not treated as code but only memory.

Perhaps you mean to say that the data which you send through the pipe is stored in the source code as text, and not loaded as part of the script as an #include would be. Obviously data sent through the pipe is treated as code on the receiving end. Wink

Yes, thanks for making it clear.


Lexikos wrote:
HotKeyIt wrote:
When AutoHotkey.exe would be included it would also not read it each time from the drive as well

Barring a major redesign, a single AutoHotkey process cannot run multiple isolated scripts. AutoHotkey would need to be loaded into a new process for each script. It is not feasible to create a new process other than by loading the executable from the file system each time.


This is really regretful, why it cannot be just copied to mem on first run and than be loaded from mem instead file system?
Can it be cached from hard drive or network, as I understand when a file is cached from usb then into mem not onto hard disk?
How does it actually work with DLL, as far as I know they are not read form file system on each use?

Lexikos wrote:
HotKeyIt wrote:
and would be as quick running from a usb stick or so, right?

Windows' file caching may lessen the impact of a slow device. If that is not enough, you could create a temporary file on the hard drive.

I have just run a test with file reading loop 1000 times for a same directory, one on my hard drive and same directory with same content on my usb device.
RESULT: doing it on usb is gone 10 times faster. WOW, caching is great.
[EDIT]: tried the same with SetBatchLines -1, and it is 16 times faster on usb, than I did the same on hard disk, but put the folder into C:\ now on C:\ it is only 2 times slower, looks like performance under C:\ is much better.


Many thanks for this hint, this helps already a lot as I need to loop my script folders to keyword my scripts. :D
If caching from hard drive is not possible, It is very recommended to have the AutoHotkey.exe on a removable drive if you like to start your scripts faster.

Lexikos wrote:
HotKeyIt wrote:
So I would like to compile all my scripts into one exe and be able to run them on request only.

Why?


Well mainly because of speed to load a script.
Wouldn't it be faster to run a pipe rather than a cached file?
EDIT: Do you think running it from cache might be faster than from a pipe?

But also to have a complete version of all my scripts.
A script would not be deleted or modified by accident.
When I need to change it I would extract from pipe to editor, edit and recompile. This would be 1 portable, flexible, safe, fast and all in one solution app, additionally I would have automatic backup of all scripts in one file (the old exe), so no need to zip all my scripts for backup anymore.

Lexikos wrote:
HotKeyIt wrote:
Additionally you could have millions of scripts with always the same and short variables, rather than 1 script with million long and complex variables.

Each script must have its own variables, even if they are named the same. Each running instance of a script would also duplicate resources, such as the code for the script "interpreter" and all commands, in addition to any C++ variables, etc.

This is clear, I rather mean write a script instead of a goto or a function, which also would return something. In that way I can use the same one letter variable names in each script to keep it simple and could have an app with million scripts that all work on their own but also combined and are dynamic, where each script is to be very small and simple.
I have had already many Ideas but I did not realize them because having so many files is dangerous for me.

Lexikos wrote:
A non-compiled script can already pipe an #include to a new instance of itself via the *i (ignore failure) flag:

So a script that will run trough pipe can have #Include *i \\.\pipe\pipename?
If so this is very great, I will test it.

Lexikos wrote:
I will keep these ideas in mind for AutoHotkey_L. If you could explain the actual uses of this kind of capability, it may help to raise the priority.

By the way, your AutoHotkey_L is very impressive.
#if expression is really great!

Lexikos wrote:
An alternative would be to add two entirely new directives which would not be recognised by Ahk2Exe - #IncludeExternal and #IncludeExternalAgain or similar.


So it is possible?
I think having all in one file would be just great, for simplicity and loading speed.

_________________
AHK_H (2alpha) AHF TT _Struct WatchDir Yaml _Input ObjTree RapidHotkey DynaRun :wink:


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: August 28th, 2008, 11:02 pm 
Offline

Joined: October 17th, 2006, 4:15 pm
Posts: 7502
Location: Australia
HotKeyIt wrote:
This is really regretful, why it cannot be just copied to mem on first run and than be loaded from mem instead file system?
Windows does not provide the necessary API.
Quote:
Can it be cached from hard drive or network, as I understand when a file is cached from usb then into mem not onto hard disk?
I was referring to file system caching, which should work for most devices.
Quote:
How does it actually work with DLL, as far as I know they are not read form file system on each use?
IIRC, if a DLL can be loaded at its preferred base memory address, it can be shared with other processes. If it cannot, I am not sure if it is loaded from disk or from memory.
Quote:
Well mainly because of speed to load a script.
I do not see why the speed would be better.
Quote:
Wouldn't it be faster to run a pipe rather than a cached file?
Not necessarily. Where would you get the data to send through the pipe?
Quote:
But also to have a complete version of all my scripts.
A script would not be deleted or modified by accident.
What is the benefit of a composite exe over a folder with read-only attribute/permissions?
Quote:
When I need to change it I would extract from pipe to editor, edit and recompile.
Btw, why pipes? If all scripts are inside the compiled exe, it can load them directly. Pipes are meant for inter-process communication. You could use FileInstall to embed or extract the files; AutoHotkeySC need only be modified to load something other than >AUTOHOTKEY SCRIPT< or >AHK WITH ICON<.
Lexikos wrote:
I rather mean write a script instead of a goto or a function, which also would return something.
To "call" the script would require a new process to be loaded each time.
Quote:
In that way I can use the same one letter variable names in each script
You can already do this with local variables.
Quote:
So a script that will run trough pipe can have #Include *i \\.\pipe\pipename?
It will instruct AutoHotkey to insert the script file "\\.\pipe\pipename" at this location in the script. The script which contains the #Include will not necessarily be run through a pipe. AutoHotkey reads from the pipe as if it were any other file.
Quote:
So it is possible?
Yes.
Quote:
I think having all in one file would be just great, for simplicity and loading speed.
I still am not convinced that loading speed would improve. What do you mean by simplicity?


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: August 28th, 2008, 11:46 pm 
Offline

Joined: June 18th, 2008, 8:36 am
Posts: 4923
Location: AHK Forum
Quote:
I was referring to file system caching, which should work for most devices.

So why is file reading loop faster on usb disk?

Quote:
I do not see why the speed would be better.

Not necessarily. Where would you get the data to send through the pipe?


I would store the script in a variable in the script which would be run trough pipe by pressing a hotkey or hoststring.

Quote:
What is the benefit of a composite exe over a folder with read-only attribute/permissions?

Just that it is one file and possibly consumes less memory on a harddrive or stick, also on compiling, comments would be excluded.

Quote:
It will instruct AutoHotkey to insert the script file "\\.\pipe\pipename" at this location in the script. The script which contains the #Include will not necessarily be run through a pipe. AutoHotkey reads from the pipe as if it were any other file.


So when I have a pipe with content "Return`n#Include "\\.\pipe\pipename"`n" and pipename would be my script it would work.

Quote:
I still am not convinced that loading speed would improve. What do you mean by simplicity?

Loading speed: I think to pass a variable trough pipe might be faster than loading an file with even cached AutoHotkey.exe ?
Simplicity: I mean 1 file instead of 200, like a one file database of scripts.

I will try to build an exe out of my scripts having them as a variable trough FileReading Loop that will run the scripts trough pipe. As I understand under your view it would be not faster than reading from cached drive, right?

_________________
AHK_H (2alpha) AHF TT _Struct WatchDir Yaml _Input ObjTree RapidHotkey DynaRun :wink:


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: August 29th, 2008, 9:16 am 
Offline

Joined: October 17th, 2006, 4:15 pm
Posts: 7502
Location: Australia
HotKeyIt wrote:
So why is file reading loop faster on usb disk?
My guess is removable drives are cached differently.
Quote:
I would store the script in a variable in the script which would be run trough pipe by pressing a hotkey or hoststring.
At what point would the script be loaded from disk into the variable? When the composite exe is loaded into memory? Would you keep all scripts in memory?
Quote:
Just that it is one file and possibly consumes less memory on a harddrive or stick, also on compiling, comments would be excluded.
To me that isn't much of a reason, given the relatively small size of scripts.
[Edit: Earlier you mentioned automatic backup of your scripts, but the backup would not be complete if comments are excluded.]
Quote:
So when I have a pipe with content "Return`n#Include "\\.\pipe\pipename"`n" and pipename would be my script it would work.
There is no sense putting #Include \\.\pipe\pipename in the content to be piped, as the pipe is not yet open! Think of a pipe as a file that exists in a program somewhere, rather than on disk. Just as #Include file.ahk instructs AutoHotkey to insert the contents of file.ahk at the position of the #Include, #Include \\.\pipe\pipename instructs AutoHotkey to read data from the pipe named "pipename" and insert it at the position of the #Include. In actuality, AutoHotkey treats it exactly like a file, and Windows takes care of the rest.

Here is an example based on my original script-pipe demonstration:
Code:
; Read and insert script content from testpipe if it exists.
#Include *i \\.\pipe\testpipe

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

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
}

; Re-launch self. This time the #include should work.
Run, "%A_ScriptFullPath%"

DllCall("ConnectNamedPipe","uint",pipe_ga,"uint",0)
DllCall("CloseHandle","uint",pipe_ga)
DllCall("ConnectNamedPipe","uint",pipe,"uint",0)

; Append ExitApp to ensure we don't loop infinitely.
Script := chr(239) chr(187) chr(191) Script "`r`nExitApp"

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

Quote:
Loading speed: I think to pass a variable trough pipe might be faster than loading an file with even cached AutoHotkey.exe ?
I believe it would provide negligible benefit, if any. I am certain it would provide no performance benefit over piping to a copy of AutoHotkey.exe.
Quote:
Simplicity: I mean 1 file instead of 200, like a one file database of scripts.
Which is simpler: 200 virtual files inside one file in the file system, or 200 files in the file system?

Edit: It seems it would not be as simple as I originally thought to support #Include in compiled scripts, or to support both embedded and real files. Loading scripts embedded via FileInstall is fairly simple, so you could pre-parse all scripts before embedding, stripping out comments and merging #includes.

I have benchmarked the following three methods using 57KB scriptlets:
  1. Run scriptlets directly using AutoHotkey.exe.
  2. Run scriptlets from memory using AutoHotkey.exe \\.\pipe\ahk.
  3. Compile scriptlets into a single executable using FileInstall and a custom AutoHotkeySC.
The following are my observations:
  • #1 is the fastest.
  • #2 is approximately half the speed of #1.
  • #3 can be as fast as #2, but slows slightly for each additional embedded file.
  • UPX compression makes no appreciable difference.
  • Loading 100 different files (with identical contents) is roughly the same as loading 1 file 100 times.


Report this post
Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 30 posts ]  Go to page 1, 2  Next

All times are UTC [ DST ]


Who is online

Users browsing this forum: Exabot [Bot], sks and 11 guests


You can post new topics in this forum
You can reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Powered by phpBB® Forum Software © phpBB Group