Orion9

|
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) вроде решает эту проблему. Вы ее пробовали?
Там только два прохода почему-то. |
|