101

Re: AHK: Работа с потоками autohotkey.dll

Спасибо, работает.

По вопросам возмездной помощи пишите на E-Mail: serzh82saratov@mail.ru Telegram: https://t.me/sergiol982
Win10x64 AhkSpy, Hotkey, ClockGui

102

Re: AHK: Работа с потоками autohotkey.dll

А возможно запустить в потоке анк файл как ресурс? То есть не прописывать код потока в переменной, а добавить его ресурсом.

По вопросам возмездной помощи пишите на E-Mail: serzh82saratov@mail.ru Telegram: https://t.me/sergiol982
Win10x64 AhkSpy, Hotkey, ClockGui

103

Re: AHK: Работа с потоками autohotkey.dll

Ну да, считываем ресурс в память по тому же принципу, потом превращаем в строку.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

104

Re: AHK: Работа с потоками autohotkey.dll

Получилось.

pData := LoadScriptResource(hModule, v, size)
MsgBox % StrGet(pData,size,"UTF-8")

А что за функция UnZipRawMemory, я не нашёл её в инклудах.

По вопросам возмездной помощи пишите на E-Mail: serzh82saratov@mail.ru Telegram: https://t.me/sergiol982
Win10x64 AhkSpy, Hotkey, ClockGui

105

Re: AHK: Работа с потоками autohotkey.dll

Это из AHK_H.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

106

Re: AHK: Работа с потоками autohotkey.dll

А, это для расшировки с паролем.

С BinRun никак не получается сделать универсальную версию, ERROR e_magic not found.

По вопросам возмездной помощи пишите на E-Mail: serzh82saratov@mail.ru Telegram: https://t.me/sergiol982
Win10x64 AhkSpy, Hotkey, ClockGui

107 (изменено: serzh82saratov, 2018-07-18 12:04:27)

Re: AHK: Работа с потоками autohotkey.dll

Непонятная штука, при добавлении ресурсом файла анк, с паролем требуется расшифровка с помощью UnZipRawMemory, то есть как будто ресурсы шифруются при добавлении, но тогда как Resource Hacker видит исходный текст этих ресурсов?

По вопросам возмездной помощи пишите на E-Mail: serzh82saratov@mail.ru Telegram: https://t.me/sergiol982
Win10x64 AhkSpy, Hotkey, ClockGui

108

Re: AHK: Работа с потоками autohotkey.dll

У меня видит зашифрованный. Ты про какой ресурс говоришь?

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

109

Re: AHK: Работа с потоками autohotkey.dll

serzh82saratov пишет:

при добавлении ресурсом файла анк

По вопросам возмездной помощи пишите на E-Mail: serzh82saratov@mail.ru Telegram: https://t.me/sergiol982
Win10x64 AhkSpy, Hotkey, ClockGui

110

Re: AHK: Работа с потоками autohotkey.dll

Пока не пробовал, потом проверю.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

111 (изменено: powercat, 2018-07-18 19:38:08)

Re: AHK: Работа с потоками autohotkey.dll

Раз уж тут тема про BinRun() зашла, может подскажет кто-то.
При компиляции данного скрипта, в ресурсы будет вшит file.exe, а называться ресурс будет - "C:\Users\user\Desktop\file.exe". Так вот этот ресурс нельзя переименовывать, иначе вылетает ERROR e_magic not found. Есть альтернативные способы "вшитья" файла в ресурсы, кроме FileInstall, с последующим успешным запуском?


If 0
	FileInstall, C:\Users\user\Desktop\file.exe,-
BinRun("C:\Users\user\Desktop\file.exe")

112

Re: AHK: Работа с потоками autohotkey.dll

powercat пишет:

Есть альтернативные способы "вшитья" файла в ресурсы

Ну да, есть обычный способ через winapi: Updating Resources.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

113

Re: AHK: Работа с потоками autohotkey.dll

teadrinker
Получилось добавить ресурс, но BinRun() выдает ошибку e_magic. Видимо FileInstall и BinRun как-то зависят друг от друга. Отдельно не работает.. Как-то сложно все с этим ahk_h.

114

Re: AHK: Работа с потоками autohotkey.dll

powercat, ничего не могу сказать, пока не увижу ваш код, как вы считываете ресурс, и как используете.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

