
AutoHotkey.dll
Experimental: Exported Functions for many Commands, BIV: Examples
Edit:
I have disabled the exported Commands for now, will add them when they are more robust.

* Merged with AutoHotkey v.1.0.48.03_L30
* in addition to AutoHotkey.dll, created AutoHotkey_N.exe with
* exported functions to access built in variables: ebiv.cpp
* added function:
* linepointer = Import(filename, aAllowDuplicateInclude = false, aIgnoreLoadFailure = false).
* linepointer is a pointer to the first line in the imported script
* If the 3rd parameter is > 1, then all currently loaded lines, functions, and labels are ignored starting now.
* If you are going to reload particular hotkeys, use the Hotkey command rather than Hotkey labels.
* i.e.
* Hotkey, F2, mylabel, on ; this can be repeated in future import(script) calls
* not
* F2::msgbox ; this will crash the script if you import another script that uses the same hotkey label

For my purposes I am trying to call Autohotkey.dll from C#. I have been able to call the DLL and have it run a script. But AFTER the script runs it always crashes C#. Idealistically, I would just like the Autohotkey.dll to expose the commands/functions built into AHK, so that I could call them directly from C#. No biggy, just playing around.
Thank you for continuing to develope your DLL.
-Basi
P.S. Haven't tried IronAHK but will look into it.

Have you tried to make the script persistent? with: #PersistentAFTER the script runs it always crashes C#.
Clean unloading of the dll is on my todo list, but low priority.
If you just want to reload a different script into the application, you can do that with the new builtin Import() function. I will add that functionality in the exported addfile() function also.
Which commands and functions are you most interested in seeing libyfied?I would just like the Autohotkey.dll to expose the commands/functions built into AHK

also modified variable assignments to preserve binary data by copying varCapacity instead of varLength.
It would be nice if argstruct and dereftype used varcapacity instead of stringlength as well, but i don't know what depends on this.
also, i compiled the machine code functions from lexikos' lowlevel as BIF's also.
AutoHotkey_N.exe
source
Example:
varsetcapacity(a, 10000, 0) a = a element = element%A_Index% numput("element0", a, 0, 10) loop, 10 { element = element%A_Index% numput(element, a, 10 * A_Index, 10) } msgbox % a loop, 10 { ListVars msgbox % el := numget(a, 10 * A_Index, 10) } return

That is likely to have an adverse effect on performance of some scripts - i.e. when the source variable's capacity is much greater than the length of the meaningful data. Simple assignments can already copy null characters if you do not update the length. For instance:also modified variable assignments to preserve binary data by copying varCapacity instead of varLength.
VarSetCapacity(n,4,1) ; 4 non-zero bytes NumPut(0,n) m := n MsgBox % "n: '" n "'`nm: '" m "'`nStrLen(m): " StrLen(m)Rather than unconditionally copying all data (meaningful or not), you could implement VarSetLength() and let the script define how much data is meaningful and therefore should be copied.
I strongly disagree. Copying beyond the length (and beyond null characters) where a string is expected would not only break the majority of scripts (where data beyond length is meaningless), but would be utterly unintuitive. That brings me to my next point...It would be nice if argstruct and dereftype used varcapacity instead of stringlength as well,
I have to ask... why??? You can easily DllCall lstrcpyn or RtlMoveMemory.numput("element0", a, 0, 10)

