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
Changes in V9:
1. Added builtin FindFunc()
2. updated addfile(), so when called with 2 as the 3rd parameter.
For ex. addfile("somefile.ahk", 1, 2)
simpleheap memory used by late included files upto this point is freed. Hotkeys, labels, and functions from the first call to addfile are retained.
If you want to create hotkeys in later include files that have already been defined, use a dynamic function call with the Hotkey command:
Hotkey, label, hotkey
2a: I ran a stress test loading 2 client scripts alternately 2000 times, and the total memory usage only increased by 200kb, compared to 2 megabytes with the older version.
2b: This allows me to experiment more seriously with an ahk webserver / app server that can run short independent scripts without launching a separate process.
For example, hosting AutoHotkey.dll in modpython for apache:
import os, time
from mod_python import apache
from ctypes import *

ahk = cdll.LoadLibrary("AutoHotkey.dll")
ahk.ahkdll(create_string_buffer("..\\htdocs\\test\\pyclient.ahk"), "", "")   

def handler(req):
    req.content_type = 'text/plain'
    req.write("Hello World2!")
    return apache.OK
FindFunc code:
EXPORT unsigned int ahkFindFunc(char *funcname)
{
return (unsigned int)g_script.FindFunc(funcname);
}

void BIF_FindFunc(ExprTokenType &aResultToken, ExprTokenType *aParam[], int aParamCount) // Added in Nv8.
{
	// Set default return value in case of early return.
	aResultToken.symbol = SYM_INTEGER ;
	aResultToken.marker = "";
	// Get the first arg, which is the string used as the source of the extraction. Call it "findfunc" for clarity.
	char funcname_buf[MAX_NUMBER_SIZE]; // A separate buf because aResultToken.buf is sometimes used to store the result.
	char *funcname = TokenToString(*aParam[0], funcname_buf); // Remember that aResultToken.buf is part of a union, though in this case there's no danger of overwriting it since our result will always be of STRING type (not int or float).
	int funcname_length = (int)EXPR_TOKEN_LENGTH(aParam[0], funcname);
	aResultToken.value_int64 = (__int64)ahkFindFunc(funcname);
	return;
}
updated addfile code:
EXPORT unsigned int addFile(char *fileName, bool aAllowDuplicateInclude, int aIgnoreLoadFailure)
{   // dynamically include a file into a script !!
	// labels, hotkeys, functions.   
	static int filesAdded = 0  ; 
	
	Line *oldLastLine = g_script.mLastLine;
	
	if (aIgnoreLoadFailure > 1)  // if third param is > 1, reset all functions, labels, remove hotkeys
	{
		g_script.mFuncCount = 0;   
		g_script.mFirstLabel = NULL ; 
		g_script.mLastLabel = NULL ; 
		g_script.mLastFunc = NULL ; 
	    g_script.mFirstLine = NULL ; 
		g_script.mLastLine = NULL ;
		 g_script.mCurrLine = NULL ; 

		if (filesAdded == 0)
			{
			SimpleHeap::sBlockCount = 0 ;
			SimpleHeap::sFirst = NULL;
			SimpleHeap::sLast  = NULL;
			SimpleHeap::sMostRecentlyAllocated = NULL;
			}
		if (filesAdded > 0)
			{
			// Naveen v9 free simpleheap memory for late include files
			SimpleHeap *next, *curr;
			for (curr = SimpleHeap::sFirst; curr != NULL;)
				{
				next = curr->mNextBlock;  // Save this member's value prior to deleting the object.
				curr->~SimpleHeap() ;
				curr = next;
				}
			SimpleHeap::sBlockCount = 0 ;
			SimpleHeap::sFirst = NULL;
			SimpleHeap::sLast  = NULL;
			SimpleHeap::sMostRecentlyAllocated = NULL;
/*  Naveen: the following is causing a memory leak in the exe version of clearing the simple heap v10
 g_script.mVar = NULL ; 
 g_script.mVarCount = 0 ; 
 g_script.mVarCountMax = 0 ; 
 g_script.mLazyVar = NULL ; 

 g_script.mLazyVarCount = 0 ; 
*/
		}
	
	g_script.LoadIncludedFile(fileName, aAllowDuplicateInclude, (bool) aIgnoreLoadFailure);
	g_script.PreparseBlocks(g_script.mFirstLine); // 
//	g_script.mFirstLine->ExecUntil(UNTIL_RETURN); // Might never return (e.g. infinite loop or ExitApp).
			filesAdded += 1;
	}
	else
	{
	g_script.LoadIncludedFile(fileName, aAllowDuplicateInclude, (bool) aIgnoreLoadFailure);
	g_script.PreparseBlocks(oldLastLine->mNextLine); // 
	}
	return (unsigned int) oldLastLine->mNextLine;  // 
}
testscripts: hostscript
start:
ahkdll := DllCall("LoadLibrary", "str", A_ScriptDir . "\AutoHotkey.dll")
sleep, 500
threadH := DllCall(A_ScriptDir . "\AutoHotkey.dll\ahkdll", "str", "cleanhostdll.ahk", "str"
, "", "str", "parameter1 parameter2", "Cdecl Int") 
DllCall(A_ScriptDir . "\AutoHotkey.dll\addFile", "str", "clean.ahk", "uchar", 1
,"uchar" , 2, "Cdecl UInt")
msgbox hostcleanexe
gosub stress
return