115 (изменено: KusochekDobra, 2020-03-30 12:31:45)

Re: AHK: Работа с потоками autohotkey.dll

Всем добра!

По мотивам интересного материала.
Простая обёртка классом, для большего удобства запуска параллельных задач. Упрощает навигацию данных коммутируемого объекта и заботится о жизненном цикле подключаемых сценариев:


Class ScriptToProcess
{
	__New() {
		this.counter := 0
		this.GUID    := CreateGUID()
		this.SERVER  := {GLOBAL_STORAGE: []}
		ObjRegisterActive(this.SERVER, this.GUID)
	}
	
	; ============================================================================================================
	;	Добавляет и запускает скрипт.
	;	script		- ( string ) текст сценария или путь до файла со сценарием, который будет запущен.
	;	pseudonym	- ( string ) псевдоним, через который будет доступен коммутируемый объект. По умолчанию
	;					использует внутренний счётчик. Если псевдоним уже используется, к нему
	;					конкатенируется текущее значение внутреннего счётчика, приращивающегося
	;					с каждым добавленным сценарием.
	;	persist		- ( bool ) добавляет "#Persistent", заставляя сценарий быть постоянно активным.
	;					По умолчанию "false".
	;	tray_icon	- ( int[0, 1]/string ) выключает или задаёт отображение иконки в трее.
	;					Допустимые значения:
	;						0 - отключить показ иконки.
	;						1 - включить показ иконки.
	;						( string ) - содержит путь к файлу, или shell32.dll с номером.
	;							(например - "shell32.dll, 110"). По умолчанию 1.
	;	libs		- ( Array(string) ) массив строк с путями к подключаемым модулям.
	;	callbacks	- ( Array(Func()) ) последний аргумент принимает любое количество коллбэков, которые
	;					затем можно вызывать в том же порядке, используя глобальную константу "SELF".
	;						(например, первый коллбэк - SELF.callbacks[1].Call( data )).
	;	
	;	В добавленные к выполнению таким образом сценарии, ВСЕГДА предустанавливаются ряд глобальных
	;		констант, а так же функция завершения. А именно:
	;	
	;	PSEUDONYM		- ( string ) строка, представляющая псевдоним конкретно этого контекста.
	;	SERVER			- ( ComObject ) глобальный коммутируемый объект, через который доступны все
	;						добавленные сценарии.
	;	GLOBAL_STORAGE	- ( ComObject ) глобальное хранилище, доступное каждому добавленному сценарию.
	;	SELF			- ( ComObject ) часть глобального коммутируемого объекта, относящаяся
	;						непосредственно к данным, оперируемым текущим контекстом. Через неё доступны
	;						поля:
	;							done		- ( Func() ) содержащее ссылку на коллбэк, завершающий работу
	;											текущего контекста.
	;							data		- ( Array() ) массив. Размещайте здесь данные, предназначенные
	;											только для текущего контекста.
	;							callbacks	- ( Array(Func()) ) массив коллбэков. Функции вызывающего
	;											сценария, вызываемые из текущего контекста.
	;	_Done_			- ( Func() ) функция, содержащая единственную инструкцию - "ExitApp".
	; ============================================================================================================
	Add(script, pseudonym := "", persist := false, tray_icon := 1, libs := "", callbacks*) {
		
		if (FileExist(script))
			FileRead, script,% script
		
		this.counter += 1
		if (!pseudonym)
			pseudonym := this.counter
		else if (this.SERVER.HasKey( pseudonym ))
			pseudonym .= this.counter
		this.SERVER[ pseudonym ] := {done: "", data: [], callbacks: callbacks}
		
		add_content := ""
		add_content .= "#NoEnv`r`n"
		add_content .= persist ? "#Persistent`r`n" : ""
		add_content .= tray_icon == 1 ? "" : "Menu, Tray, " . (tray_icon == 0 ? "NoIcon" : "Icon, " . tray_icon) . "`r`n"
		add_content .= "Global PSEUDONYM := """ . pseudonym . """`r`n"
		add_content .= "Global SERVER := ComObjActive(""" . this.GUID . """)`r`n"
		add_content .= "Global GLOBAL_STORAGE := SERVER[ ""GLOBAL_STORAGE"" ]`r`n"
		add_content .= "Global SELF := SERVER[ PSEUDONYM ]`r`n"
		add_content .= "SELF.done := Func(""_Done_"")`r`n"
		add_content .= "_Done_() {`r`n	ExitApp`r`n}`r`n"
		
		lib_content := "`r`n"
		For i, lib in libs {
			if (FileExist(lib))
				FileRead, lib,% lib
			lib_content .= lib . "`r`n"
		}
		
		script := add_content . script . lib_content
		
		DynaRun(script)
	}
	
	; ============================================================================================================
	;	Удаляет и завершает выполнение добавленного сценария.
	;	pseudonym	- псевдоним сценария, выполнение которого необходимо завершить.
	; ============================================================================================================
	Remove(pseudonym) {
		if (pseudonym != "GLOBAL_STORAGE" && this.SERVER.HasKey( pseudonym )) {
			Try
				this.SERVER[ pseudonym ].done.Call()
			this.SERVER.Delete( pseudonym )
		}
	}
	
	; ============================================================================================================
	;	Завершает все активные сценарии. Вызывается автоматически при удалении экземпляра
	;		этого класса.
	; ============================================================================================================
	RemoveAll() {
		tasks := []
		For key in this.SERVER
			tasks.Push( key )
		For i, task in tasks
			this.Remove( task )
	}
	
	__Get(name) {
		return this.SERVER[ name ]
	}
	
	__Delete() {
		this.RemoveAll()
		ObjRegisterActive(this.SERVER, "")
	}
}

ObjRegisterActive(Object, CLSID, Flags := 0) {
	static cookieJar := {}
	if !CLSID {
		if (( cookie := cookieJar.Delete(Object) ) != "")
			DllCall("oleaut32\RevokeActiveObject", UInt, cookie, Ptr, 0)
		return
	}
	if cookieJar[Object]
		throw Exception("Object is already registered", -1)
	VarSetCapacity(_clsid, 16, 0)
	if (hr := DllCall("ole32\CLSIDFromString", WStr, CLSID, Ptr, &_clsid)) < 0
		throw Exception("Invalid CLSID", -1, CLSID)
	hr := DllCall("oleaut32\RegisterActiveObject", Ptr, &Object, Ptr, &_clsid, UInt, Flags, UIntP, cookie, UInt)
	if hr < 0
		throw Exception(format("Error 0x{:x}", hr), -1)
	cookieJar[Object] := cookie
}

CreateGUID()
{
	VarSetCapacity(pguid, 16, 0)
	if !DllCall("ole32.dll\CoCreateGuid", Ptr, &pguid)  {
		size := VarSetCapacity(sguid, (38 << !!A_IsUnicode) + 1, 0)
		if DllCall("ole32.dll\StringFromGUID2", Ptr, &pguid, Ptr, &sguid, UInt, size)
			return StrGet(&sguid)
	}
}

DynaRun(tempScript, ahkPath := "", pipeName := "", args*)
{
	static params := [ "UInt", PIPE_ACCESS_OUTBOUND     := 0x2, "UInt", 0
						, "UInt", PIPE_UNLIMITED_INSTANCES := 255, "UInt", 0
						, "UInt", 0, "Ptr", 0, "Ptr", 0, "Ptr" ]
		, BOM := Chr(0xFEFF)

	(ahkPath = "" && ahkPath := A_AhkPath)
	(pipeName = "" && pipeName := "AHK_" . A_TickCount)
	
	Loop 1 {
		for k, v in ["pipeGA", "pipe"]
			%v% := DllCall("CreateNamedPipe", Str, "\\.\pipe\" . pipeName, params*)
		if ( (pipe = -1 || pipeGA = -1) && error := "Can't create pipe """ . pipeName . """`nLastError: " . A_LastError )
			break
		sCmd := ahkPath . " ""\\.\pipe\" . pipeName . """"
		for k, v in args
			sCmd .= " """ . v . """"
		Run, % sCmd,, UseErrorLevel HIDE, PID
		if (ErrorLevel && error := "Can't open file:`n ""\\.\pipe\" . pipeName . """")
			break
		for k, v in ["pipeGA", "pipe"]
			DllCall("ConnectNamedPipe", Ptr, %v%, Ptr, 0)
		tempScript := BOM . tempScript
		tempScriptSize := ( StrLen(tempScript) + 1 ) << !!A_IsUnicode
		if !DllCall("WriteFile", Ptr, pipe, Str, tempScript, UInt, tempScriptSize, UIntP, 0, Ptr, 0)
			error := "WriteFile failed, LastError: " . A_LastError
	}
	for k, v in ["pipeGA", "pipe"]
		( %v% != -1 && DllCall("CloseHandle", Ptr, %v%) )
	if error
		throw Exception(error)
	Return PID
}

