Call the v2 library in v1

Post your working scripts, libraries and tools for AHK v1.1 and older
User avatar
thqby
Posts: 402
Joined: 16 Apr 2021, 11:18
Contact:

Call the v2 library in v1

Post by thqby » 16 Jan 2022, 00:00

Modify from lexikos' loadfile

Use autohotkey.exe or autohotkey.dll of v2 to load the library files, and returns an object which can be used as follows:

Code: Select all

; load v2 librarys
lib := import_v2lib("LibPath", "AHK.exe")
lib := import_v2lib(["Lib1Path", "Lib2Path"], "AHK.exe")
lib := import_v2lib("test(a){`n}", "AHK.dll")

; Call a function named 'Function' in the target library:
lib.Function.Call(params)
lib.Function.Call

; Retrieve a global variable:
value := lib["VarName"]
value := lib.VarName

; Set a global variable:
lib["VarName"] := value
lib.VarName := value

; Create a class instance
value := lib["ClassName"].Call(params)
value := lib.ClassName.NestedClass.Call(params)

; set __Item prop `a := [[1]]`
lib.SetItem(lib.a[1], 22222, 1) ; a[1][1] := 22222

; get __Item prop
lib.GetItem(lib.a[1], 1)	; 22222
All of the required functions are expected to be installed in a function library.

Code: Select all

/**
/**
 * import ahk v2 library to v1. It can call v2 functions and classes, read and modify global variables.
 * 
 * - import v2 lib `v2 := import_v2lib("gdip_all.ahk", "ahkv2.exe")`
 * - create a class instance `v2["classname"].Call(params)` or `v2.classname.Call(params)`
 * - call a functions `v2.funcname.Call(params)`
 * - set a global variable `v2.varname := "str"` or `v2["varname"] := {}`
 * - retrieve a global variable `value := v2["varname"]` or `value := v2.varname`
 * - set __Item prop `v2.SetItem(v2object, value, param1 ...)`
 * - get __Item prop `v2.GetItem(v2object, param1 ...)`
 * 
 * @param pathsorcode The paths of the include script files or ahk v2 code. `Array or String`
 * @param ahkv2runtime The path of the AutoHotkey v2 runtime (AutoHotkey.exe or AutoHotkey.dll).
 */