VarCapacity only exceeds VarStringLength significantly when you have set the capacity high yourself, with VarSetCapacity, which to me serves the purpose of VarSetLength. How about a flag for string vs. binary data? Binary data would get copied by capacity, string data by string length...That is likely to have an adverse effect on performance of some scripts -...you could implement VarSetLength() and let the script define how much data is meaningful and therefore should be copied.
Well, I definitely don't want to break any existing scripts. Will think about its implications some more. IMO, what is unintuitive is:Copying beyond the length (and beyond null characters) where a string is expected would not only break the majority of scripts (where data beyond length is meaningless), but would be utterly unintuitive.
VarSetCapacity(x, 100, 88) x = aaaaaaaaasdfs ; some data numput(0, x, 80, "char") copyx := x if (numget(x, 85) != numget(copyx, 85)) Msgbox copyx is not the same as x beyond byte element 80. x = aaaaaaaaasdfsaaaaaaaaaaaaaaaaaa ; some data numput(0, x, 10, "char") copyx := x if (numget(x, 15) == numget(copyx, 15)) Msgbox copyx is the same as x beyond byte element 10 for (varsize < 64)
for the same reasons as having numput in the first place. The current limitations on numput are unnecessary. Also, isn't numput faster than a dllcall?I have to ask... why??? You can easily DllCall lstrcpyn or RtlMoveMemory.numput("element0", a, 0, 10)
By the way, I think i have an idea of how to reset the memory of the script. I can just step through the linked list of simpleheaps, and free them all. Then I can load a whole new script using the bif Import() or exported addfile()... without most of the memory leak.
Also, I am thinking of modifying and exporting the command: Hotkey to take function names and function pointers in addition to labels. Something like:
void Hotkey::PerformInNewThreadMadeByCaller(HotkeyVariant &aVariant){ ... if aVariant.mJumpToLabel ResultType result = aVariant.mJumpToLabel->Execute(); if aVariant.mHotFunc { char returnValue[64]; Func *hotfunc = g_script.FindFunc(aVariant.mHotFunc); hotfunc->Call(returnValue); } if aVariant.mFunctionPointer { (*aVariant.mFunctionPointer)(aVariant); // optionally in an os thread } }

Really? :roll:VarCapacity only exceeds VarStringLength significantly when you have set the capacity high yourself, with VarSetCapacity,
Loop, 50 var .= "VarSetCapacity isn't the only way to expand a variable. " var := "abc" MsgBox % VarSetCapacity(var)Not the most practical example, but hopefully you get my point.
The capacity of a variable is by definition the amount of data it can contain (not necessarily the amount it does contain), regardless of how VarSetCapacity is most commonly used.which to me serves the purpose of VarSetLength.
What if binary data may be variable length, and we want the variable to be able to hold more binary data than it currently holds? Flagging the variable as "binary or not" does not allow for this; VarSetLength() does.How about a flag for string vs. binary data? Binary data would get copied by capacity, string data by string length...
I don't see why you expect meaningless garbage to be copied, especially when you've assigned a string. (I know it's "just" an example...)IMO, what is unintuitive is:
I think you missed my point. Num is short for NUMBER. A (non-numeric) string is not a number. If you were to create a built-in function for copying strings and give it a more logical name, I would not object.for the same reasons as having numput in the first place.
Careful. Some structures use dynamic memory (e.g. Func::mVar). Some other global structures must be re-initialized.By the way, I think i have an idea of how to reset the memory of the script.
The current limitations are intuitive.The current limitations on numput are unnecessary.
Sounds useful.Also, I am thinking of modifying and exporting the command: Hotkey to take function names and function pointers in addition to labels.

was causing problems, have removed this buggy feature for now.
Will use a new BIF called move or copy instead of numput as lexikos suggests.

modified fileappend, to send output to stderr, with "stderr" is specified as filename. then do:
FileAppend, errors, stderr ... > autohotkey.exe script.ahk 2> error.logmodified FileReadLine, to accept stdin for file
FileReadLine, line1, stdin, 1 FileReadLine, line2, stdin, 1 listvars msgbox ... > type script.ahk | autohotkey.exe script.ahkexample filter program:
pipes.exepipes.ahk
AutoHotkeySC.bin

Tried the function import() but it does not return the line_ptr but a character, which differs from file to file? Is it a bug?
line_ptr:=Import("x:\test1.ahk") MsgBox % line_ptr

Thank you so much for multithreading feature, its just great

This is exactly what i have been asking around a while ago. I already thought this is not going to happen

Hopefully it will be possible to unload the dll so memory gets freed and a file can be reloaded. :?:
Is there a way to include that functionality/function into AutoHotkey_N.exe so dll would not be required at all?
I found out that you can load different dlls just by renaming them so copying is not required

I have created a function for this so you can run as many threads as you like.
- Thread(file,parameters,dll)
- - file can be a file or text/variable containing script
- - parameters to pass to file
- - AutoHotkey.dll location
:!: Currently it is required to include #Persistent in your script and you cannot load same file again
Press F1, F2 or F3 to run the file (if exist) or F4 or F5 to run variable script
Mandelbrot*.ahk
#Persistent AHKfile1=%A_ScriptDir%\mandelbrot1.ahk AHKfile2=%A_ScriptDir%\mandelbrot2.ahk AHKfile3=%A_ScriptDir%\mandelbrot3.ahk Return !r::Reload !q::ExitApp F1:: F2:: F3:: file:="AHKfile" . SubStr(A_ThisHotkey,0) Thread(%file%) return F4:: Script= ( #Persistent #NoEnv #NoTrayIcon Loop ToolTip `% A_Now "``n" A_TickCount,0,0 ) Thread(script) Return F5:: Script= ( #Persistent #NoEnv #NoTrayIcon Loop ToolTip `% A_Now "``n" A_TickCount,0,50 ) Thread:=Thread(script) Return Thread(file="",parameters="",dll="AutoHotkey.dll"){ static thread, ahkdll, file_check If (!FileExist(dll)) Return false isfile=0 Loop %file% If (InStr(file_check,"|" . A_LoopFileFullPath . "|") or (file_check.=("|" . A_LoopFileFullPath . "|") and !(isfile:=1))) Return false thread:="AutoHotkey_" A_TickCount . ".dll" FileMove, %dll%, %thread% lib:=DllCall("LoadLibrary", "str", thread) FileMove, %thread%,%dll% If (!isfile){ #__PIPE_NAME_ := A_TickCount #__PIPE_GA_ := DllCall("CreateNamedPipe","str","\\.\pipe" #__PIPE_NAME_,"uint" ,2,"uint",0,"uint",255,"uint",0,"uint",0,"uint",0,"uint",0) #__PIPE_ := DllCall("CreateNamedPipe","str","\\.\pipe" #__PIPE_NAME_,"uint" ,2,"uint",0,"uint",255,"uint",0,"uint",0,"uint",0,"uint",0) if (#__PIPE_=-1 or #__PIPE_GA_=-1) { MsgBox CreateNamedPipe failed. Return } thread_ := DllCall(thread . "\ahkdll", "str", "\\.\pipe" . #__PIPE_NAME_, "str" , "", "str", parameters, "Cdecl Int") DllCall("ConnectNamedPipe","uint",#__PIPE_GA_,"uint",0) DllCall("CloseHandle","uint",#__PIPE_GA_) DllCall("ConnectNamedPipe","uint",#__PIPE_,"uint",0) } else thread_ := DllCall(thread . "\ahkdll", "str", file, "str", "", "str", parameters, "Cdecl Int") If (!isfile){ file := chr(239) . chr(187) . chr(191) . file if !DllCall("WriteFile","uint",#__PIPE_,"str",file,"uint",StrLen(file)+1,"uint*",0,"uint",0) MsgBox WriteFile failed: %ErrorLevel%/%A_LastError% DllCall("CloseHandle","uint",#__PIPE_) } ErrorLevel:=lib Return thread_ }

The code imported is already byte compiled.
You can start running functions imported dynamically without doing anything else...
I had a plan for clearing the loaded script and its memory, but I won't get to it soon unfortunately.

It would be nice if a pointer would be returnedI wouldn't worry about the pointer or char too much.
The code imported is already byte compiled.
You can start running functions imported dynamically without doing anything else...

Is it feasible to import text instead of a file as well?

You were right, there was a bug.It would be nice if a pointer would be returned
Is it feasible to import text instead of a file as well?
Now Import also returns a pointer just like addfile.
Importing text is possible, but will require some changes to addfile.
Will add it to list of things to do. Until then, you can load text through a named pipe.
To use lowlevel with AutoHotkey_N or AutoHotkey.dll make the following changes to LowLevel instead of using LowLevel_Init():
; Replaced mcode functions with builtin functions __static(var) { return static(var) } __getVar(var) { return getVar(var) } __alias(alias, alias_for) { return alias(alias, alias_for) } __cacheEnable(var) { return cacheEnable(var) } __getTokenValue(token) { return getTokenValue(token) } /* myLowLevel_init() { __init() } __init() { Global ; __getFirstFunc must be called at least once before (or by) __mcode, or it won't work properly later on. __getFirstFunc() __mcode("__getVar","8B4C24088B0933C08379080375028B018B4C2404998901895104C3") __mcode("__static","8B4424088B008378080375068B0080481504C3") __mcode("__alias","8B4C24088B01837808038B4904751D8B51088B005633F64A74044A4A75028B3185F60F94C189700C8848175EC3") __mcode("__cacheEnable","8B4424088B0083780803750F8B008078170075038B400C8060157FC3") __mcode("__getTokenValue","8B4424088B0083780801752B8B008B500883FA0375108B008A481580E13074180FB6D1C1EA048B4C24048951088B1089118B4004894104C38B4C2404C74108040000008B40088901C3") __mcode("__init","C3"), __mcode("LowLevel_init","C3") ; C3 = RET } */