stress:
loop, 2
{
loop, 1000
{
DllCall(A_ScriptDir . "\AutoHotkey.dll\addFile", "str", "clean.ahk", "uchar", 1
,"uchar" , 2, "Cdecl UInt")
DllCall(A_ScriptDir . "\AutoHotkey.dll\addFile", "str", "clean2.ahk", "uchar", 1
,"uchar" , 2, "Cdecl UInt")
}
msgbox 1000  
}
  return

F3::
DllCall(A_ScriptDir . "\AutoHotkey.dll\addFile", "str", "clean.ahk", "uchar", 1
,"uchar" , 2, "Cdecl UInt")
msgbox hostcleanexe clean
return

F4::
DllCall(A_ScriptDir . "\AutoHotkey.dll\addFile", "str", "clean2.ahk", "uchar", 1
,"uchar" , 2, "Cdecl UInt")
msgbox hostcleanexe clean2
return

!r::
Reload

!q::
ExitApp
client scripts:
#Persistent
return

file1:
x := "second"
x = test
x = another
return

fxclean()
{
msgbox clean
x = 3
}
2nd client script
#Persistent
return

file1:
x := "second"
x = test
x = another
return

fxclean2()
{
msgbox clean2
x = 3
}


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

...
2. updated addfile(), so when called with 2 as the 3rd parameter.
For ex. addfile("somefile.ahk", 1, 2)
simpleheap memory used by late included files upto this point is freed. Hotkeys, labels, and functions from the first call to addfile are retained.
If you want to create hotkeys in later include files that have already been defined, use a dynamic function call with the Hotkey command:
Hotkey, label, hotkey


Hi tinku99, that is great, thanks for update.
Btw. what happens to hotkeys + directives + OnMessage... from previous script? Are these also deactivated/disabled?

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

what happens to hotkeys + directives + OnMessage... from previous script? Are these also deactivated/disabled?

Directives will stay.
Hotkeys and OnMessage: Depends what they point to. If its to the first script loaded with ahkdll(), then they will work.
You can manually replace Hotkeys with the Hotkey command.
OnMessage can also be dynamically unhooked I believe.

If they point to code loaded with addfile(), hotkeys will fail, and OnMessage will crash the script, because it will try to call a function that doesn't exist anymore.

I will probably add the option to run an autoexecute section at the top of each include file loaded with addfile...

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

aResultToken.symbol = SYM_INTEGER ;
aResultToken.marker = "";

:shock:
marker is for SYM_STRING or SYM_OPERAND. Also, aResultToken.symbol is already set to SYM_INTEGER before the built-in function is called. Anyway, that function will never return early so there's no need to set any defaults.

// A separate buf because aResultToken.buf is sometimes used to store the result.

It is sometimes used by the built-in function, but in this case you are not using it at all. If there's any risk that aResultToken.buf will be overwritten, you can copy the pointer to a local variable (the buffer itself exists in the stack of the caller). However, since there shouldn't be any need to support SYM_INTEGER / SYM_FLOAT inputs, you may as well use NULL in place of the buf. TokenToString would then return an empty string if the input is not a string (or var).

