Для начала. Если подсказка вызывается на тяжелом файле, ТС вешается, и приходится только ждать. Это обычное поведение тотала:
[cut]

[/cut]
Проблема с stdin/stdout, о которой я писал выше, выглядит следующим образом.
Когда ExifTool выполняет команду -execute, результат выполнения передается в stdout с маркером {ready}. PeekNamedPipe, добавленная в последнем коде, позволяет заглянуть в устройство вывода и проверить есть ли в нем данные. Если данных нет, то можно еще немного подождать и выйти по таймауту. Однако это не остановит саму ExifTool и не прервет операцию вывода. При наведении на следующий файл в подсказке будут отображаться даннные таймаута, а не текущего файла.
Чтобы синхронизировать процесс ввода-вывода, к команде -execute можно добавить любое число: тогда ExifTool пометит вывод для этой команды соответствующим маркером {ready+число}. Например: -execute320 --> {ready320}. Теперь можно пропарсить этот вывод, выкинув из него всё ненужное. Такой вариант я и попробовал, однако он не решает всех проблем. Подсказка легко скользит по панели и не вешает ТС даже на медленных файлах, но ExifTool продолжает делать вывод в stdout, и нет возможности его прервать: разработчик не предусмотрел такой опции.
Болван писал(а):ExifTool не имеет встроенного текстового или программного механизма, чтобы сказать ему: «Отмени текущую команду -execute, но сам не умирай».
В официальной документации Image::ExifTool и в описании параметров -stay_open логика стейт-машины прописана однозначно. Когда вы отправляете маркер -execute, ExifTool заходит во внутренний синхронный цикл (написанный на Perl). Пока этот цикл парсит битый или тяжелый файл, процесс физически не читает stdin. Вы можете слать туда любые команды отмены — они просто будут копиться в буфере операционной системы. ExifTool увидит их только тогда, когда закончит текущую операцию (то есть когда отменять будет уже нечего)
Пока вывод не будет закончен, нет возможности показать другие подсказки. Визуально это выглядит так:
[cut]

[/cut]
Пришлось придумать что-то другое. Получилось где-то так:
[cut]

[/cut]
Прикольно, конечно, но все-равно приходится ждать. Но это лучше, чем повисший интерфейс. Если бы была возможность остановить ненужный вывод, было бы еще лучше.
Переменные, которые управляют режимами
Код: Выделить всё
Local gExifBusyHint = 1, gExifTimeOutHint = 1
Если оставить нули
Код: Выделить всё
Local gExifBusyHint = 0, gExifTimeOutHint = 0
То подсказка будет стандартная тоталовская, но за счет Sleep(10) дает возможность откликаться интерфейсу при зависании
[cut]

[/cut]
Переменная gExifFixAnsi включает преобразование кракозябр после первой ":", gExifConverseAnsi включает преобразование utf8 символов в ansi после первой ":"
Обновленные функции (остальные были выше)
[cut]
Код: Выделить всё
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
[/cut]
[cut]
Код: Выделить всё
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
[/cut]
Добавлено спустя 3 минуты:
AkulaBig писал(а):Интересно, умеет ли Авторан запускать именованный экземпляр?
Можно спросить у болвана, умеет ли это делать AutoHotKey v2. Если да, то и Autorun тоже с большой долей вероятности.