#NoEnv
#SingleInstance, Force

SetWorkingDir,% A_ScriptDir
ListLines, Off
SetBatchLines, -1
OnExit, BeforeExit
SplitPath,% A_ScriptFullPath,,,, fNameNoExt

; >>--++--<<  >>--++--<<
	#Include ScriptToProcess.ahk
; >>--++--<<  >>--++--<<
	ww := 400, hh := 100
; >>--++--<<  >>--++--<<
	INI := "Config.ini"
	if (!FileExist(INI))
		FileAppend,,% INI,UTF-16
		
	IniRead, x_pos,% INI, Main, x_pos,% A_ScreenWidth // 2 - ww // 2
	IniRead, y_pos,% INI, Main, y_pos,% A_ScreenHeight // 2 - hh // 2
; >>--++--<<  >>--++--<<

	Gui, 1: +HWND_main_h
	Gui, 1: Margin, 10, 10

	Gui, 1: Add, Edit, xm y+5 w%ww% r5 v_myEdit
	Gui, 1: Add, Button, xm y+5 w%ww% gDoSomething,Кнопка

	Gui, 1: Show,x%x_pos% y%y_pos%,% fNameNoExt

; >>--++--<< script >>--++--<<
task =
(%
data := SELF.data

interval := -1
SetTimer, CheckMyData,% interval

Return

CheckMyData:
	if (data.Count()) {
		; MsgBox,% data.RemoveAt(1)
		SELF.callbacks[1].Call(Format("'{}' from CallBack name => '{}'", data.RemoveAt(1), SELF.callbacks[1].name ))
		SetTimer, CheckMyData,% interval := -1
	} else
		SetTimer, CheckMyData,% interval > -250 ? (interval -= 1) : interval
Return
)
; >>--++--<< script >>--++--<<
include_libs := []