If they point to code loaded with addfile(), hotkeys will fail, and OnMessage will crash the script, because it will try to call a function that doesn't exist anymore.

It shouldn't be difficult to iterate through the functions and labels which are about to be deleted, and remove any associated hotkeys/message monitors (or to remove all hotkeys/message monitors). Hotkeys are kept in Hotkey::shk[], hotkey->mFirstVariant points to the first variant in the linked list, and each variant's mJumpToLabel points to... well, you know. g_MsgMonitor points to an array of MsgMonitorStruct; presumably each one's 'func' field contains a pointer to the function which will be called if message 'msg' is received.

HotKeyIt
  • Moderators
  • 7439 posts
  • Last active: Jun 22 2016 09:14 PM
  • Joined: 18 Jun 2008
Thanks for info, would be great if everything would get removed / deleted automatically, so new script will start off fresh.

Another question, would it be possible to addfile if thread (Autohotkey.dll) was terminated ( DllCall("TerminateThread",.. or DllCall("ExitThread",...) ) or if it hangs / crashed, so any loaded dll could be reused, that would be awsome.

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

Looks like I cannot understand how to use addfile, could you possibly give some more information about parameters and functionality?

I thought the old script will be replaced by the new one but it looks to be not the case.

So when I run following script using ahkdll:
lib:=DllCall("LoadLibrary","str",A_ScriptDir . "\AutoHotkey.dll")
thread_ := DllCall(A_ScriptDir . "\AutoHotkey.dll\ahkdll", "str", A_ScriptDir . "\test.ahk", "str", "", "str", parameters, "Cdecl Int")
MsgBox % thread_
DllCall(A_ScriptDir . "\AutoHotkey.dll\addFile", "str", A_ScriptDir . "\test1.ahk", "uchar", 1,"uchar" , 2, "Cdecl UInt")
MsgBox
Return
This script is loaded:
#Persistent
SetTimer, test
Return
test:
ToolTip % A_Now
Return
And addfile following:
#Persistent
SetTimer, test1
Return
test1:
ToolTip % A_TickCount
Return

Old script is still running.

Am I doing something wrong?

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

Old script is still running.

Am I doing something wrong?

The first script is special, it is not removed. If you load a 3rd script, the 2nd one will be replaced. Same with 4th, 5th etc... they will be removed. The first one will stay persistent.
If you don't need a first script, just load a dummy script with a single line: #Persistent.

But the first one is usefull because you can use it for utility functions and hotkeys, that will be common to all client scripts.

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

#Persistent
SetTimer, test1
Return
test1:
ToolTip % A_TickCount
Return

By the way, if you try to replace this script without first stopping the timer, it will probably crash.

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

I've got some questions regarding AutoHotkey.dll.

As far as I understand, loaded file will not be executed straight away, right?

So the only way to access an added file is to create a subroutine in host script that will check if a function or label exist (via IsFunc or IsLabel) and launch it afterwards.
Working example of host script (first loaded file):
#Persistent
Hotkey,1,test
Return
test:
	If IsLabel(label:="test")
		Gosub %label%
Return
or
#Persistent
Loop {
	If IsLabel(label:="test")
		Gosub %label%
}

I think it would be much better if the script would get executed straight away (at least optionally).

Also I do not think keeping host script running after addfile is necessary for AutoHotkey.dll, I think all code should be removed. It would be much better if we could have more control over the thread from the app that launched it, like killing and restarting it as well as launching a function or label.
For example something like:
DllCall("AutoHotkey.dll\ExecFunc","str","test",...)
DllCall("AutoHotkey.dll\ExecLabel","str","test",...)

What do you think?

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

It shouldn't be difficult to iterate through the functions and labels which are about to be deleted, and remove any associated hotkeys/message monitors (or to remove all hotkeys/message monitors). Hotkeys are kept in Hotkey::shk[], hotkey->mFirstVariant points to the first variant in the linked list, and each variant's mJumpToLabel points to... well, you know. g_MsgMonitor points to an array of MsgMonitorStruct; presumably each one's 'func' field contains a pointer to the function which will be called if message 'msg' is received.

A simple matter of programming. :)

Removing and adding variants from the host application is on my todo list.
Basically I plan to export variations of the Hotkey command.

Will look in to msgmonitor and possibly exporting the OnMessage()function.

I think it would be much better if the script would get executed straight away (at least optionally).

DllCall("AutoHotkey.dll\ExecFunc","str","test",...) 
DllCall("AutoHotkey.dll\ExecLabel","str","test",...)

Will add these features in the next release.

When the thread gets killed/terminated or you call "DllCall("ExitThread",..)", what is left there? ...
I have experimented with FreeLibrary, it all looks to work fine, the memory decreases but after LoadLibrary again (even another file) it gets same library pointer and this message RegClass appears....

As Lexikos pointed out a while back on this thread, terminating the thread or freeing the library without cleanup is problematic.
Also, freeing the library and reloading it is inefficient and unnecessary for any purpose that i can think of.
> if it hangs / crashed, any loaded dll could be reused, that would be awsome.
Real programmers get it right the first time. :wink:

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

So in next version, when addfile is called for Autohotkey.dll, it will replace the host script as well and new script will get executed?

As Lexikos pointed out a while back on this thread, terminating the thread or freeing the library without cleanup is problematic.
Also, freeing the library and reloading it is inefficient and unnecessary for any purpose that i can think of.
> if it hangs / crashed, any loaded dll could be reused, that would be awsome.
Real programmers get it right the first time. ;)


So I have to wait until "real programmers" get same request/requirement :?:

Some background why I need it especially:
When I launch "Loop, filepattern" (expecially on a network drive), it could take very long time to finish that operation and there is only one way to stop that loop, kill the thread/process.
There are some other commands that could get your script hang for some time and you would like to kill it.

Does AutoHotkey a cleanup on ExitApp? If so, then we only need to change ExitApp to ExitThread and it could work, right?

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

Hi tinku99,
So in next version, when addfile is called for Autohotkey.dll, it will replace the host script as well and new script will get executed?
So I have to wait until "real programmers" get same request/requirement :?:

1. New script will be executed. Have not decided on clearing the first script. The first script can be blank anyway, so there is nothing to replace. (Its not really a host script, the host is outside the dll, my mistake in terminology earlier.
2. lol, nice comeback. sorry I had wasted some time on this when I first created the dll.

"[Loop, filepattern" and] some other commands could get your script hang for some time.

This could be a more general question for autohotkey. Consider creating a new discussion thread for this.

Does AutoHotkey a cleanup on ExitApp? If so, then we only need to change ExitApp to ExitThread and it could work, right?

Hmm, Maybe if we kill the ahkdll thread and clear the green threads also, we could reenter at MsgSleep(SLEEP_INTERVAL, WAIT_FOR_MESSAGES);
Will try it...

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

Hmm, Maybe if we kill the ahkdll thread and clear the green threads also, we could reenter at MsgSleep(SLEEP_INTERVAL, WAIT_FOR_MESSAGES);
Will try it...


I'll keep my fingers crossed :D

tinku99
  • Members
  • 560 posts
  • Last active: Feb 08 2015 12:54 AM
  • Joined: 03 Aug 2007
ok, I enabled autoexecute for the top of the replacement scripts.
you can execute labels from outside now.
Functions don't take any parameters and don't return anything yet, so they are not much better than labels. Will work on it.
DllCall(A_ScriptDir . "\AutoHotkey.dll\ahkLabel", "str", "labelname", "Cdecl UInt")
DllCall(A_ScriptDir . "\AutoHotkey.dll\ahkFunction", "str", "funcname", "str", result, "Cdecl UInt")
download

killing the thread and trying to enter at msgsleep didn't simply work.
Killing the thread destroys the main gui. Its difficult to separate out which resources have to be reinitialized and which can be reused. Also, its complicated to pass on a mixture of global, static, and class variables through threads.
sorry. will try again after a while.

HotKeyIt
  • Moderators
  • 7439 posts
  • Last active: Jun 22 2016 09:14 PM
  • Joined: 18 Jun 2008
That's great tinku99, thank you so much, I will try as soon as I can.

I am really curious about killing the thread. I hope it'll work.