Orion9

|
Posted: Tue May 26, 2026 14:04 Post subject: |
|
|
AkulaBig
Не ради спора или попыток что-то доказать, а исключительно из любви к Autorun )
 Hidden text Win+[ запуск exiftool для каждого файла. Win+] держит процесс открытым и взаимодействует с ним через файл аргументов. Win+[ взаимодействует с процессом через stdin и stdout. Во временном каталоге создаются файлы exif_0, exif_1 и exif_2 с выхлопами каждого режима. Выхлопы должны совпадать за исключением метки (тега) последнего времени доступа и маркера {ready} для последнего режима.
| Code: | SetHotkeyAction /K:W /V:219 TestExiftoolSpeed 0
SetHotkeyAction /K:W /V:221 TestExiftoolSpeed 1
SetHotkeyAction /K:W /V:220 TestExiftoolSpeed 2
Static sExif = COMMANDER_PATH & "\Plugins\wlx\ExifToolView\exiftool.exe"
Static sExifOut = TEMP & "\exiftool_out.txt", sExifArgs = TEMP & "\exiftool_args.txt"
Static hChildStdInWr = 0
Static hChildStdOutRd = 0
Static hChildStdInRd = 0
Static hChildStdOutWr = 0
Static hProcess = 0
Static hThread = 0
#TestExiftoolSpeed(3)
Func TestExiftoolSpeed(StayOpen)
Local sFile = "", j, files = 0, str
Local sPath = RequestCopyDataInfo("SP")
Local sName = RequestCopyDataInfo("SN")
Static aSel = List()
aSel.Count = 0
aSel.Text = GetSelectedItems(3, 0)
If aSel.Count = 0 Then aSel.Add(sName)
If StayOpen = 1 And Not ProcessExist("exiftool.exe") Then
DllCall("kernel32.dll\DeleteFileW", "Wstr", sExifOut)
DllCall("kernel32.dll\DeleteFileW", "Wstr", sExifArgs)
FileWrite(sExifArgs, "", "UTF-8 NOBOM")
ShellExec /TT /SW_HIDE %sExif% "-stay_open True -@ ""%sExifArgs%"""
ShowHint("Запуск exiftool.exe")
Sleep(1000)
Local nPID = ProcessGetId("exiftool.exe")
If nPID = 0 Then Return MsgBox("Процесс ExifTool не запущен")
EndIf
If StayOpen = 2 Then
If Not StartExifProcess() Then Return ShowHint("Операция прервана")
EndIf
If StayOpen = 3 Then
If StartExifProcess() Then
WriteLine("-G")
WriteLine("-S")
WriteLine("-lang")
WriteLine("ru")
WriteLine("d:\Temp\Images\test.jpg")
WriteLine("-execute")
#MsgBox(hProcess & " " & hChildStdInWr & " " & hChildStdOutRd & " " & hChildStdInRd & " " & hChildStdOutWr )
MsgBox(ReadResponse())
Return ExifClose()
Else
Return ShowHint("Операция прервана")
EndIf
EndIf
T1 = GetUptime()
ShowHint("Обработка выделенного Exiftool.exe")
For j = 0 To aSel.Count - 1
sFile = sPath & aSel[j]
If FileExist(sFile) And Not StrPos(FileGetAttr(sFile), "D") Then
files += 1
str &= ExiftoolInfo(sFile, StayOpen)
EndIf
Next
T2 = Round(GetUptime() - T1, 0) / 1000
ShowHint("Выбрано: " & aSel.Count & auCRLF & _
"Обработано: " & files & auCRLF & _
"-stay_open: " & (StayOpen ? "Да" : "Нет") & auCRLF & _
"stdin/stdout: " & (StayOpen = 2 ? "Да" : "Нет") & auCRLF & _
"Объем данных: " & SizeFormat(StrLen(str), 0, 'B', 2) & auCRLF & _
"Время операции: " & StrFormat("%.3f", T2) & " sec")
ClipPut(str)
FileWrite(TEMP & "\_exif_" & StayOpen, str, "UTF-8")
If StayOpen = 2 Then ExifClose()
If StayOpen = 1 Then
ProcessTerminate("exiftool.exe")
Sleep(100)
DllCall("kernel32.dll\DeleteFileW", "Wstr", sExifOut)
DllCall("kernel32.dll\DeleteFileW", "Wstr", sExifArgs)
EndIf
EndFunc
Func ExiftoolInfo(FileName, StayOpen)
If StayOpen = 2 Then
#WriteLine("-G")
#WriteLine("-S")
#WriteLine("-lang")
#WriteLine("ru")
#WriteLine(FileName)
#WriteLine("-execute")
WriteLine("-G" & auCRLF & _
"-S" & auCRLF & _
"-lang" & auCRLF & _
"ru" & auCRLF & _
Filename & auCRLF & _
"-execute" & auCRLF)
Return ReadResponse() & auCRLF
EndIf
If Not StayOpen Then
ProcessExecGetOutput /C:65001 out %sExif% '-charset filename=Russian -lang ru -G -S "%FileName%"'
Else
If FileExist(sExifOut) Then
DllCall("kernel32.dll\DeleteFileW", "Wstr", sExifOut)
EndIf
Local sArgs = "-G" & auCRLF & _
"-S" & auCRLF & _
"-lang" & auCRLF & _
"ru" & auCRLF & _
"-W+!" & auCRLF & sExifOut & auCRLF & FileName & auCRLF & "-execute" & auCRLF
Static obj = BinaryFile(sExifArgs, "a")
obj.WriteStr(sArgs, "", "UTF-8")
out = "<time-out>"
For i = 1 To 50
If FileExist(sExifOut) Then
out = FileRead(sExifOut, 0, "UTF-8")
DllCall("kernel32.dll\DeleteFileW", "Wstr", sExifOut)
Break
EndIf
Sleep(10)
Next
EndIf
Return out
EndFunc
Func StartExifProcess()
# Настройка атрибутов безопасности для наследования дескрипторов
Local Sec = Buffer(auX64 ? 24 : 12)
Sec.Zero()
Sec.SetNum(0, "UInt", Sec.size) # nLength
If auX64 Then
Sec.SetNum(8, "Ptr", 0) # lpSecurityDescriptor
Sec.SetNum(16, "Int", 1) # bInheritHandle
Else
Sec.SetNum(4, "Ptr", 0)
Sec.SetNum(8, "Int", 1)
EndIf
# Создаем Pipe для STDIN (Запись из Autorun -> Чтение в ExifTool)
If Not DllCall("CreatePipe", "Ptr*", @hChildStdInRd, "Ptr*", @hChildStdInWr, "Ptr", Sec.Ptr, "UInt", 0) Then
MsgBox("Error CreatePipe STDIN")
Return 0
EndIf
# Создаем Pipe для STDOUT (Запись из ExifTool -> Чтение в Autorun)
if Not DllCall("CreatePipe", "Ptr*", @hChildStdOutRd, "Ptr*", @hChildStdOutWr, "Ptr", Sec.Ptr, "UInt", 0) Then
DllCall("CloseHandle", "Ptr", hChildStdInRd)
DllCall("CloseHandle", "Ptr", hChildStdInWr)
MsgBox("Error CreatePipe")
Return 0
EndIf
# Запрещаем наследование нашей (записывающей/читающей) стороны каналов
DllCall("SetHandleInformation", "Ptr", hChildStdInWr, "UInt", 1, "UInt", 0)
DllCall("SetHandleInformation", "Ptr", hChildStdOutRd, "UInt", 1, "UInt", 0)
# Настройка STARTUPINFO
Local SI = Buffer(auPtrSize = 8 ? 104 : 68)
SI.Zero()
SI.SetNum(0, "UInt", SI.Size)
SI.SetNum(auX64 ? 60 : 44, "UInt", 0x00000101) # STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW
SI.SetNum(auX64 ? 64 : 48, "UShort", 0) # SW_HIDE (скрывает окно консоли)
SI.SetNum(SI.Size - 3 * auPtrSize, "Ptr", hChildStdInRd)
SI.SetNum(SI.Size - 2 * auPtrSize, "Ptr", hChildStdOutWr)
SI.SetNum(SI.Size - auPtrSize, "Ptr", hChildStdOutWr) # Дублируем stdout в stderr
# Настройка PROCESS_INFORMATION
PI = Buffer(auPtrSize = 8 ? 24 : 16)
PI.Zero()
# Командная строка запуска
Local cmdLine = '"' & sExif & '" -stay_open True -@ -'
# Флаг CREATE_NO_WINDOW (0x08000000) полностью блокирует создание консольного окна
If Not DllCall("CreateProcessW", "Ptr", 0, "WStr", cmdLine, _
"Ptr", 0, "Ptr", 0, "Int", 1, "UInt", 0x08000000, _
"Ptr", 0, "Ptr", 0, "Ptr", SI.Ptr, "Ptr", PI.Ptr) Then
# Закрываем все открытые дескрипторы при ошибке
DllCall("CloseHandle", "Ptr", hChildStdInRd)
DllCall("CloseHandle", "Ptr", hChildStdInWr)
DllCall("CloseHandle", "Ptr", hChildStdOutRd)
DllCall("CloseHandle", "Ptr", hChildStdOutWr)
MsgBox("Error CreateProcess")
Return 0
EndIf
# Сохраняем нужные дескрипторы
hProcess = PI.GetNum(0, "Ptr")
hThread = PI.GetNum(auPtrSize, "Ptr")
# Закрываем дескрипторы, которые больше не нужны родительскому процессу
DllCall("CloseHandle", "Ptr", hChildStdInRd)
DllCall("CloseHandle", "Ptr", hChildStdOutWr)
Return 1
EndFunc
Func WriteLine(text)
rawString = text & auCRLF
# ExifTool ожидает UTF-8 или системную кодировку (зависит от настроек, UTF-8 наиболее универсален)
bufLen = StrLen(rawString) * 2
buf = Buffer(bufLen)
buf.Zero()
bufWr = buf.SetStr(rawString, 0, bufLen, "UTF-8")
# Запись через WinAPI WriteFile
Local bytesWritten = 0
DllCall("WriteFile", "Ptr", hChildStdInWr, "Ptr", buf.ptr, "UInt", bufWr, "UInt*", @bytesWritten, "Ptr", 0)
Free(buf)
EndFunc
Func ReadResponse()
Local output = "", chunk = ""
# Буфер для побайтового/посимвольного чтения из пайпа
Static readBuf = Buffer(1024*2)
readBuf.Zero()
While 1
Local bytesRead = 0
If Not DllCall("ReadFile", _
"Ptr", hChildStdOutRd, _
"Ptr", readBuf.Ptr, "UInt", readBuf.Size, "UInt*", @bytesRead, "Ptr", 0) Then
Break
EndIf
If bytesRead <= 0 Then Break
chunk = readBuf.GetStr(0, bytesRead, "UTF-8")
output &= chunk
If StrPos(chunk, "{ready}") Then
Break
EndIf
Wend
Return StrTrim(output)
EndFunc
Func ExifClose()
If hChildStdInWr Then
WriteLine("-stay_open")
WriteLine("False")
DllCall("CloseHandle", "Ptr", hChildStdInWr)
hChildStdInWr = 0
EndIf
If hChildStdOutRd Then
DllCall("CloseHandle", "Ptr", hChildStdOutRd)
hChildStdOutRd = 0
EndIf
if hProcess Then
DllCall("CloseHandle", "Ptr", hProcess)
hProcess = 0
EndIf
If hThread Then
DllCall("CloseHandle", "Ptr", hThread)
hThread = 0
EndIf
EndFunc |
Пришлось целый вечер на это убить, но чего только не сделаешь ради любви
| AkulaBig wrote: | | Вот возьмите конкретный файл и протестируйте, за сколько по нему выведется инфа: |
Этот файл шифрованный и обрабатывается медленно. Для таких файлов должен быть предусмотрен таймаут. В выхлопе об этом так и сказано
| Code: | [ExifTool] ExifToolVersion: 13.52
[ExifTool] Warning: [minor] Decryption is very slow for encryption V5.6 or higher
[File] FileName: mwt-2021-proceedings.pdf
[File] Directory: D:/Temp
[File] FileSize: 70 MB |
| AkulaBig wrote: | | А в действительности работает медленнее, чем с временными файлами. |
Работать может медленнее, если буфер для чтения маленький. Я специально сделал на однобайтовом буфере сначала, чтобы посмотреть скорость. Работает в 3-4 раза медленнее, чем с файлами, но раз в 10 быстрее, чем если просто exiftool.exe вызывать. Увеличив буфер до 2Кб удалось по скорости обойти режим работы с файлами на 150-200% в зависимости числа и типов файлов.
| AkulaBig wrote: | | Почему не получится, если у меня получилось и результаты теста я привел? |
Я не знаю, что у вас именно получилось. WinScriptAdv это костыль, который создает временный скрипт для каждого файла и с нуля его запускает. Это очень ограничивает действия. В частности не получится повторить код на Autohotkey, который сразу предлагал болван, и который я повторил на Autorun.
| AkulaBig wrote: | | Ни одного скрипта мне запустить не удалось. |
Первый скрипт с ComObject рабочий, я только заменил теги на "-G" и указал свои пути
| Code: | ; Формируем пакет аргументов для текущего файла.
; Каждая опция и значение должны быть на новой строке.
;cmd := "-Artist=Автор " index "`n"
;cmd .= "-Copyright=2026 Студия`n"
cmd := "-G`n"
cmd .= targetFile "`n" ; Путь к файлу передается в конце пакета команд
cmd .= "-execute" ; ОБЯЗАТЕЛЬНО: сообщает ExifTool, что этот пакет команд пора выполнить |
Второй скрипт тоже рабочий, но только для X64. Чтобы он заработал в Autohotkey32 пришлось подкорректировать буфер
| Code: | ; Настройка атрибутов безопасности для наследования дескрипторов
SA := Buffer(12, 0)
NumPut("UInt", 12, SA, 0) ; nLength
NumPut("Ptr", 0, SA, 4) ; lpSecurityDescriptor
NumPut("Int", 1, SA, 8) ; bInheritHandle |
И еще я забыл приложить второе сообщение из чата с примером вызова. Не думал все же, что займусь переводом на Autorun
 Hidden text | Code: | ; 1. Инициализация процесса через WinAPI
exiftoolPath := "c:\Portable\exiftool.exe"
et := ExifToolPipe(exiftoolPath)
; 2. Передача команды (как в вашем оригинальном коде)
; Важно: каждая команда и опция для -stay_open должны идти отдельной строкой,
; а в конце блока обязательно передается параметр "-execute"
et.WriteLine("-ImageSize")
et.WriteLine("c:\Portable\Images\test.jpg")
et.WriteLine("-execute")
;MsgBox("1")
; 3. Считывание ответа через WinAPI ReadFile до строки {ready}
response := et.ReadResponse()
MsgBox(response)
; 4. По завершении работы скрипта закрываем дескрипторы
et.Close() |
Файлы .ahk обязательно должны сохраняться как UTF-8, иначе они могут не работать. |
|