Jump to content

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

AutoHotkey.dll


  • Please log in to reply
1328 replies to this topic
tinku99
  • Members
  • 560 posts
  • Last active: Feb 08 2015 12:54 AM
  • Joined: 03 Aug 2007
V0.7 re-enabled Reload command. (the hosting application restarts with the client A_ScriptName as the command line parameter)

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.

tinku99
  • Members
  • 560 posts
  • Last active: Feb 08 2015 12:54 AM
  • Joined: 03 Aug 2007
Revision 7

* 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

basi
  • Members
  • 8 posts
  • Last active: Jan 11 2011 07:49 PM
  • Joined: 28 May 2007
Just wanted to let you know that some of us have been playing around with this.

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.

tinku99
  • Members
  • 560 posts
  • Last active: Feb 08 2015 12:54 AM
  • Joined: 03 Aug 2007
Basi, thanks for the feedback.

AFTER the script runs it always crashes C#.

Have you tried to make the script persistent? with: #Persistent
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.

I would just like the Autohotkey.dll to expose the commands/functions built into AHK

Which commands and functions are you most interested in seeing libyfied?

tinku99
  • Members
  • 560 posts
  • Last active: Feb 08 2015 12:54 AM
  • Joined: 03 Aug 2007
I modified numget and numput to take numerical arguments for type in addition to the dllcall types. (number != dlltypesize)
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


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

also modified variable assignments to preserve binary data by copying varCapacity instead of varLength.

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:
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.

It would be nice if argstruct and dereftype used varcapacity instead of stringlength as well,

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...

numput("element0", a, 0, 10)

I have to ask... why??? You can easily DllCall lstrcpyn or RtlMoveMemory.

tinku99
  • Members
  • 560 posts
  • Last active: Feb 08 2015 12:54 AM
  • Joined: 03 Aug 2007

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.

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...

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.

Well, I definitely don't want to break any existing scripts. Will think about its implications some more. IMO, what is unintuitive is:
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)

numput("element0", a, 0, 10)

I have to ask... why??? You can easily DllCall lstrcpyn or RtlMoveMemory.

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?

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


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

VarCapacity only exceeds VarStringLength significantly when you have set the capacity high yourself, with VarSetCapacity,

Really? :roll:
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.

which to me serves the purpose of VarSetLength.

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.

How about a flag for string vs. binary data? Binary data would get copied by capacity, string data by string length...

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.

IMO, what is unintuitive is:

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

for the same reasons as having numput in the first place.

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.

By the way, I think i have an idea of how to reset the memory of the script.

Careful. Some structures use dynamic memory (e.g. Func::mVar). Some other global structures must be re-initialized.

The current limitations on numput are unnecessary.

The current limitations are intuitive.

Also, I am thinking of modifying and exporting the command: Hotkey to take function names and function pointers in addition to labels.

Sounds useful.

tinku99
  • Members
  • 560 posts
  • Last active: Feb 08 2015 12:54 AM
  • Joined: 03 Aug 2007
var := binaryVar
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.

tinku99
  • Members
  • 560 posts
  • Last active: Feb 08 2015 12:54 AM
  • Joined: 03 Aug 2007
Changes in ahkdll9:
modified fileappend, to send output to stderr, with "stderr" is specified as filename. then do:
FileAppend, errors, stderr
...
> autohotkey.exe script.ahk 2> error.log
modified FileReadLine, to accept stdin for file
FileReadLine, line1, stdin, 1
FileReadLine, line2, stdin, 1
listvars
msgbox
...
> type script.ahk | autohotkey.exe script.ahk
example filter program:
pipes.exepipes.ahk
AutoHotkeySC.bin

HotKeyIt
  • Moderators
  • 7439 posts
  • Last active: Jun 22 2016 09:14 PM
  • Joined: 18 Jun 2008
Hi tinku99, I have played a little around with AutoHotkey_N.exe and AutoHotkey.dll.

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


HotKeyIt
  • Moderators
  • 7439 posts
  • Last active: Jun 22 2016 09:14 PM
  • Joined: 18 Jun 2008
Hi tinku99,

Thank you so much for multithreading feature, its just great :D
This is exactly what i have been asking around a while ago. I already thought this is not going to happen :O
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 :D
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_
}


tinku99
  • Members
  • 560 posts
  • Last active: Feb 08 2015 12:54 AM
  • Joined: 03 Aug 2007
I 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...

I had a plan for clearing the loaded script and its memory, but I won't get to it soon unfortunately.

HotKeyIt
  • Moderators
  • 7439 posts
  • Last active: Jun 22 2016 09:14 PM
  • Joined: 18 Jun 2008

I 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...

It would be nice if a pointer would be returned :)
Is it feasible to import text instead of a file as well?

tinku99
  • Members
  • 560 posts
  • Last active: Feb 08 2015 12:54 AM
  • Joined: 03 Aug 2007

It would be nice if a pointer would be returned :)
Is it feasible to import text instead of a file as well?

You were right, there was a bug.
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
}
*/