tasks := new ScriptToProcess()
; tasks.Add("test.ahk", "helper", 1, "shell32.dll, 110", include_libs, Func("CallBackFunction"))
tasks.Add(task, "helper", 1, "shell32.dll, 110", include_libs, Func("CallBackFunction") )
helper_data := tasks[ "helper" ].data

Return

CallBackFunction(data) {
	MsgBox,% data
}
	
DoSomething:
	Gui, 1: Submit, NoHide
	helper_data.Push(_myEdit)
Return

BeforeExit:
	
	WinGetPos, xx, yy, , , ahk_id%_main_h%
	Gui, 1: Submit
	
	if (xx != x_pos && xx > 0)
		IniWrite,% xx,% INI, Main, x_pos
	if (yy != y_pos && yy > 0)
		IniWrite,% yy,% INI, Main, y_pos
	
	if (A_ExitReason) {
		ExitApp
	}
return

GuiClose:
	ExitApp

В примере, основной сценарий помещает некоторые данные из поля Edit в data коммутируемого объекта, а подключаемый, ожидает эти данные, чтобы передать их обратно основному сценарию в коллбэке.

30.03.2020 UPD: Теперь позволяет подключать модули(#Include), полученные в виде массива строк с путями к ним, из параметра "libs".

116

Re: AHK: Работа с потоками autohotkey.dll

KusochekDobra, приветствую!
Нужно иметь в виду, что подбный ExecScript() не поддерживает юникод. Сравните:

ExecScript("MsgBox,, ExecScript, ♪")
DynaRun("MsgBox,, DynaRun, ♪")

ExecScript(script)
{
   shell := ComObjCreate("WScript.Shell")
   exec := shell.Exec("AutoHotkey.exe /ErrorStdOut *")
   exec.StdIn.Write(script)
   exec.StdIn.Close()
   Return exec.ProcessID
}

DynaRun(tempScript, ahkPath := "", pipeName := "", args*)
{
   static params := [ "UInt", PIPE_ACCESS_OUTBOUND     := 0x2, "UInt", 0
                    , "UInt", PIPE_UNLIMITED_INSTANCES := 255, "UInt", 0
                    , "UInt", 0, "Ptr", 0, "Ptr", 0, "Ptr" ]
        , BOM := Chr(0xFEFF)

   (ahkPath = "" && ahkPath := A_AhkPath)
   (pipeName = "" && pipeName := "AHK_" . A_TickCount)
   
   Loop 1 {
      for k, v in ["pipeGA", "pipe"]
         %v% := DllCall("CreateNamedPipe", Str, "\\.\pipe\" . pipeName, params*)
      if ( (pipe = -1 || pipeGA = -1) && error := "Can't create pipe """ . pipeName . """`nLastError: " . A_LastError )
         break
      sCmd := ahkPath . " ""\\.\pipe\" . pipeName . """"
      for k, v in args
         sCmd .= " """ . v . """"
      Run, % sCmd,, UseErrorLevel HIDE, PID
      if (ErrorLevel && error := "Can't open file:`n ""\\.\pipe\" . pipeName . """")
         break
      for k, v in ["pipeGA", "pipe"]
         DllCall("ConnectNamedPipe", Ptr, %v%, Ptr, 0)
      tempScript := BOM . tempScript
      tempScriptSize := ( StrLen(tempScript) + 1 ) << !!A_IsUnicode
      if !DllCall("WriteFile", Ptr, pipe, Str, tempScript, UInt, tempScriptSize, UIntP, 0, Ptr, 0)
         error := "WriteFile failed, LastError: " . A_LastError
   }
   for k, v in ["pipeGA", "pipe"]
      ( %v% != -1 && DllCall("CloseHandle", Ptr, %v%) )
   if error
      throw Exception(error)
   Return PID
}
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

117

Re: AHK: Работа с потоками autohotkey.dll

teadrinker, благодарю, обновил.

118

Re: AHK: Работа с потоками autohotkey.dll

Не считая возможности обмениваться сообщениями между скриптами — на сегодня можно обходиться без autohotkey.dll для описанного функционала?

119

Re: AHK: Работа с потоками autohotkey.dll

Какого именно?

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

120 (изменено: DD, 2021-01-21 22:02:43)

Re: AHK: Работа с потоками autohotkey.dll

Работы с потоками. Вначале описывается, как с помощью autohotkey.dll настроить многопоточность.

121

Re: AHK: Работа с потоками autohotkey.dll

На сегодняшний день в этом плане ничего не изменилось.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

122

Re: AHK: Работа с потоками autohotkey.dll

teadrinker, у кода из 100 поста не работает terminate.

RunNewThreadWithAutoHotkeyDll("#persistent", A_IsCompiled ? "" : A_PtrSize = 8 ? ahkDll64 : ahkDll32)
sleep 1000
RunNewThreadWithAutoHotkeyDll()

123

Re: AHK: Работа с потоками autohotkey.dll

Там первый параметр обязательный, нужно RunNewThreadWithAutoHotkeyDll(""). У меня завершение ветки происходит, но, правда, при этом основная ветка тоже крашится. Может, от версии зависит, сейчас проверил на AHK_H\v1.1.32.00.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

124

Re: AHK: Работа с потоками autohotkey.dll

Да, я про это и говорю.
При этом такой пример работает:

DllCall(MemLib.GetProcAddress("ahktextdll"),"Str","~lbutton up::msgbox","Str","","Str","") ; start first AutoHotkey.dll thread
Sleep 1000
MsgBox,0, AutoHotkey.dll started, press OK to terminate.
DllCall(MemLib.GetProcAddress("ahkterminate")),     	MemLib.Free()
MsgBox AutoHotkey.dll threads terminated and memory freed

125

Re: AHK: Работа с потоками autohotkey.dll

Если TerminateThread() так сделать

TerminateThread(MemLib) {
   DllCall(MemLib.GetProcAddress("ahkterminate")), MemLib.Free()
}

то тоже нормально работает, но только для одной ветки, для большего количества начинает ошибки из _Struct.ahk выдавать. Скорее всего, баги в библиотеках. Я как-то пытался связаться с HotKeyIt по поводу другого бага, но безуспешно, и бросил использовать эту конструкцию в реальных проектах.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder