Orion9

|
Posted: Sun Oct 04, 2026 20:34 Post subject: |
|
|
Для начала. Если подсказка вызывается на тяжелом файле, ТС вешается, и приходится только ждать. Это обычное поведение тотала:
 Hidden text
Проблема с stdin/stdout, о которой я писал выше, выглядит следующим образом.
Когда ExifTool выполняет команду -execute, результат выполнения передается в stdout с маркером {ready}. PeekNamedPipe, добавленная в последнем коде, позволяет заглянуть в устройство вывода и проверить есть ли в нем данные. Если данных нет, то можно еще немного подождать и выйти по таймауту. Однако это не остановит саму ExifTool и не прервет операцию вывода. При наведении на следующий файл в подсказке будут отображаться даннные таймаута, а не текущего файла.
Чтобы синхронизировать процесс ввода-вывода, к команде -execute можно добавить любое число: тогда ExifTool пометит вывод для этой команды соответствующим маркером {ready+число}. Например: -execute320 --> {ready320}. Теперь можно пропарсить этот вывод, выкинув из него всё ненужное. Такой вариант я и попробовал, однако он не решает всех проблем. Подсказка легко скользит по панели и не вешает ТС даже на медленных файлах, но ExifTool продолжает делать вывод в stdout, и нет возможности его прервать: разработчик не предусмотрел такой опции.
| Болван wrote: | ExifTool не имеет встроенного текстового или программного механизма, чтобы сказать ему: «Отмени текущую команду -execute, но сам не умирай».
В официальной документации Image::ExifTool и в описании параметров -stay_open логика стейт-машины прописана однозначно. Когда вы отправляете маркер -execute, ExifTool заходит во внутренний синхронный цикл (написанный на Perl). Пока этот цикл парсит битый или тяжелый файл, процесс физически не читает stdin. Вы можете слать туда любые команды отмены — они просто будут копиться в буфере операционной системы. ExifTool увидит их только тогда, когда закончит текущую операцию (то есть когда отменять будет уже нечего)
| Пока вывод не будет закончен, нет возможности показать другие подсказки. Визуально это выглядит так:
 Hidden text
Пришлось придумать что-то другое. Получилось где-то так:
 Hidden text
Прикольно, конечно, но все-равно приходится ждать. Но это лучше, чем повисший интерфейс. Если бы была возможность остановить ненужный вывод, было бы еще лучше.
Переменные, которые управляют режимами
| Code: | Local gExifBusyHint = 1, gExifTimeOutHint = 1 |
Если оставить нули
| Code: | Local gExifBusyHint = 0, gExifTimeOutHint = 0 |
То подсказка будет стандартная тоталовская, но за счет Sleep(10) дает возможность откликаться интерфейсу при зависании
 Hidden text
Переменная gExifFixAnsi включает преобразование кракозябр после первой ":", gExifConverseAnsi включает преобразование utf8 символов в ansi после первой ":"
Обновленные функции (остальные были выше)
 Hidden text | Code: | Func ExifHint(FileName, FieldIndex, UnitIndex)
Static c = 0
# первое поле подсказки C1
# инициализация новой подсказки
If FieldIndex = 1 Then
# объект является каталогом
If StrPos(FileGetAttr(FileName), "D") Then
gHintPipe = ""
Return
EndIf
# проверка CapsLock и Ctrl
Local bCtrl = BitAND(DllCall("GetKeyState", "int", 0x14, "short"), 1)
If Not bCtrl then bCtrl = IsPressed (0x11)
# обратное поведение Ctrl
If gHintCaps And Not bCtrl Then
gHintPipe = ""
Return
EndIf
# разделение панели на три области
Local x, y, w, sArgs
Local hWnd = RequestInfo(ItemAtCursor("panel"))
WinGetPos("", "", "w", "", hWnd)
If w > 0 Then
MouseGetPos("x","y")
Static buf = Buffer(8) # POINT
buf.Zero()
buf.SetNum(0, "long", x, "long", y)
DllCall("ScreenToClient", "hwnd", hWnd, "ptr", buf.ptr)
x = buf.GetNum(0)
Switch Round(x/w*100,0)
Case 0 To 9
sArgs = "-m" & auCRLF & "-q" #& auCRLF & "-S"
Case 10 To 84
sArgs = "-G0" & auCRLF & "-lang" & auCRLF & "ru"
Case 85 To 100
sArgs = "-g" & auCRLF & "-s2" & auCRLF & "-lang" & auCRLF & "ru"
EndSwitch
EndIf
# запись в stdin процесса
c += 1
WriteLine(sArgs & auCRLF & Filename & auCRLF & "-execute" & c & auCRLF)
# получение ответа
gHintPipe = ReadResponse(c)
Endif
# разделение ответа на строки
# выполняется для всех полей подсказки C1-C60
If gHintPipe = "" Then Return
gExifFixAnsi = false
If gExifFixAnsi Then
Return FixSpaces(StrPart(gHintPipe, auCRLF, FieldIndex))
Else
Return StrPart(gHintPipe, auCRLF, FieldIndex)
EndIf
EndFunc
|
 Hidden text | Code: | Func ReadResponse(num)
