Autorun

Обсуждение только плагинов и утилит для Total Commander.

Модераторы: Avada, CaptainFlint, Nik, d-view, Моторокер

AkulaBig
Сообщения: 639
Зарегистрирован: Ср дек 03, 2008 11:48

Сообщение AkulaBig »

Orion9 писал(а):У вас же отдельный поток все время крутится, можно из него переодически проверять зависшие процессы exiftool.exe и прибивать.
У меня сейчас примерно так и сделано. Проверяется открыты колонки пользователя и если нет, то процесс прибивается. Но при этом прибиваются и подсказки. Так что тут что-то серьезное надо. Интересно, умеет ли Авторан запускать именованный экземпляр?
Аватара пользователя
Orion9
Сообщения: 1283
Зарегистрирован: Пн янв 01, 2024 17:18

Сообщение Orion9 »

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

[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 тоже с большой долей вероятности.
AkulaBig
Сообщения: 639
Зарегистрирован: Ср дек 03, 2008 11:48

Сообщение AkulaBig »

Orion9 писал(а):Прикольно
Действительно прикольно с мигающим окном предупреждения. Хотя и второй вариант неплох. Попробую повторить. Мне кажется у меня побыстрей данные вытягивает из mwt-2021-proceedings.pdf.
Orion9 писал(а): умеет ли это делать AutoHotKey v2. Если да, то и Autorun тоже с большой долей вероятности.
Я не пойму, Авторан на AHK написан? Или функционал с него скопирован?

Добавлено спустя 10 минут:

Сейчас самое главное разобраться, как тайм-аут будет работать с временными файлами совместно подсказки и колонок. Сейчас у меня подсказка через потоки сделана из-за того, что подсказка через файлы забивает колонки через файлы. Но если подсказка не будет забивать колонки, ее тоже лучше сделать через файлы. Все-же этот вариант самый быстрый. В общем будем тестить.
Аватара пользователя
Orion9
Сообщения: 1283
Зарегистрирован: Пн янв 01, 2024 17:18

Сообщение Orion9 »

AkulaBig писал(а): Я не пойму, Авторан на AHK написан? Или функционал с него скопирован?
Последние версии Autorun многое переняли из Autohotkey, хотя сам Loopback никогда не говорил, что ориентируется именно на AHK, но синтаксис очень похож. Особенно последние добавленные функции, вроде DllCall, которые выглядят очень похоже.

Именно об этом и речь. Если есть возможность решить задачу на Autohotkey при использовании DllCall и прочего, то скорее всего получится сделать то же самое и на Autorun.

Есть, конечно, исключения, например объект СОМ, которого в Autorun попросту нет, или какие-нибудь двумерные массивы или прочие ограничения, но в целом, много можно повторить и сделать.

А к болвану так и так идти надо. Вы думаете, я открыл доки по пайпам и стал что-то там конструировать? Да нет, конечно. Весь код болванный :lol: Даже последняя доработка с PeekNamedPipe тоже была от болвана :lol:

Конечно, нужно его контролировать, чтобы он мусор не выкидывал, но в основном он вменяем и хорошие идеи подает.

А что касается подсказки, там есть, где полю развернуться. Можно сделать ini-файл для хранения опций и параметров exiftool, можно менять их на лету для каждой секции панели или в зависимости от расширения и т.д.

С временными файлами должно точно так же работать. Если есть таймаут, exiftool не остановится и продолжит выполнение, пока не запишет все во временный файл, а сам файл скорее всего уже не удалится и останется во временном каталоге.
AkulaBig
Сообщения: 639
Зарегистрирован: Ср дек 03, 2008 11:48

Сообщение AkulaBig »

Orion9 писал(а):Если есть таймаут, exiftool не остановится и продолжит выполнение
В общем с тайм-аутом не получилось. Вешает даже без совместной работы с колонками. Например, наводим на mwt-2021-proceedings.pdf. Подсказка начинает мигать. Не ждем, а начинаем двигать мышь дальше. Все вешается :(
Еще интересный глюк появился. Почему-то крякозябры перестали преобразовываться по очень интересной схеме. С половины данных. То-есть пол строки преобразовалось, а половина нет.
Важный вопрос. Про глюк с индексами. У меня поля так прописаны: "C60:::FileHint" "CD61:::CD". То-есть есть поле, не относящееся к подсказке. Удалил "CD61:::CD" и пока не удалось ни одного глюка словить. То-есть надо ограничивать количество обработанных колонок только нужными? Если да, то как это сделать?
AkulaBig
Сообщения: 639
Зарегистрирован: Ср дек 03, 2008 11:48

Сообщение AkulaBig »

Проверил сам. Количество обрабатываемых полей соответствует их определению. Значит не из-за этого ошибка вываливается. А может глючить из-за того, что полей 61? Поле работает отлично в другом модуле. По идее значит не может быть из-за этого глюк.
Аватара пользователя
yozhik
Сообщения: 353
Зарегистрирован: Вс май 04, 2014 20:34
Откуда: Электросталь

Сообщение yozhik »

БАГ
Для Loopback, когда он back.
Buffer.SetNum с типом ptr на x64 обрезает значение до 32 бит. 64-битная SetNum("ptr") теряет старшие 32 бита. Тест:

Код: Выделить всё

If AUTORUN_TCARCH = 64 Then
    Local tst = Buffer(8)
    tst.Zero()
    tst.SetNum(0, "ptr", 0xABCD12345678)
    Local nHi = tst.GetNum(4, "dword")
    MsgBox("SetNum(ptr) high dword=" & nHi & _
           "  (43981=OK, 0=обрезает до 32 бит)", "test")
    Free(tst)
EndIf
Amo ergo sum
Аватара пользователя
Orion9
Сообщения: 1283
Зарегистрирован: Пн янв 01, 2024 17:18

Сообщение Orion9 »

yozhik
Да, похоже, теряет. Тип "handle" тоже не работает. Тип hwnd не проверял, а вот int_ptr и uint_ptr работают. Тип "int64" тоже вроде рабочий.
AkulaBig писал(а):Почему-то крякозябры перестали преобразовываться по очень интересной схеме. С половины данных. То-есть пол строки преобразовалось, а половина нет.
Может быть. Преобразовывается то, что после двойных точек. Но если двойных точек две-три, или кроме символов 0xFFFB есть что-то еще, то образуются новые кракозябры.

Решение от болвана - переводить посимвольно, но нужно ли?

[cut]

Код: Выделить всё

#Requires AutoHotkey v2.0

/**
 * Высокоскоростной разбор буфера без DllCall внутри цикла.
 * Идеально синхронизирует латиницу, UTF-8 и битый ANSI (CP1251).
 */
FastDecodeAndFixBuffer(rawBuffer, bytesCount) {
    if (bytesCount <= 0)
        return ""

    outString := ""
    ptr := rawBuffer.Ptr
    endPtr := ptr + bytesCount

    while (ptr < endPtr) {
        b1 := NumGet(ptr, "UChar")

        ; --- 1. Стандартная латиница и ASCII (1 байт в UTF-8) ---
        if (b1 < 0x80) {
            outString .= Chr(b1)
            ptr += 1
            continue
        }

        ; --- 2. Потенциальный правильный UTF-8 (2 байта: Кириллица, Греческий, Арабский) ---
        if (b1 >= 0xC2 && b1 <= 0xDF) {
            ; Проверяем, есть ли следующий байт и является ли он валидным байтом продолжения (10xxxxxx)
            if (ptr + 1 < endPtr) {
                b2 := NumGet(ptr + 1, "UChar")
                if (b2 >= 0x80 && b2 <= 0xBF) {
                    ; Склеиваем два байта по формуле Unicode
                    charUnit := ((b1 & 0x1F) << 6) | (b2 & 0x3F)
                    outString .= Chr(charUnit)
                    ptr += 2
                    continue
                }
            }
        }

        ; --- 3. Потенциальный правильный UTF-8 (3 байта: Иероглифы) ---
        if (b1 >= 0xE0 && b1 <= 0xEF) {
            if (ptr + 2 < endPtr) {
                b2 := NumGet(ptr + 1, "UChar")
                b3 := NumGet(ptr + 2, "UChar")
                if (b2 >= 0x80 && b2 <= 0xBF && b3 >= 0x80 && b3 <= 0xBF) {
                    charUnit := ((b1 & 0x0F) << 12) | ((b2 & 0x3F) << 6) | (b3 & 0x3F)
                    outString .= Chr(charUnit)
                    ptr += 3
                    continue
                }
            }
        }

        ; --- 4. Потенциальный правильный UTF-8 (4 байта: Эмодзи, редкие знаки) ---
        if (b1 >= 0xF0 && b1 <= 0xF4) {
            if (ptr + 3 < endPtr) {
                b2 := NumGet(ptr + 1, "UChar")
                b3 := NumGet(ptr + 2, "UChar")
                b4 := NumGet(ptr + 3, "UChar")
                if (b2 >= 0x80 && b2 <= 0xBF && b3 >= 0x80 && b3 <= 0xBF && b4 >= 0x80 && b4 <= 0xBF) {
                    charUnit := ((b1 & 0x07) << 18) | ((b2 & 0x3F) << 12) | ((b3 & 0x3F) << 6) | (b4 & 0x3F)
                    outString .= Chr(charUnit)
                    ptr += 4
                    continue
                }
            }
        }

        ; --- 5. ВЫРАВНИВАНИЕ БРАКА: Это изолированный байт ANSI (CP1251) ---
        ; Если байт попал сюда, значит он не прошел проверку на UTF-8. 
        ; Для русской кодовой страницы 1251: коды 0xC0...0xFF — это буквы А-Я и а-я.
        if (b1 >= 0xC0 && b1 <= 0xFF) {
            ; Математическое смещение из таблицы CP1251 в Unicode (диапазон U+0410...U+044F)
            ; 0xC0 (А в CP1251) становится 0x0410 (А в Unicode)
            outString .= Chr(b1 + 0x0350) 
        } else if (b1 == 0xA8) {
            outString .= "Ё"
        } else if (b1 == 0xB8) {
            outString .= "ё"
        } else {
            ; Любой другой неопознанный битый байт (например, системный 0x80)
            outString .= Chr(0xFFFD) 
        }
        
        ptr += 1 ; Шагаем строго на 1 байт, так как это был одиночный ANSI символ
    }

    return outString
}
[/cut]
AkulaBig
Сообщения: 639
Зарегистрирован: Ср дек 03, 2008 11:48

Сообщение AkulaBig »

Orion9 писал(а):Преобразовывается то, что после двойных точек.
Да не, все нормально у меня. Кстати, для ET надо и название и значение преобразовывать. Я про употребление тайм-аута. С ним почему-то название или значение может наполовину крякозябрами показывать. Возможно цикл преобразования не успевает отработать.
Аватара пользователя
Orion9
Сообщения: 1283
Зарегистрирован: Пн янв 01, 2024 17:18

Сообщение Orion9 »

AkulaBig
Ситуация с индексами, как я понял, примерно такая.

Когда поля создаются через Pragma AutorunPluginFields первому полю присваивается первый индекс и т.д. по порядку. Если объявить 60 полей С1-С60, то индекс=52 должен вернуть значение С52 и т.д. Но если объявить что-то перед С1-С60, то индекс сместится, т.е. индекс=52 уже не вернет значение поля С52, а вернет индекс другого, соседнего поля. Поэтому, создавая группу полей, где важно сохранить индекс в первоначальном виде, чтобы потом не мучиться со смещением, нужно объявлять их сразу за Pragma AutorunPluginFields, и можно спокойно увеличить их количество: C1-C90.

Если вы хотите еще добавить полей, то лучше добавлять их по 30, т.к. это ограничение тотала на один тип. Но можно еще 10 добавить, тогда в секции HintsCustomField будет уже 4 ключа на *.*, а не три.

На счет кракозябр. Изначально было сделано самым простым способом, когда из буфера, содержащего байты, вытягивается одна строка в кодировке Utf8, а другая в Ansi. Далее строка Utf8 обрабатывается построчно. Если в ней есть символ FFFB, то берется эквивалент из Ansi. Расчет на то, что конец строки (за символом ":") весь в Ansi, но если это условие не соблюдается, то будут другие кракозябры из-за избыточной конвертации.

Болван предложил решение, конечно, крутое, но у меня возникла другая идея, попроще. Функция делает практически то же самое, и если бы не суррогатные пары, была бы совсем простой

[cut]

Код: Выделить всё

Func FFFDToAnsi(uStr, aStr)
    Static FFFD = Chr(0xFFFD)
    
    Local uLen = StrLen(uStr), aPos = 1, outStr = "", uChar, byte
    
    For i = 1 To uLen
       uChar = StrMid(uStr, i, 1)
       code = Asc(uChar)
       If code < 0x80 Then 
          byte = 1 
       ElseIf code < 0x800 Then
          byte = 2
       ElseIf code < 0xFFFF Then          
          byte = 3
          # High Surrogate (0xD800 - 0xDBFF)
          If code >= 0xD800 And code <= 0xDBFF And i < uLen Then
             # Low Surrogate (0xDC00 - 0xDFFF)
             low = Asc(StrMid(uStr, i+1, 1))             
             If low >= 0xDC00 And low <= 0xDFFF Then
                # Истинный код = ((High - 0xD800) * 0x400) + (Low - 0xDC00) + 0x10000
                code = ((code - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000
                byte = 4
                i += 1
                uChar &= Chr(low)
             EndIf
          EndIf       
       EndIf
       If uChar = FFFD Then
          byte = 1
          uChar = StrMid(aStr, aPos, 1)
       EndIf
       #OutputDebugString('char=' & uChar & ' asc=' & code & ' pos=' & aPos & ' byte=' & byte)
       aPos += byte    
       outStr &= uChar       
    Next
    
    Return outStr
EndFunc 
[/cut]

Вместо
[cut]

Код: Выделить всё

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
[/cut]
теперь вот так, но зато посимвольно
[cut]

Код: Выделить всё

For i = 0 to utf8.Count - 1
   wrong_pos = StrPos(utf8[i], wrong)
   If wrong_pos Then
      utf8[i] = FFFDToAnsi(utf8[i], ansi[i])
   EndIf
Next
[/cut]
AkulaBig
Сообщения: 639
Зарегистрирован: Ср дек 03, 2008 11:48

Сообщение AkulaBig »

Orion9 писал(а):Ситуация с индексами
Похоже больше 60 колонок не поддерживается :( В Ауторан при выборе полей в колонках показывает хоть сколько. Но при выводе подсказки 61 полем, которое я отдал под uCD, периодически глючит :( Хотя я обрабатываю только 60 полей в подсказке ET, MI. В общем сейчас я перешел на 59 полей в подсказке и плюс под uCD, то-есть всего 60 полей. Посмотрим, будет ли глючить.
Orion9 писал(а):зато посимвольно
Посмотрю, конечно. У мене не совсем как у вас сделано. Кодировки немного другие при получении данных. Поэтому надо разбираться. Только не пойму, сейчас ведь тоже посимвольно:

Код: Выделить всё

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 
Или я не прав?
Orion9 писал(а):Расчет на то, что конец строки (за символом ":") весь в Ansi
Нет, нет. У ET надо и название и значение конвертить. Так как крякозябры и там и там могут быть. Вот так у меня для ET:

Код: Выделить всё

Func TrimSpacesET(String)
	Local NameField, ValField
	If Not StrPos(String, ":") Then Return String

	NameField = StrPart(String, ": ", 1)
	ValField = StrPart(String, ": ", 2)

	Return StrTrim(FixCPET(NameField)) & ": " & StrTrim(FixCPET(ValField))
EndFunc
AkulaBig
Сообщения: 639
Зарегистрирован: Ср дек 03, 2008 11:48

Сообщение AkulaBig »

Вернул 60 колонок для подсказки. Точно глюки не с количеством столбцов связаны. А вот очень похоже, что проблема со скоростью перекодирования.
Аватара пользователя
Orion9
Сообщения: 1283
Зарегистрирован: Пн янв 01, 2024 17:18

Сообщение Orion9 »

AkulaBig писал(а):Похоже больше 60 колонок не поддерживается
Всё работает. Нужно просто добавить новые поля:

Код: Выделить всё

Pragma AutorunPluginFields "C1:::MediaHint" "C2:::MediaHint" "C3:::MediaHint" "C4:::MediaHint" "C5:::MediaHint" "C6:::MediaHint" "C7:::MediaHint" "C8:::MediaHint" "C9:::MediaHint" "C10:::MediaHint" "C11:::MediaHint" "C12:::MediaHint" "C13:::MediaHint" "C14:::MediaHint" "C15:::MediaHint" "C16:::MediaHint" "C17:::MediaHint" "C18:::MediaHint" "C19:::MediaHint" "C20:::MediaHint" "C21:::MediaHint" "C22:::MediaHint" "C23:::MediaHint" "C24:::MediaHint" "C25:::MediaHint" "C26:::MediaHint" "C27:::MediaHint" "C28:::MediaHint" "C29:::MediaHint" "C30:::MediaHint" "C31:::MediaHint" "C32:::MediaHint" "C33:::MediaHint" "C34:::MediaHint" "C35:::MediaHint" "C36:::MediaHint" "C37:::MediaHint" "C38:::MediaHint" "C39:::MediaHint" "C40:::MediaHint" "C41:::MediaHint" "C42:::MediaHint" "C43:::MediaHint" "C44:::MediaHint" "C45:::MediaHint" "C46:::MediaHint" "C47:::MediaHint" "C48:::MediaHint" "C49:::MediaHint" "C50:::MediaHint" "C51:::MediaHint" "C52:::MediaHint" "C53:::MediaHint" "C55:::MediaHint" "C55:::MediaHint" "C56:::MediaHint" "C57:::MediaHint" "C58:::MediaHint" "C59:::MediaHint" "C60:::MediaHint" "C61:::MediaHint" "C62:::MediaHint" "C63:::MediaHint" "C64:::MediaHint" "C65:::MediaHint" "C66:::MediaHint" "C67:::MediaHint" "C68:::MediaHint" "C69:::MediaHint" "C70:::MediaHint" "C71:::MediaHint" "C72:::MediaHint" "C73:::MediaHint" "C74:::MediaHint" "C75:::MediaHint" "C76:::MediaHint" "C77:::MediaHint" "C78:::MediaHint" "C79:::MediaHint" "C80:::MediaHint" "C81:::MediaHint" "C82:::MediaHint" "C83:::MediaHint" "C85:::MediaHint" "C85:::MediaHint" "C86:::MediaHint" "C87:::MediaHint" "C88:::MediaHint" "C89:::MediaHint" "C90:::MediaHint" 
И не забыть про шаблон в HintsCustomField

Код: Выделить всё

27exts=*.*
27fields=[=autorun.C61]\n[=autorun.C62]\n[=autorun.C63]\n[=autorun.C64]\n[=autorun.C65]\n[=autorun.C66]\n[=autorun.C67]\n[=autorun.C68]\n[=autorun.C69]\n[=autorun.C70]\n[=autorun.C71]\n[=autorun.C72]\n[=autorun.C73]\n[=autorun.C74]\n[=autorun.C75]\n[=autorun.C76]\n[=autorun.C77]\n[=autorun.C78]\n[=autorun.C79]\n[=autorun.C80]\n[=autorun.C81]\n[=autorun.C82]\n[=autorun.C83]\n[=autorun.C84]\n[=autorun.C85]\n[=autorun.C86]\n[=autorun.C87]\n[=autorun.C88]\n[=autorun.C89]\n[=autorun.C90]
Но, как я писал, полей может оказаться слишком много, и подсказка начнет выезжать за экран даже на стандартном разрешении 1920х1080.

60 полей это нормальный компромисс, хотя нужно, конечно, сделать умный выбор в зависимости от настроек экрана.

Кстати, что-то подобное я пробовал делать, но только в качестве эксперимента

Код: Выделить всё

If SYSINFO_DESKTOPHEIGHT < 1080 Then gHintLines = 50
If SYSINFO_DESKTOPHEIGHT < 720 Then gHintLines = 40
Тогда в Autorun еще не было макроса SYSINFO_DESKTOPDPI, а dpi и масштабирование тоже придется учитывать.

Добавлено спустя 1 час 28 минут:
AkulaBig писал(а):Только не пойму, сейчас ведь тоже посимвольно
Это как раз построчно, причем здесь вся строка заменяется на ANSI, если встречается символ ����, что может привести к проблемам. Лучше использовать посимвольное преобразование.

Нашел один косяк, связанный с буфером. Поскольку количество байт на символ отличается в UTF8, то произошло, что и ожидалось: при склеивании буфера, симовол теряется и превращается в кракозябру. На картинке поле "Lame - Фильтр" плавает на стыке буферов

[cut]Изображение[/cut]

Пока просто поднял значение буфера то 10 Кб, но в будущем нужно будет переделать.

Код: Выделить всё

Static readBuf = Buffer(1024*20) 
Вот теперь всё как надо: выхлоп чистый как кристалл, мощный как из пушки

[cut]Изображение[/cut]
AkulaBig
Сообщения: 639
Зарегистрирован: Ср дек 03, 2008 11:48

Сообщение AkulaBig »

Orion9 писал(а):Вместо
Можете полностью функцию привести? Не понял, откуда вы этот кусок взяли. В сборке у вас тоже такого нет.
Аватара пользователя
Orion9
Сообщения: 1283
Зарегистрирован: Пн янв 01, 2024 17:18

Сообщение Orion9 »

AkulaBig писал(а):Можете полностью функцию привести?
Она выше была, но я ее переименовал в FixFFFD.

Чтобы применить ее к коду для взаимодействия через файлы, нужно заменить строку

Код: Выделить всё

If StrPos(u[i], wrong) Then u[i] = a[i]
На

Код: Выделить всё

If StrPos(u[i], wrong) Then u[i] = FixFFFD(u[i], a[i])
Но я бы добавил сюда один символ для обозначения перевода, чтобы лучше видеть что происходит

Код: Выделить всё

If StrPos(u[i], wrong) Then u[i] = FixFFFD(u[i], a[i]) & " " & wrong
Полная функция для работы через файлы с повторением функционала мигания и таймаута

[cut]

Код: Выделить всё

Func GetExifInfo(FileName)
    Static a = List(), u = List()
    Local ansi, utf8, wrong = Chr(0xFFFD)
    If Not gExifStay Then
       ProcessExecGetOutput ansi %gMediaExifX32% ' -charset filename=Russian -G -S -lang ru "%FileName%"'
    Else          
       Local gExifOut  = GetTempFileName()       
       Local gExifBusy = true
       
       Local sArgs = "-G0"   & auCRLF & _
                     "-lang" & auCRLF & _
                     "ru"    & auCRLF & _                     
                     "-W+!"  & auCRLF & gExifOut & auCRLF & FileName & auCRLF & "-execute" & auCRLF
                     
       Static obj = BinaryFile(gExifArgs, "a")    
       obj.WriteStr(sArgs, "", "UTF-8")
       
       ansi = "Exiftool <time-out>"
       For i = 1 To 10
           Sleep(50)
           If i = 10 And gExifBusy Then
              ExiftoolBusyHint("Exiftool <time-out>")
              i = 1
              Continue
           EndIf
           If FileExist(gExifOut) Then
              ansi = FileRead(gExifOut, 0, "ANSI")
              If ERROR = 1 Then Continue
              DllCall("kernel32.dll\DeleteFileW", "Wstr", gExifOut)              
              Break
           EndIf
       Next
    EndIf   
    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] = FixFFFD(u[i], a[i]) & " " & wrong
    Next
    Return u.text    
EndFunc
[/cut]
Добавлена If ERROR = 1 Then Continue после FileRead для более надежного чтения. Если опция gExifBusy отключена, подсказка будет скользить с таймаутом (второй вариант, который был выше), но нужно учитывать, что в таком случае временные файлы, созданные в промежутке, не будут удаляться, поэтому их придется вычищать вручную. Для начала немного изменить функцию GetTempFileName, чтобы лучше отличать файлы exiftool

Код: Выделить всё

Func GetTempFileName()
    Local tmpfile
    Do
      tmpfile = TEMP & "\exif_" & Hex(Random(1, 0xFFFFFF, 1), 6) & ".tmp"
    Until Not FileExist(tmpfile)
    Return tmpfile
EndFunc 
При выходе из Autorun должно быть что-то вроде

Код: Выделить всё

If gExifPID > 0 Then 
   ProcessTerminate("~/TT", gExifPID)
   Sleep(10)
   DllCall("kernel32.dll\DeleteFileW", "Wstr", gExifArgs)
   ShellExec /SW_HIDE %COMSPEC% '/c del /f /q exif_*.tmp' %TEMP%
EndIf 
чтобы подчистить все зависшие концы, если они там есть. Временные файлы могут зависнуть только если был таймаут на тяжелом или битом файле, в остальных случаях они сами удаляются.

Ну и нужно предусмотреть работу из двух и более процессов ТС, это отдельная песня, но конфликты могут быть.
Ответить