| View previous topic :: View next topic |
| Author |
Message |
Orion9

Joined: 01 Jan 2024 Posts: 1268
|
(Separately) Posted: Wed Sep 30, 2026 12:12 Post subject: |
|
|
| AkulaBig wrote: | | куда правильно вставить это условие, чтобы все заработало. |
Тут чекушку открывать надо, а сейчас за рулем, не могу.
Но если навскидку, то функция ET запускается 60 раз. Процесс exiftool должен запуститься один раз, и желательно за пределами этой функции. FieldIndex = 1 должен сделать запрос к запущенному процессу и ждать ответа. Остальные 59 вызовов будут просто разбирать полученный выхлоп на строки.
Это была тестовая функция для проверки скорости на группе выделенных файлов. В подсказке прирост скорости менее очевиден. Имхо, лучше оставить общение с процессом через файл, что я и сделал. И код намного проще, и скорость практически та же.
Вы можете сами протестировать и убедиться в этом. Тестовая функция с тремя режимами как раз и создавалась для этого:
Win+[ запуск exiftool для каждого файла.
Win+] держит процесс открытым и взаимодействует с ним через файл аргументов.
Win+\ взаимодействует с процессом через stdin и stdout.
Результат сохраняется во временный каталог, в файлы exif_[0,1,2]. Все три файла должны совпадать за исключением метки последнего времени доступа и маркера {ready}.
 Hidden text | 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 |
Чем больше выделено файлов, тем больше (должен быть) прирост. Это было критично только для набора колонок в режиме просмотра х64. Но опция SetFieldsParam("Delayed", 1) вроде решает эту проблему. Вы ее пробовали?
Там только два прохода почему-то. |
|
| Back to top |
|
 |
AkulaBig
Joined: 03 Dec 2008 Posts: 618
|
(Separately) Posted: Wed Sep 30, 2026 13:39 Post subject: |
|
|
| Orion9 wrote: | | Процесс exiftool должен запуститься один раз, и желательно за пределами этой функции. |
За пределами он будет запускаться при старте ТС, что не очень хорошо. Но он запускается один раз, поэтому на данном этапе место запуска не имеет значения.
| Orion9 wrote: | | В подсказке прирост скорости менее очевиден. |
Очень заметен.
| Orion9 wrote: | | лучше оставить общение с процессом через файл |
Лучше, но проблема в том, при запуске колонок они засекаться начинают и все подвисает. А так у меня и вариант с файлами есть. но и там FieldIndex = 1 работает непонятно. Без него работает ощутимо медленней, а с ним когда начинаешь крякозябры убирать, почему-то по одному полю обрабатывается. То ли с FieldIndex = 1 надо алгоритм менять, то ли знать, куда его впихнуть. Но я многие варианты уже попробовал. Пока не получается.
| Orion9 wrote: | | SetFieldsParam("Delayed", 1) |
Пока не пробовал. Не до нее. Сейчас надо сделать чтобы все работало правильно. А в дальнейшем похоже для подсказки придется запускать отдельный экземпляр ET и временные файлы с другим названием. |
|
| Back to top |
|
 |
FallenAngel
Joined: 30 Dec 2025 Posts: 45
|
(Separately) Posted: Wed Sep 30, 2026 17:09 Post subject: |
|
|
В последнее время часто вылетает на Win11 25H2
| Code: | Имя сбойного приложения: TOTALCMD64.EXE, версия: 11.58.0.0, метка времени: 0x00000000
Имя сбойного модуля: Autorun.wdx64, версия: 2.2.21.3, метка времени: 0x68ef4c72
Код исключения: 0xc0000005
Смещение ошибки: 0x000000000005fa02
Идентификатор сбойного процесса: 0x229C
Время запуска сбойного приложения: 0x1DD50D5A31BB41E
Путь сбойного приложения: D:\soft\Total Commander 64\TOTALCMD64.EXE
Путь сбойного модуля: D:\soft\Total Commander 64\Plugins\Autorun\Autorun.wdx64
Идентификатор отчета: 0c64ebd0-8054-4fdf-8c0b-3116aec21693
Полное имя сбойного пакета:
Код приложения, связанного со сбойным пакетом: |
|
|
| Back to top |
|
 |