Local output = "", bytesAvailable, i = 0
# Буфер для побайтового/посимвольного чтения из пайпа
Static readBuf = Buffer(1024*2)
readBuf.Zero()
While 1
If Not DllCall("PeekNamedPipe", _
"Ptr", hChildStdOutRd, _
"Ptr", 0, "UInt", 0, "Ptr", 0, "Dword*", @bytesAvailable, "Ptr", 0, "Int") Then
Return "---" & auCRLF & _
"Exiftool <no-response>" & auCRLF & "---"
EndIf
Local gExifBusyHint = 1, gExifTimeOutHint = 1
If bytesAvailable <= 0 Then
i += 1
Sleep(10)
If i > 50 Then
If gExifBusyHint Then
ExiftoolBusyHint("Exiftool <time-out>")
i = 0
Continue
ElseIf gExifTimeoutHint Then
WriteLine(auCRLF & "-echo" & auCRLF & "<time-out>" & auCRLF)
Return "---" & auCRLF & "Exiftool <time-out>" & auCRLF & "---" & auCRLF & output
Else
Continue
EndIf
Else
Continue
EndIf
EndIf
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
Local chunk_utf8 = readBuf.GetStr(0, bytesRead, "UTF-8")
gExifConverseAnsi = 1
If gExifConverseAnsi Then
Local chunk_ansi = readBuf.GetStr(0, bytesRead, "ANSI")
Static ansi = List(), utf8 = List(), wrong = Chr(0xFFFD)
ansi.Count = 0
utf8.Count = 0
ansi.Text = chunk_ansi
utf8.Text = chunk_utf8
Local wrong_pos, colon_pos, colon_ansi
For i = 0 to utf8.Count - 1
wrong_pos = StrPos(utf8[i], wrong)
If wrong_pos Then
colon_pos = StrPos(utf8[i], ":")
If wrong_pos > colon_pos Then
colon_ansi = StrPos(ansi[i], ":")
If colon_ansi Then utf8[i] = StrLeft(utf8[i], colon_pos) & StrMid(ansi[i], colon_ansi)
EndIf
EndIf
Next
chunk_utf8 = utf8.text
EndIf
output &= chunk_utf8
If StrPos(chunk_utf8, "{ready" & num & "}") Then
Break
EndIf
Wend
Local t = StrPos(output, "<time-out>", -1)
If t Then output = StrMid(output, t+1)
Return output
EndFunc
Func ExiftoolBusyHint(HintText)
#SetHintParam("ShowHint", "Font", 11, "Arial")
SetHintParam("ShowHint", "BackColor", 0xFF0000)
SetHintParam("ShowHint", "Text", 0xFFFFFF)
ShowHint(HintText, "", "", 500, 1)
Sleep(50)
SetHintParam("ShowHint", "Reload")
EndFunc |
Добавлено спустя 3 минуты:
| AkulaBig wrote: | | Интересно, умеет ли Авторан запускать именованный экземпляр? |
Можно спросить у болвана, умеет ли это делать AutoHotKey v2. Если да, то и Autorun тоже с большой долей вероятности. |
|