import_v2lib(pathsorcode, ahkv2runtime) {
	static IDispatch, _ := (VarSetCapacity(IDispatch, 16), NumPut(0x46000000000000c0, NumPut(0x20400, IDispatch, "int64"), "int64"))
	lresult := DllCall("oleacc\LresultFromObject", "ptr", &IDispatch, "ptr", 0, "ptr", &(client := { proxy: 0 }), "ptr")
	if !FileExist(ahkv2runtime) {
		; free com marshal on fail
		DllCall("oleacc\ObjectFromLresult", "ptr", lresult, "ptr", &IDispatch, "ptr", 0, "ptr*", idsp := 0), ObjRelease(idisp)
		throw Exception("AutoHotkey v2 runtime isn't exist")
	}
	if IsObject(pathsorcode) {
		t := pathsorcode, pathsorcode := ""
		for i, p in t
			pathsorcode .= "`n#include " p
	}
	v2script := Format("
	(
		#NoTrayIcon
		class __AHKv2LibHelper {
			static __New() {
				Persistent
				NumPut('int64', 0x20400, 'int64', 0x46000000000000c0, IDispatch := Buffer(16))
				DllCall('oleacc\ObjectFromLresult', 'ptr', {}, 'ptr', IDispatch, 'ptr', 0, 'ptr*', &idisp := 0)
				ComObjFromPtr(idisp).proxy := this()
			}
			__Get(name, params) => params.Length ? %name%[params*] : %name%
			__Set(name, params, value) {
				global
				if params.Length
					%name%[params*] := value
				else %name% := value
			}
			GetItem(obj, params*) => obj[params*]
			SetItem(obj, value, params*) => obj[params*] := value
			__Delete() => SetTimer(ExitApp, -1)
		}
		{}
	)", lresult, FileExist(pathsorcode) ? "#include " pathsorcode : pathsorcode)
	if (ahkv2runtime ~= "i)\.dll$") {
		if (!DllCall("GetModuleHandle", "str", ahkv2runtime) && !DllCall("LoadLibrary", "str", ahkv2runtime)) {
			DllCall("oleacc\ObjectFromLresult", "ptr", lresult, "ptr", &IDispatch, "ptr", 0, "ptr*", idsp := 0), ObjRelease(idisp)
			throw Exception("load AutoHotkey.dll fail")
		}
		if !(threadID := DllCall(ahkv2runtime "\NewThread", "str", v2script, "str", "", "str", "", "cdecl uint")) {
			DllCall("oleacc\ObjectFromLresult", "ptr", lresult, "ptr", &IDispatch, "ptr", 0, "ptr*", idsp := 0), ObjRelease(idisp)
			throw Exception("Failed to load script")
		}
		while !client.proxy
			Sleep 10
	} else {
		exec := ComObjCreate("WScript.Shell").Exec("""" ahkv2runtime """ /ErrorStdOut *")
		exec.StdIn.Write(v2script), exec.StdIn.Close()
		while (exec.Status = 0 && !client.proxy)
			Sleep 10
		if (exec.Status != 0) {
			ex := Exception("Failed to load script")
			if (RegExMatch(err := exec.StdErr.ReadAll(), "Os)(.*?) \((\d+)\) : ==> (.*?)(?:\s*Specifically: (.*?))?\R?$", m))
				ex.Message .= "`n`nReason:`t" m[3] "`nLine text:`t" m[4] "`nFile:`t" m[1] "`nLine:`t" m[2]
			DllCall("oleacc\ObjectFromLresult", "ptr", lresult, "ptr", &IDispatch, "ptr", 0, "ptr*", idsp := 0), ObjRelease(idisp)
			throw ex
		}
	}
	return client.proxy
}

amateur+
Posts: 655
Joined: 09 Oct 2021, 15:43

Re: Call the v2 library in v1

Post by amateur+ » 17 Jan 2022, 18:53

So could I use ThisFunctionHasTheCodeRequiringAhkV2() from %A_MyDocuments%\AutoHotkey\Lib\ in my v1 script so that the function will be executed and also able to return a value?
Have found any drawback in my code or approach? Please, point it out. /The moderator ordered to remove the rest of the signature, I had obeyed.
And I really apologize for our russian president. Being a citizen of an aggressor country is very shameful. Personally I tried to avoid this trying to defend elections from fraud being a member of the election commission of one of the precincts but only was subjected to a hooligan attack and right before the vote count was illegally escorted from the polling station and spent the night behind bars (in jail) in a result of illegal actions of corrupt policemen.

User avatar
thqby
Posts: 402
Joined: 16 Apr 2021, 11:18
Contact:

Re: Call the v2 library in v1

Post by thqby » 17 Jan 2022, 19:59

Yes, all types of return values are supported, and the objects will be converted to COM objects.

guest3456
Posts: 3454
Joined: 09 Oct 2013, 10:31

Re: Call the v2 library in v1

Post by guest3456 » 17 Jan 2022, 22:22

thqby wrote:
16 Jan 2022, 00:00
or autohotkey.dll of v2
will you be merging your ahk.dll changes back into the main AHK_H repo?


User avatar
thqby
Posts: 402
Joined: 16 Apr 2021, 11:18
Contact:

Re: Call the v2 library in v1

Post by thqby » 18 Jan 2022, 04:13

hotkeyit has merged into repo, but hasn't been released yet.
@guest3456

SOTE
Posts: 1426
Joined: 15 Jun 2015, 06:21

Re: Call the v2 library in v1

Post by SOTE » 18 Jan 2022, 08:10

Very interesting. Another good one, thqby :bravo:

dbgba
Posts: 19
Joined: 02 Apr 2021, 22:11

Re: Call the v2 library in v1

Post by dbgba » 13 Sep 2023, 09:39

Thanks to @thqby Functions, I've tweaked and added examples to Functions.
Support for ahkdll has been removed due to compatibility issues with v2 calls.

Demo.ahk

Code: Select all

#Requires AutoHotkey v1.1.33+
SetBatchLines -1
SetWorkingDir %A_ScriptDir%

v2code=
(LTrim ` %
  testFunc(a) => MsgBox(a)
  Class testClass {
    static hello(n) => MsgBox(n)
  }
)

V2Process := import_v2lib(v2code, "C:\Program Files\AutoHotkey\AutoHotkeyU64_V2H.exe")
; V2 := import_v2lib("C:\Users\Dianbo\Desktop\UIAutomation.ah2", "C:\Program Files\AutoHotkey\AutoHotkeyU64_V2H.exe")
Return


F1::V2Process.testFunc.Call((onoff := !onoff) ? "V2 Passing Parameter Test: 1234" : "V2 Passing Parameter Test: 5678")

F2::V2Process["testClass"].hello(WinExist("A"))

F3::V2Process.ToolTip.Call("V2 ToolTip: " A_TickCount)

; Modified from: https://www.autohotkey.com/boards/viewtopic.php?f=6&t=98917
import_v2lib(PathsOrCode, ahkv2runtime, Timeout:=30, v2NoTrayIcon:="#NoTrayIcon") {
	Static IDispatch, V2LibNumber := 0, _ := (VarSetCapacity(IDispatch, 16), NumPut(0x46000000000000c0, NumPut(0x20400, IDispatch, "int64"), "int64"))
	if !FileExist(ahkv2runtime)
		Throw Exception("The V2 interpreter path is set incorrectly!")
	lresult := DllCall("oleacc\LresultFromObject", "Ptr", &IDispatch, "Ptr", 0, "Ptr", &(client := { proxy: 0 }), "Ptr")
	if IsObject(PathsOrCode) {
		t := PathsOrCode, PathsOrCode := ""
		For __, p in t
			PathsOrCode .= "`n#include " p
	}

	v2script := Format("
	(
		" v2NoTrayIcon "
		class __AHKv2LibHelper {
			static __New() {
				NumPut('int64', 0x20400, 'int64', 0x46000000000000c0, IDispatch := Buffer(16))
				, DllCall('oleacc\ObjectFromLresult', 'ptr', {}, 'ptr', IDispatch, 'ptr', 0, 'ptr*', &idisp := 0)
				, ComObjFromPtr(idisp).proxy := this()
				Persistent
				this.V2LibGuiHide := Gui(, '<<V2LibGuiHide" ++V2LibNumber ">>')
			}
			__Get(name, params) => params.Length ? `%name`%[params*] : `%name`%
			__Set(name, params, value) {
				global
				if params.Length
					`%name`%[params*] := value
				else `%name`% := value
			}
			GetItem(obj, params*) => obj[params*]
			SetItem(obj, value, params*) => obj[params*] := value
			__Delete() => SetTimer(ExitApp, -1)
		}
		{}
	)", lresult, FileExist(PathsOrCode) ? "" : PathsOrCode)
	if FileExist(PathsOrCode)
		exec := ComObjCreate("WScript.Shell").Exec("""" ahkv2runtime """ /include """ PathsOrCode """ *")
	 else
		exec := ComObjCreate("WScript.Shell").Exec("""" ahkv2runtime """ /CP0 *")
	exec.StdIn.Write(v2script), exec.StdIn.Close()

	DetectHiddenWindows % ("On", DHW:=A_DetectHiddenWindows)
	Loop % Timeout*1000//15
		Sleep 15
	Until if WinExist("<<V2LibGuiHide" V2LibNumber ">> ahk_pid " exec.ProcessID)
	if !WinExist("<<V2LibGuiHide" V2LibNumber ">> ahk_pid " exec.ProcessID)
		Throw Exception("Timeout or failure to start V2 process!")
	DetectHiddenWindows %DHW%
	Sleep 10
	return client.proxy
}

Post Reply

Return to “Scripts and Functions (v1)”