AkulaBig
Joined: 03 Dec 2008 Posts: 618
|
(Separately) Posted: Wed Sep 30, 2026 18:03 Post subject: |
|
|
Orion9
Вот вариант с временными файлами. Ваш вариант летает как ракета, если FieldIndex = 1. Но кривой вывод:
А если оставить -G -S, то только на английском и с наименованием группы.
А вот мой вариант с корректным выводом полей, но без FieldIndex:
 Hidden text
| Code: |
Func ET(FileName, FieldIndex, UnitIndex)
# Расположение файла Autorun.ini
# Location of Autorun.ini file
Local ini_TypeHints = COMMANDER_PATH & "\Ini\Autorun.ini"
# Читаем номер вида
# Reading type number
Local TypeHintsNum = IniRead(ini_TypeHints, "TypeHints", "CurrVal", 0)
# 0 - стандартный вид подсказки над файлами
# 1 - расширенный вид подсказки над файлами
If TypeHintsNum = 0 Then Return
If StrPos(FileGetAttr(FileName), "D") Then Return
Local iPID = 0, sOutFile = TEMP & "\out.txt", sArgFile = TEMP & "\args.txt"
iPID = ProcessGetId("exiftool.exe")
If iPID = 0 Then
FileWrite(sArgFile, "", "UTF-8 NOBOM")
ShellExec /TT /SW_HIDE %util_ET% "-stay_open True -@ ""%sArgFile%"""
EndIf
# ShellExec /SW_HIDE /WAIT %COMSPEC% "/c break > ""%sOutFile%"""
DllCall("kernel32.dll\DeleteFileW", "Wstr", sOutFile)
Local ansi
If FieldIndex = 1 Then
Local sArgs = "-lang" & auCRLF & _
"ru" & auCRLF & _
"-m" & auCRLF & _
"-q" & auCRLF & _
"-q" & auCRLF & _
"-W+!" & auCRLF & _
sOutFile & auCRLF & FileName & auCRLF & "-execute" & auCRLF
Local obj = BinaryFile(sArgFile, "a")
obj.WriteStr(sArgs, "", "UTF-8")
Free(obj)
# Проверяем наличие файла для записи данных
While 1
If FileExist(sOutFile) Then
# Читаем данные из файла
ansi = FileRead(sOutFile)
If ansi <> "" Then
# ShellExec /SW_HIDE /WAIT %COMSPEC% "/c break > ""%sOutFile%"""
DllCall("kernel32.dll\DeleteFileW", "Wstr", sOutFile)
Break
EndIf
EndIf
Sleep(10)
WEnd
EndIf
Return TrimSpaces1(StrPart(ansi, auLF, FieldIndex))
EndFunc
Func TrimSpaces1(String)
Local a, b
If Not StrPos(String, ":") Then Return String
a = StrPart(String, ": ", 1)
b = StrPart(String, ": ", 2)
Return StrTrim(FixCPET(a)) & ": " & StrTrim(FixCPET(b))
EndFunc
Func FixCPET(String)
Static a = List(), u = List()
Local ansi = String, utf8, wrong = Chr(0xFFFD)
Local nSize = StrLen(ansi)
Local buf = Buffer(nSize)
buf.Zero()
buf.SetStr(ansi, 0, nSize, "ANSI")
utf8 = buf.GetStr(0, nSize, "CP:65001")
Free(buf)
a.Count = 0
u.Count = 0
a.Text = ansi
u.Text = utf8
For i = 0 to u.Count - 1
If StrPos(u[i], wrong) Then u[i] = a[i]
Next
Return u.text
EndFunc
|
Он работает медленно. А если прописать FieldIndex, то получается фигня:
Но с MI этот-же алгоритм работает без проблем. Очень хочется добиться быстрой и правильной работы. |
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|