Страница 187 из 243

Добавлено: Сб май 10, 2025 14:14
AkulaBig
Orion9 писал(а):будь я на вашем месте, я сделал бы верхнее меню
Я уже забыл, верхнее меню это что у нас?

Добавлено: Вт май 13, 2025 17:30
Orion9
AkulaBig писал(а):Я уже забыл, верхнее меню это что у нас?
Это то, к чему Гислер за 30 лет не сделал никакого GUI, хотя именно это нужно было делать в первую очередь, а не отмазываться от пользователя неудобным меню "Запуск", которое, как выяснилось, имеет еще и кривую многоязычную поддержку, исправлять которую он теперь не желает. Как-то так.

Думал, что еще не скоро возьмусь за Autorun, но понадобилась одна фича, над которой сейчас работаю. Простенький монитор каталога, чтобы отслеживал изменения и сообщал об удаленных файлах в реальном времени. Попутно сделал кнопку для подсчета времени воспроизведения в каталоге. Что-то подобное делает плагин MediaTime. При желании можно полностью заменить его функционал, если не устраивает одиночное нажатие кнопки:
[cut]TOTALCMD#BAR#DATA
62013

%COMMANDER_EXE%
Подсчёт времени воспроизведения


-1
[/cut]
[cut]

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

RegisterCommand 62013 "DurationInfo"

Func DurationInfo(lParam)
    Local name = RequestCopyDataInfo("SN")
    Local file = RequestCopyDataInfo("SP") & RequestCopyDataInfo("SN")   
    If Not FileExist(file) Then 
       ShowHint("Файл не существует " & file)
       Return
    Endif
    Local obj = Plugin("TCMediaInfo")
    If ERROR <> 0 Then
       ShowHint("TCMediaInfo plugin error " & ERROR)
       Return
    Endif
    Local s = 0
    If Not StrPos(FileGetAttr(file), "D") Then
       obj.FileName = file     
       s = obj.GetValue(0)	# duration   
       ShowHint("Файл: " & name & auCRLF & "Длительность: " & s)
       Free(obj)       
       Return 
    Endif
    Local i, out, files = 0, aFiles = List(), seconds = 0
    ShowHint("Подсчёт времени")
    # загрузка файлов в массив    
    ProcessExecGetOutput /OEM out %COMSPEC% "/c dir /s /b *.*" %"file"
    aFiles.Text = out
    # ShowHint("Обработка " & aFiles.Count & " элементов")
    For i = 0 To aFiles.Count - 1
        obj.FileName = aFiles[i]
        s = obj.GetValue(0,2)	# duration -> seconds 
        If s > 0 Then
           seconds += s
           files += 1
        EndIf
    Next
    Free(obj)    
    Local day = Floor(seconds / 86400)
    Local hour = Floor((seconds - (day * 86400)) / 3600)
    Local min = Floor(((seconds - (day * 86400)) - (hour * 3600)) / 60)
    Local sec = seconds - (day * 86400 + hour * 3600 + min * 60)
    Local total = StrFormat("%02d:%02d:%02d:%02d",day,hour,min,sec)
    ShowHint("Каталог: " & name & auCRLF & _
             "Элементов: " & aFiles.Count & auCRLF & _
             "Обработано: " & files & auCRLF & _
             "Секунд: " & seconds & auCRLF & _
             "Минут: " & Round(seconds / 60, 2) & auCRLF & _
             "Часов: " & Round(seconds / 3600, 2) & auCRLF & _
             "Суток: " & Round(seconds / 86400, 2) & auCRLF & _             
             "Длительность: " & total)
EndFunc
[/cut]
Функция полностью завязана на плагин TCMediaInfo и его поля, хотя, конечно, можно было и через DllCall сделать, но использование объекта Plugin сводит весь код на Autorun до смешных нескольких строк.

Добавлено: Вт май 13, 2025 20:40
AkulaBig
Orion9 писал(а):Функция полностью завязана на плагин TCMediaInfo и его поля, хотя, конечно, можно было и через DllCall сделать
У меня наоборот, что-то возникло желание переделать пользовательские колонки на Dll, отказавшись от TCMediaInfo. Его использовать только как листер-плагин. Как-бы в моем понимании это даст какую-то упорядоченность.
Только вот навряд-ли Autorun выдержит еще полей 30. Думаю в данном случае как-раз попробовать использовать файл типа hint.txt или массив значений.

Добавлено: Ср май 14, 2025 23:32
Orion9
AkulaBig писал(а):отказавшись от TCMediaInfo
Имхо, TCMediaInfo один из лучших плагинов, не понятно, зачем от него отказываться. Я наоборот хотел с ним лучше разобраться, т.к. он у меня в подвисшем состоянии остался.
AkulaBig писал(а):Только вот навряд-ли Autorun выдержит еще полей 30.
Да поля-то он, наверное, выдержит, только все-равно не понятно, зачем заменять один плагин другим. В скорости, например, от этого вряд ли получится выиграть, я как раз в этом убедился сейчас.

Пришлось переделать функцию подсчета, убрав из неё получение списка через ProcessExecGetOutput, т.к. этот вариант не работает с UNC-путями. Попутно сделал вызов через DllCall и расширил функционал. Сейчас вызов с ALT делает подсчет через плагин TCMediaInfo.wdx, а простой вызов через MediaInfo.dll. Поскольку MediaInfo.dll обрабатывает все файлы (даже те, которые не-медиа, что замедляет работу), пришлось добавить фильтр. При включеном CapsLock фильтр отключается. Вызов с CTRL ограничивает глубину каталога до первого уповня. SHIFT - выводит в окно отладчика информацию о пропущенных элементах.

[cut]

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

RegisterCommand 62013 "Duration"

Func Duration(lParam)
    RunThread DurationInfo
EndFunc

Func DurationInfo()
    Local T1 = GetUptime()
    Local b_Ctrl = IsPressed(0x11), b_Shift = IsPressed(0x10), b_Alt = IsPressed(0x12)
    Local b_Caps = BitAND(DllCall("GetKeyState", "int", 0x14, "short"), 1)
    Local name = RequestCopyDataInfo("SN")
    Local file = RequestCopyDataInfo("SP") & name
    If Not FileExist(file) Then 
       ShowHint("Файл не существует " & file)
       Return
    Endif
    Local hMI = 0
    Static sLib = "MediaInfo" & (auX64 ? "" : "_i386") & ".dll", _
           hLib = DllCall("LoadLibrary", "wstr", COMMANDER_PATH & "\Ini\Tools\Libs" & sLib, "ptr")
    Static sExt = ".aac .ac3 .aif .aiff .aifc .afc .ape .au .snd .cda .dsf .dts .dtswav .dtshd .dtsma .eac3 .flac .fla _
                   .m1a .m2a .mka .mpa .mp1 .mp2 .mp3 .mp4 .m4a .m4b .m4r .mod .mpc .mp+ .mpp .ogg .ogg .oga .ogx .ogg _
                   .ra .spx .opus .qoa .svx .8svx .tak .tta .wav .wave .w64 .bwf .rf64 .wma .wv .avi .wmv .wmp .wm .asf _
                   .mpg .mpeg .mpe .m1v .m2v .mpv2 .mp2v .ts .tp .tpr .trp .vob .ifo .ogm .ogv .mp4 .m4v .m4p .m4b .3gp _
                   .3gpp .3g2 .3gp2 .mkv .rm .ram .rmvb .rpm .flv .swf .mov .qt .amr .nsv .dpg .m2ts .m2t .mts .dvr-ms _
                   .k3g .skm .evo .nsr .amv .divx .webm .wtv .f4v .mxf"
    If Not b_Alt Then           
       If hLib = 0 Then 
          ShowHint("Error LoadLibrary " & sLib)
          Return 
       EndIf
       hMI = DllCall(sLib & '\MediaInfo_New', "Ptr")
       If hMI = 0 Then 
          ShowHint("MediaInfo failed to create new object")
          Return
       Endif
    Else     
       Local obj = Plugin("TCMediaInfo")
       If ERROR <> 0 Then
          ShowHint("TCMediaInfo plugin error " & ERROR)
          Return
       Endif
    EndIf   
    Local s = 0
    If Not StrPos(FileGetAttr(file), "D") Then
       If b_Alt Then
          obj.FileName = file     
          s = obj.GetValue(0)	# duration
          Free(obj)
       Else
          If DllCall(sLib & '\MediaInfo_Open', 'ptr', hMI, "wstr", file, "uint") <> 1 Then
             Return ShowHint("MediaInfo_Open failed to open file " &  file)                            
          EndIf
          s = DllCall(sLib & '\MediaInfo_Get', _
              'ptr', hMI, "int", 0, "int", 0, "wstr", "Duration/String3", "int", 1, "int", 0, "wstr")
       EndIf   
       Return ShowHint("Файл: " & name & auCRLF & "Длительность: " & s) 
    Endif
    Local i, out, dirs = 0, files = 0, errs = 0, aFiles = List(), seconds = 0
    Local depth = (b_Ctrl ? 1 : 0)    
    ShowHint("Depth: " & depth & auCRLF & _
             "CapsLock: " & (b_Caps ? "yes" : "no") & auCRLF & _
             (b_Alt ? "TCMediaInfo.wdx" : "MediaInfo.dll") & auCRLF & _              
              "Подсчёт времени")
    b_Win = false
    # загрузка файлов в массив    
    If b_Win Then
       #ProcessExecGetOutput /OEM out %COMSPEC% "/c dir /s /b *.*" %"file"
       #aFiles.Text = out
    Else
       ListDirectory(file, aFiles, depth)
    EndIf   
    # ShowHint("Обработка " & aFiles.Count & " элементов")
    For i = 0 To aFiles.Count - 1
        If StrPos(FileGetAttr(aFiles[i]), "D") Then
           dirs += 1
           If b_Shift Then OutputDebugString("Duration:Directory:" & aFiles[i])
           Continue 
        EndIf        
        If b_Alt Then
           obj.FileName = aFiles[i]
           s = obj.GetValue(0,3)	# duration -> milliseconds
        Else
           ext = FileGetExt(aFiles[i])
           If Not b_Caps Then
              If Not StrPos(sExt, '.' & ext) Then 
                  If b_Shift Then OutputDebugString("Duration:Filter:" & aFiles[i])
                 Continue
              EndIf
           EndIf
           If DllCall(sLib & '\MediaInfo_Open', 'ptr', hMI, "wstr", aFiles[i], "uint") <> 1 Then
              errs += 1
              If b_Shift Then OutputDebugString("Duration:Error:" & aFiles[i])
              Continue        
           EndIf
           s = DllCall(sLib & '\MediaInfo_Get', _
               'ptr', hMI, "int", 0, "int", 0, "wstr", "Duration", "int", 1, "int", 0, "wstr")     
        EndIf
        If s > 0 Then
           seconds += s
           files += 1
        Else
           If b_Shift Then OutputDebugString("Duration:Empty:" & aFiles[i])
        EndIf
        If IsPressed(0x11) And IsPressed(0x12) Then 
           ShowHint((b_Alt ? "TCMediaInfo.wdx" : "MediaInfo.dll") & auCRLF & _
                     "CapsLock: " & (b_Caps ? "yes" : "no") & auCRLF & _
                     "Depth: " & depth & auCRLF & _
                     "File: " & i & " of " & aFiles.Count & auCRLF & _
                     aFiles[i])
        EndIf             
    Next
    If b_Alt Then Free(obj)
    If hMI <> 0 Then DllCall(sLib & '\MediaInfo_Delete', "Ptr", hMI)    
    seconds = Floor(seconds / 1000)
    Local day = Floor(seconds / 86400)
    Local hour = Floor((seconds - (day * 86400)) / 3600)
    Local min = Floor(((seconds - (day * 86400)) - (hour * 3600)) / 60)
    Local sec = seconds - (day * 86400 + hour * 3600 + min * 60)
    Local total = StrFormat("%02d:%02d:%02d:%02d",day,hour,min,sec)
    ShowHint("Папка: " & name & auCRLF & _
             "Каталогов: " & dirs & auCRLF & _    
             "Элементов: " & aFiles.Count & auCRLF & _
             "Обработано: " & files & auCRLF & _
             "Ошибки DLL: " & errs & auCRLF & _
             "Секунд: " & seconds & auCRLF & _
             "Минут: " & Round(seconds / 60, 2) & auCRLF & _
             "Часов: " & Round(seconds / 3600, 2) & auCRLF & _
             "Суток: " & Round(seconds / 86400, 2) & auCRLF & _             
             "Длительность: " & total & auCRLF & _
             "Powered by " & (b_Alt ? "TCMediaInfo.wdx" : "MediaInfo.dll") & auCRLF & _
             "Время операции: " &  Round(GetUptime() - T1, 3))
    Free(aFiles)
EndFunc

Func ListDirectory(sPath, ByRef aList, nDepth = 1)
    Local sFile, nAttr
    Local ffd = Buffer(604)
    Local hf = DllCall("FindFirstFileW", "wstr", sPath & "\*.*", "ptr", ffd.Ptr)
    If hf <> 0 then 
        While True 
            sFile = ffd.GetStr(44)         # cFileName
            nAttr = ffd.GetNum(0, "dword") # dwFileAttributes
            If BitAND(nAttr, 16) Then 
               If Not ((sFile = ".") Or (sFile = "..")) Then 
                  aList.Add(sPath & "" & sFile)
                  If nDepth <> 1 Then
                     ListDirectory(sPath & "" & sFile, aList, nDepth - 1)
                  EndIf   
               EndIf
            Else
               aList.Add(sPath & "" & sFile)
            Endif     
            If DllCall("FindNextFileW", "handle", hf, "ptr", ffd.Ptr) = 0 Then Break
        Wend
        DllCall("FindClose", "handle", hf)
    Endif 
    Free(ffd)
EndFunc
[/cut]

Добавлено: Чт май 15, 2025 06:09
AkulaBig
Orion9 писал(а):он у меня в подвисшем состоянии остался
В этом все и дело. Если зайдете в его тему, увидите, что мы там обсуждали некоторые проблемы. Не все гладко с файлом настройки. Вот и получается, что либо там разбираться с настройками, либо здесь пойти по уже проторенной дорожке. Ну а листер-плагину TCMediaInfo альтернативы нет.

Добавлено: Чт май 15, 2025 13:06
Orion9
AkulaBig писал(а):либо там разбираться с настройками, либо здесь пойти по уже проторенной дорожке
Так ведь плагин TCMediaInfo и есть проторенная дорожка. Вопрос здесь больше в изучении самой MediaInfo.dll и её возможностей, чтобы лучше понимать что и в каких случаях выводить. А для этого придётся глубже копнуть в форматы файлов и их устройство, а не только в изучение настроек XML.

Откинул меня подсчёт времени воспроизведения, но вроде доделал как надо, хотя нужно больше тестов. Сейчас повторное нажатие на кнопку прерывает операцию, как и комбинация Win + ESC. Зажатие CTRL + ALT во время подсчёта показывает прогресс операции в подсказке. Обнаружил одну неприятную проблему. MediaInfo.dll может зависнуть на некоторых файлах и повесить ТС. Пока мне попался один такой файл, который нормально проигрывается, но видимо с тегами что-то не так. Foobar при проверке целостности пишет: Warning: Multiple ID3v2 tags encountered, item decoded with minor problems. Программа Mp3Tag пишет: ID3v2.3 ERROR: MP3 header parse error. В остальном проблем больше не заметил. Последняя версия кнопки:


[cut]

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

RegisterCommand 62013 "Duration"

Global MI_DURATION = 0, MI_BREAK = false

Func Duration(lParam)
    If MI_DURATION > 0 Then
       MI_BREAK = true
       Sleep(250)
       Return
    EndIf
    RunThread DurationInfo
EndFunc

Func DurationInfo()
    Local T1 = GetUptime(), T2 = 0
    Local b_Ctrl = IsPressed(0x11), b_Shift = IsPressed(0x10), b_Alt = IsPressed(0x12)
    Local b_Caps = BitAND(DllCall("GetKeyState", "int", 0x14, "short"), 1)
    Local name = RequestCopyDataInfo("SN")
    Local file = RequestCopyDataInfo("SP") & name
    If Not FileExist(file) Then 
       ShowHint("Файл не существует " & file)
       Return
    Endif
    Local hMI = 0
    Static sLib = "MediaInfo" & (auX64 ? "" : "_i386") & ".dll", _
           hLib = DllCall("LoadLibrary", "wstr", COMMANDER_PATH & "\Ini\Tools\Libs" & sLib, "ptr")
    Static sExt = ".aac .ac3 .aif .aiff .aifc .afc .ape .au .snd .cda .dsf .dts .dtswav .dtshd .dtsma .eac3 _
                   .flac .fla .m1a .m2a .mka .mpa .mp1 .mp2 .mp3 .mp4 .m4a .m4b .m4r .mod .mpc .mp+ .mpp _
                   .oga .ogx .ogg .ra .spx .opus .qoa .svx .8svx .tak .tta .wav .wave .w64 .bwf .rf64 .wma .wv _
                   .avi .wmv .wmp .wm .asf .mpg .mpeg .mpe .m1v .m2v .mpv2 .mp2v .ts .tp .tpr .trp .vob .ifo _
                   .ogm .ogv .mp4 .m4v .m4p .m4b .3gp .3gpp .3g2 .3gp2 .mkv .rm .ram .rmvb .rpm .flv .swf .mov _
                   .qt .amr .nsv .dpg .m2ts .m2t .mts .dvr-ms .k3g .skm .evo .nsr .amv .divx .webm .wtv .f4v .mxf"
    If Not b_Alt Then           
       If hLib = 0 Then 
          ShowHint("Error LoadLibrary " & sLib)
          Return 
       EndIf
       hMI = DllCall(sLib & '\MediaInfo_New', "Ptr")
       If hMI = 0 Then 
          ShowHint("MediaInfo.dll failed to create new object")
          Return
       Endif
    Else     
       Local obj = Plugin("TCMediaInfo")
       If ERROR <> 0 Then
          ShowHint("TCMediaInfo.wdx plugin error " & ERROR)
          Return
       Endif
    EndIf   
    Local s = 0
    If Not StrPos(FileGetAttr(file), "D") Then
       If b_Alt Then
          obj.FileName = file     
          s = obj.GetValue(0)	# duration
          Free(obj)
       Else
          If DllCall(sLib & '\MediaInfo_Open', 'ptr', hMI, "wstr", file, "uint") <> 1 Then
             Return ShowHint("MediaInfo.dll failed to open file " &  file)                            
          EndIf
          s = DllCall(sLib & '\MediaInfo_Get', _
              'ptr', hMI, "int", 0, "int", 0, "wstr", "Duration/String3", "int", 1, "int", 0, "wstr")
       EndIf   
       Return ShowHint("Файл: " & name & auCRLF & "Длительность: " & s) 
    Endif
    ShowHint("Загрузка")
    MI_BREAK = false
    MI_DURATION = 1    
    Local i, out, dirs = 0, files = 0, errs = 0, aFiles = List(), seconds = 0, b_Hint = 0
    Local depth = (b_Ctrl ? 1 : 0)  
    b_Win = false
    # загрузка файлов в массив    
    If b_Win Then
       #ProcessExecGetOutput /OEM out %COMSPEC% "/c dir /s /b *.*" %"file"
       #aFiles.Text = out
    Else
       ListDirectory(file, aFiles, depth)
    EndIf
    If aFiles.Count > 1 Then
       ShowHint("Depth: " & depth & auCRLF & _
                "CapsLock: " & (b_Caps ? "Yes" : "No") & auCRLF & _
                (b_Alt ? "TCMediaInfo.wdx" : "MediaInfo.dll") & auCRLF & _
                "Элементов: " & aFiles.Count & auCRLF & _                     
                "Подсчёт времени...")
    EndIf
    For i = 0 To aFiles.Count - 1
        #OutputDebugString("Duration:File:" & aFiles[i])
        If StrPos(FileGetAttr(aFiles[i]), "D") Then
           dirs += 1
           If b_Shift Then OutputDebugString("Duration:Directory:" & aFiles[i])
           Continue 
        EndIf        
        If b_Alt Then
           obj.FileName = aFiles[i]
           s = obj.GetValue(0,3)	# duration -> milliseconds
        Else
           ext = FileGetExt(aFiles[i])
           If Not b_Caps Then
              If Not StrPos(sExt, '.' & ext) Then 
                  If b_Shift Then OutputDebugString("Duration:Filter:" & aFiles[i])
                 Continue
              EndIf
           EndIf
           If DllCall(sLib & '\MediaInfo_Open', 'ptr', hMI, "wstr", aFiles[i], "uint") <> 1 Then
              errs += 1
              If b_Shift Then OutputDebugString("Duration:Error:" & aFiles[i])
              Continue        
           EndIf
           s = DllCall(sLib & '\MediaInfo_Get', _
               'ptr', hMI, "int", 0, "int", 0, "wstr", "Duration", "int", 1, "int", 0, "wstr")     
        EndIf
        If s > 0 Then
           seconds += s
           files += 1
        Else
           If b_Shift Then OutputDebugString("Duration:Empty:" & aFiles[i])
        EndIf
        If MI_BREAK = true Or (IsPressed (0x11) And IsPressed (0x1B)) Then
           ShowHint("Операция прервана")
           Sleep(700)
           Break
        EndIf
        If IsPressed(0x11) And IsPressed(0x12) Then
           b_Hint += 1
           If Round(GetUptime() - T2, 0) > 500 Then
              ShowHint( _
                         (b_Alt ? "TCMediaInfo.wdx" : "MediaInfo.dll") & auCRLF & _
                         "CapsLock: " & (b_Caps ? "Yes" : "No") & auCRLF & _
                         "Depth: " & depth & auCRLF & _
                         "File: " & i & " of " & aFiles.Count _
                       )
              T2 = GetUptime()
            EndIf
        Else 
            If b_Hint > 0 Then
               b_Hint = 0
               ShowHint("Depth: " & depth & auCRLF & _
                        "CapsLock: " & (b_Caps ? "Yes" : "No") & auCRLF & _
                        (b_Alt ? "TCMediaInfo.wdx" : "MediaInfo.dll") & auCRLF & _
                        "Элементов: " & aFiles.Count & auCRLF & _                     
                        "Подсчёт времени...")
            EndIf 
        EndIf             
    Next    
    If b_Alt Then Free(obj)
    If hMI <> 0 Then DllCall(sLib & '\MediaInfo_Delete', "Ptr", hMI)
    MI_BREAK = false
    MI_DURATION = 0
    seconds = Floor(seconds / 1000)
    Local day = Floor(seconds / 86400)
    Local hour = Floor((seconds - (day * 86400)) / 3600)
    Local min = Floor(((seconds - (day * 86400)) - (hour * 3600)) / 60)
    Local sec = seconds - (day * 86400 + hour * 3600 + min * 60)
    Local total = StrFormat("%02d:%02d:%02d:%02d",day,hour,min,sec)
    ShowHint("Папка: " & name & auCRLF & _
             "Каталогов: " & dirs & auCRLF & _    
             "Элементов: " & aFiles.Count & auCRLF & _
             "Обработано: " & files & auCRLF & _
             "Ошибки DLL: " & errs & auCRLF & _
             "Секунд: " & seconds & auCRLF & _
             "Минут: " & Round(seconds / 60, 2) & auCRLF & _
             "Часов: " & Round(seconds / 3600, 2) & auCRLF & _
             "Суток: " & Round(seconds / 86400, 2) & auCRLF & _             
             "Время: " & Chr(0x2211) & " "& total & auCRLF & _
             "Powered by " & (b_Alt ? "TCMediaInfo.wdx" : "MediaInfo.dll") & auCRLF & _
             "Время операции: " &  Round(GetUptime() - T1, 3))
    Free(aFiles)
EndFunc

Func ListDirectory(sPath, ByRef aList, nDepth = 1)
    Local sFile, nAttr
    Local ffd = Buffer(604)
    Local hf = DllCall("FindFirstFileW", "wstr", sPath & "\*.*", "ptr", ffd.Ptr)
    If hf <> 0 then 
        While True 
            sFile = ffd.GetStr(44)         # cFileName
            nAttr = ffd.GetNum(0, "dword") # dwFileAttributes
            If BitAND(nAttr, 16) Then 
               If Not ((sFile = ".") Or (sFile = "..")) Then 
                  aList.Add(sPath & "" & sFile)
                  If nDepth <> 1 Then
                     ListDirectory(sPath & "" & sFile, aList, nDepth - 1)
                  EndIf   
               EndIf
            Else
               aList.Add(sPath & "" & sFile)
            Endif     
            If DllCall("FindNextFileW", "handle", hf, "ptr", ffd.Ptr) = 0 Then Break
        Wend
        DllCall("FindClose", "handle", hf)
    Endif 
    Free(ffd)
EndFunc
[/cut]

Добавлено: Чт май 15, 2025 14:05
AkulaBig
Orion9 писал(а):придётся глубже копнуть в форматы файлов и их устройство
Копай, не копай. Раз тэги заполняют люди, все равно не угадаешь с заполненными полями. Собственно ведь поля тэгов для всех этих расширений одинаковы.
Orion9 писал(а):а не только в изучение настроек XML
Дак это еще один язык программирования нестандартный. Получается язык Autorun надо изучить и язык TCMediaInfo. Для молодых и умных может и нет проблем. А у нас время ограничено. Не очень интересно этим заниматься.

Добавлено: Чт май 15, 2025 23:32
Orion9
AkulaBig писал(а):Собственно ведь поля тэгов для всех этих расширений одинаковы
MediaInfo.dll больше, чем просто тэги. Гораздо важнее техническая информация, которую дает эта библиотека. И если задача просто вывести некоторые поля из MediaInfo.dll, то, думаю, TCMediaInfo.wdx вполне для этого достаточно. А то, что там используется свой язык, так это только гибкости добавляет, на мой взгляд.

Все, готов монитор. Очень простенький, конечно, но зато рабочий.

Отдельный модуль:
[cut="Monitor.aucfg"]

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

Global DIR_MONITOR1 = "D:\Temp\Test"
Global DIR_MONITOR2 = COMMANDER_PATH & "\Ini"
Global DIR_MONITOR3 = "\\Server\Share\Test"

RegisterCommand 81000 "DirMonitorCmd"
RegisterCommand 81001 "DirMonitorMenu"

Func DirMonitorCmd(lParam)
    DirMonitor(DIR_MONITOR1, 0)
EndFunc

Func DirMonitor(FolderName, FolderDepth)
    Local sLog, sChanges, sOps
    # счётчик, лог, снимок, каталог
    Static c = 0, sLogFile = TEMP & "\tc_dir_monitor.log", _
                  sMenuFile = TEMP & "\tc_dir_changes.log", _
                  sSnapFile = TEMP & "\tc_dir_snap.lst", sDir = FolderName
    # список файлов и сохраненный список
    Static aFiles = List(), aSnap = List()
    # список новых и удаленных файлов
    Static aNewFiles = List(), aDelFiles = List()
    c += 1
    # смена каталога в текущей сессии
    If sDir <> FolderName Then
       c = 1
       sDir = FolderName
       aSnap.Count = 0       
       aFiles.Count = 0
       aNewFiles.Count = 0
       aDelFiles.Count = 0
    EndIf
    # первый запуск монитора
    If c = 1 Then
       sLog &= auCRLF & auCRLF
       sLog &= LogFormat(1, "Мониторинг каталога " & sDir)
       If FileExist(sSnapFile) Then
          sLog &= LogFormat(1, "Загрузка снимка " & sSnapFile)       
          aSnap.LoadFromFile(sSnapFile)
          # проверка снимка      
          If aSnap.Count > 0 And Not StrPos(aSnap[0], sDir) Then
             sLog &= LogFormat(1, "Снимок не содержит предыдущих записей")
             aSnap.Count = 0
          Endif
          If aSnap.Count > 0 Then
             sLog &= LogFormat(1, "Снимок загружен: " & aSnap.Count & " элементов")          
          EndIf
       Else   
          sLog &= LogFormat(1, "Снимок не обнаружен")               
       EndIf   
    Endif
    sLog &= LogFormat(1, "Проверка N " & c)
    # каталог не доступен   
    If Not FileExist(sDir) Then 
       sLog &= LogFormat(1, "Каталог не доступен.")
       Return
    Endif
    Local out  
    # загрузка файлов в массив
    aFiles.Count = 0
    ListDirectory(sDir, aFiles, FolderDepth)
    sLog &= LogFormat(1, "Файлы взяты в массив: " & aFiles.Count & " элементов")
    # каталог пустой      
    If aFiles.Count = 0 Or Not StrPos(aFiles[0], sDir) Then
       sLog &= LogFormat(1, "Каталог не содержит записей.")
    Endif
    # обработка предыдущего списка
    Local a = 0, d = 0
    If aSnap.Count > 0 Then
       For i = 0 To aSnap.Count - 1
           If aFiles.IndexOf(aSnap[i]) = -1 Then 
              aDelFiles.Add(aSnap[i])
              sLog &= LogFormat(1, "Файл удален " & aSnap[i])
              d += 1
              sChanges &= LogFormat(1, " [-]  " & FileGetName(aSnap[i]))
              sOps &= FileGetName(aSnap[i]) & auCRLF
           EndIf
       Next
       For i = 0 To aFiles.Count - 1
           If aSnap.IndexOf(aFiles[i]) = -1 Then 
              aNewFiles.Add(aFiles[i])
              sLog &= LogFormat(1, "Файл добавлен " & aFiles[i])
              a += 1
              sChanges &= LogFormat(1, " [+]  " & FileGetName(aFiles[i]))
              sOps &= FileGetName(aFiles[i]) & auCRLF              
           EndIf
       Next       
    EndIf
    # новое состояние списка    
    aSnap = aFiles.Clone()
    aSnap.SaveToFile(TEMP & "\tc_dir_snap.lst")
    # фиксирование изменений
    If a > 0 Or d > 0 Then
       FileAppend(sMenuFile, sChanges)
       Local sMsg
       If d > 0 Then sMsg &= LogFormat(0, "Удалено: " & d)
       If a > 0 Then sMsg &= LogFormat(0, "Добавлено: " & a)
       sMsg &= sOps
       sLog &= LogFormat(1, "Изменения в каталоге:")
       sLog &= LogFormat(1, "Удалено: " & d)
       sLog &= LogFormat(1, "Добавлено: " & a)       
       NotifyInfoMessage(0, sMsg, "Монитор каталога", NIIF_INFO)
    Else
       sLog &= LogFormat(1, "Нет изменений в каталоге")
    Endif 
    FileAppend(sLogFile, sLog)          
EndFunc

Func LogFormat(DateTime, LogText)
    Local txt
    Switch DateTime 
    Case 0
       txt = StrFormat("%s\r\n", LogText)
    Case 1
       txt = StrFormat("%s %s %s\r\n", Date(), Time(), LogText)
    Case 2
       txt = StrFormat("%s %s\r\n", Date(), LogText)
    Case 3
       txt = StrFormat("%s %s\t%s\r\n", Date(), Time(), LogText)       
    Else
       txt = StrFormat("%s %s\r\n", Time(), LogText)    
    EndSwitch
    Return txt
EndFunc

#RunThread ThreadDirMonitor

Func ThreadDirMonitor()
    While 1
        #Sleep(180*60*1000)
        Sleep(5000)
        DirMonitor(DIR_MONITOR1, 0)
    Wend
EndFunc

Func DirMonitorMenu(lParam)
    ShowPopupMenu /D /F "CreateDirMonitorMenu"
EndFunc

Func CreateDirMonitorMenu()
    Local txt, ops
    Local buf = FileRead(TEMP & "\tc_dir_changes.log")
    If buf <> "" Then
       Local aLog = List(), i = 0, j, date = Date()
       aLog.Text = buf
       For j = aLog.Count - 1 To 0 Step -1
          i += 1
          ops &= 'MENUITEM "' & aLog[j] & '", ' & _
                              (StrPos(aLog[j], date) > 0 ? "em_dir_monitor_new" : "em_dir_monitor_old") & auCRLF
          If i > 20 Then Break
       Next
       Free(aLog)
    Else
       ops = 'MENUITEM "Empty", 100000'
    EndIf
    txt = 'MENUITEM "Журнал монитора...", em_dir_monitor_log' & auCRLF & _
          'MENUITEM "Журнал операций...", em_dir_monitor_ops' & auCRLF & _    
          'MENUITEM SEPARATOR' & auCRLF
    txt &= ops & auCRLF
    txt &= 'MENUITEM SEPARATOR' & auCRLF
    txt &= 'MENUITEM "Перейти к файлу монитора...", em_dir_monitor_2log' & auCRLF
    txt &= 'MENUITEM "Перейти к файлу операций...", em_dir_monitor_2ops' & auCRLF
    Return txt
EndFunc
[/cut]
Дополнительные функции:
[cut="ListDirectory"]

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

Func ListDirectory(sPath, ByRef aList, nDepth = 1)
    Local sFile, nAttr
    Local ffd = Buffer(604)
    Local hf = DllCall("FindFirstFileW", "wstr", sPath & "\*.*", "ptr", ffd.Ptr)
    If hf <> 0 then 
        While True 
            sFile = ffd.GetStr(44)         # cFileName
            nAttr = ffd.GetNum(0, "dword") # dwFileAttributes
            If BitAND(nAttr, 16) Then 
               If Not ((sFile = ".") Or (sFile = "..")) Then 
                  aList.Add(sPath & "" & sFile)
                  If nDepth <> 1 Then
                     ListDirectory(sPath & "" & sFile, aList, nDepth - 1)
                  EndIf   
               EndIf
            Else
               aList.Add(sPath & "" & sFile)
            Endif     
            If DllCall("FindNextFileW", "handle", hf, "ptr", ffd.Ptr) = 0 Then Break
        Wend
        DllCall("FindClose", "handle", hf)
    Endif 
    Free(ffd)
EndFunc
[/cut]
[cut="NotifyInfoMessage"]

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

# иконки окна уведомлений
Const NIIF_NONE    = 0x00000000, _
      NIIF_INFO    = 0x00000001, _
      NIIF_WARNING = 0x00000002, _
      NIIF_ERROR   = 0x00000003, _
      NIIF_USER    = 0x00000004
      
# без звука
Const NIIF_NOSOUND    = 0x00000010
# большая иконка
Const NIIF_LARGE_ICON = 0x00000020

# сообщения окна уведомлений
Const NIN_BALLOONSHOW      = 0x402, _
      NIN_BALLOONHIDE      = 0x403, _
      NIN_BALLOONTIMEOUT   = 0x404, _
      NIN_BALLOONUSERCLICK = 0x405, _
      NIN_POPUPOPEN        = 0x406, _
      NIN_POPUPCLOSE       = 0x407

# флаги структуры
Const NIF_MESSAGE = 0x00000001, _
      NIF_ICON    = 0x00000002, _
      NIF_TIP     = 0x00000004, _
      NIF_STATE   = 0x00000008, _
      NIF_INFO    = 0x00000010  

SetMessageAction /P 99998 TrayNotifyInfo 

# служебная иконка
Func TrayNotifyInfo(hWnd, uMsg, wParam, lParam)
    # освобождение невидимой иконки
    If lParam = NIN_BALLOONUSERCLICK or lParam = NIN_BALLOONTIMEOUT Then
       NotifyIcon("delete", 1010) 
    EndIf   
EndFunc

Func NotifyInfoMessage(Icon, InfoText, InfoTitle = "Autorun", InfoType = 0)
    Local dwFlags = 0
    Local buf = Buffer(auPtrSize = 4 ? 956 : 976)
    Local hIco = 0, tc = COMMANDER_PATH & "\TOTALCMD.EXE" 
    hIco = DllCall("shell32\ExtractIconW", "ptr", AUTORUN_TCHANDLE, "wstr", tc, "uint", 0, "ptr")    
    If Icon Then       
       dwFlags = BitOR(NIF_MESSAGE, NIF_ICON, NIF_INFO)
    Else
       dwFlags = BitOR(NIF_MESSAGE, NIF_STATE, NIF_ICON, NIF_INFO)
    Endif   
    buf.Zero()
    If auX64 Then
        buf.SetNum(0,  "dword", buf.size)
        buf.SetNum(8,  "hwnd", AUTORUN_TCHANDLE)
        buf.SetNum(16, "uint", 1010, _
                       "uint", dwFlags, _
                       "uint", 99998)             
        buf.SetNum(32, "ptr",  hIco)                       
        buf.SetNum(296, "dword", 1, _
                        "dword", 1)                            
    Else
        buf.SetNum(0, "dword", buf.size, _   
                      "hwnd", AUTORUN_TCHANDLE, _         
                      "uint", 1010, _
                      "uint", dwFlags, _
                      "uint", 99998, _
                      "ptr",  hIco) ) 
        buf.SetNum(280, "dword", 1, _
                        "dword", 1)

    EndIf
    Local offset = (auX64 ? 40 : 24) + 256 + 4 + 4
    buf.SetStr(StrLeft(InfoText, 199) & Chr(0), offset)
    If StrLen(InfoTitle) > 0 Then       
       buf.SetStr(StrLeft(InfoTitle, 47) & Chr(0), offset + 512 + 4)
    EndIf   
    If InfoType > 0 Then
       buf.SetNum(offset + 512 + 4 + 128, "dword", InfoType)
    EndIf
    Local Result = DllCall("Shell32.dll\Shell_NotifyIconW", "uint", 0, "ptr", buf.ptr)
    If hIco > 0 Then DllCall("DestroyIcon", "ptr", hIco)    
    Free(buf)
    Return Result
EndFunc
[/cut]
Последняя функция используется для показа системных сообщений без отображения значка в трее, но за ней были замечены проблемы на Win8.1 и Win10. Лучше взять доработанный пример из справки от Loopback, у меня времени не хватило, чтобы вставить его в модуль.

Функция следит за удаленными и добавленными файлами и создает лог во временном каталоге. Также в %TEMP% создаются еще два файла, в которых хранится предыдущий снимок и журнал операций. Последний открывается в кнопке-меню по коду 81001. Чтобы украсить меню значками, используются em_ команды:

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

[em_dir_monitor_new]
button=%COMMANDER_PATH%\Ini\NewsBar\notify-yes.ico

[em_dir_monitor_old]
button=%COMMANDER_PATH%\Ini\NewsBar\notify-no.ico

[em_dir_monitor_log]
button=%COMMANDER_PATH%\Utilites\AkelPad\AkelPad.exe
cmd=%COMMANDER_PATH%\Utilites\AkelPad\AkelPad.exe
param=tc_dir_monitor.log
path=%TEMP%

[em_dir_monitor_ops]
button=%COMMANDER_PATH%\Utilites\AkelPad\AkelPad.exe
cmd=%COMMANDER_PATH%\Utilites\AkelPad\AkelPad.exe
param=tc_dir_changes.log
path=%TEMP%

[em_dir_monitor_2log]
button=%COMMANDER_EXE%,0
cmd=CD %TEMP%\tc_dir_monitor.log

[em_dir_monitor_2ops]
button=%COMMANDER_EXE%,0
cmd=CD %TEMP%\tc_dir_changes.log

Простой способ запустить монитор в отдельном потоке:

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

RunThread ThreadDirMonitor

Func ThreadDirMonitor()
    While 1
        Sleep(180*60*1000)
        DirMonitor(DIR_MONITOR1, 0)
    Wend
EndFunc
Раз в полчаса каталог DIR_MONITOR1 будет проверяться и изменения будут логироваться в файлы, сообщения об изменениях будут отображаться в системных уведомлениях.

Добавлено: Пт май 16, 2025 05:12
AkulaBig
Orion9 писал(а): И если задача просто вывести некоторые поля из MediaInfo.dll, то, думаю, TCMediaInfo.wdx вполне для этого достаточно.
Я выше написал. Там некоторые поля неправильно выводятся. Надо править его файл настройки xml.

Добавлено: Пт май 16, 2025 10:49
Orion9
AkulaBig писал(а):Я выше написал. Там некоторые поля неправильно выводятся.
У меня не было времени посмотреть, и сейчас тоже не могу, так как много разных дел появилось, но еще вернусь к этому позже.

Обнаружил первый небольшой косячок, забыл добавить FileAppend(sLogFile, sLog) перед Return:

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

# каталог не доступен   
    If Not FileExist(sDir) Then 
       sLog &= LogFormat(1, "Каталог не доступен.")
       FileAppend(sLogFile, sLog)          
       Return
    Endif
Это не важно, т.к. функцией вряд ли кто-то будет пользоваться в таком виде, я скинул данный код на случай новых идей и решений.

Добавлено: Ср май 21, 2025 17:23
Orion9
Доработал функцию подсчёта. Теперь может обрабатывать список выделенного, и ряд других улучшений:

[cut]TOTALCMD#BAR#DATA
62013

%COMMANDER_EXE%
Подсчёт времени воспроизведения|Alt - Использовать TCMediaInfo.wdx|Ctrl - Глубина каталога 1|Shift - Вывод информации в отладчик|CapsLock - Отключить фильтр расширений


-1
[/cut]
[cut]

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

RegisterCommand 62013 "Duration"

Global MI_DURATION = 0, MI_BREAK = false

Func Duration(lParam)
    If MI_DURATION > 0 Then
       MI_BREAK = true
       Sleep(250)
       Return
    EndIf
    RunThread DurationInfo
EndFunc

Func DurationInfo()
    Local T1 = GetUptime(), T2 = 0
    Local b_Ctrl = IsPressed(0x11), b_Shift = IsPressed(0x10), b_Alt = IsPressed(0x12)
    Local b_Caps = BitAND(DllCall("GetKeyState", "int", 0x14, "short"), 1)
    Local hMI = 0
    Static sLib = "MediaInfo" & (auX64 ? "" : "_i386") & ".dll", _
           hLib = DllCall("LoadLibrary", "wstr", COMMANDER_PATH & "\Ini\Tools\Libs\" & sLib, "ptr")
    Static sExt = ".aac .ac3 .aif .aiff .aifc .afc .ape .au .snd .cda .dsf .dts .dtswav .dtshd .dtsma .eac3 _
                   .flac .fla .m1a .m2a .mka .mpa .mp1 .mp2 .mp3 .mp4 .m4a .m4b .m4r .mod .mpc .mp+ .mpp _
                   .oga .ogx .ogg .ra .spx .opus .qoa .svx .8svx .tak .tta .wav .wave .w64 .bwf .rf64 .wma .wv _
                   .avi .wmv .wmp .wm .asf .mpg .mpeg .mpe .m1v .m2v .mpv2 .mp2v .ts .tp .tpr .trp .vob .ifo _
                   .ogm .ogv .mp4 .m4v .m4p .m4b .3gp .3gpp .3g2 .3gp2 .mkv .rm .ram .rmvb .rpm .flv .swf .mov _
                   .qt .amr .nsv .dpg .m2ts .m2t .mts .dvr-ms .k3g .skm .evo .nsr .amv .divx .webm .wtv .f4v .mxf"
    If Not b_Alt Then           
       If hLib = 0 Then 
          ShowHint("Error LoadLibrary " & sLib)
          Return 
       EndIf
       hMI = DllCall(sLib & '\MediaInfo_New', "Ptr")
       If hMI = 0 Then 
          ShowHint("MediaInfo.dll failed to create new object")
          Return
       Endif
    Else     
       Local obj = Plugin("TCMediaInfo")
       If ERROR <> 0 Then
          ShowHint("TCMediaInfo.wdx plugin error " & ERROR)
          Return
       Endif
    EndIf
    Local s = 0
    Local aSel = List()
          aSel.Text = GetSelectedItems(3, 0)       
    Local sPath = RequestCopyDataInfo("SP")    
    Local sName = RequestCopyDataInfo("SN")        
    Local sFile = sPath & sName
    If aSel.Count = 0 Then
       If Not FileExist(sFile) Then 
          ShowHint("Файл не существует " & sFile)
          Free(aSel)
          Return
       Endif
    EndIf       
    If aSel.Count = 0 And Not StrPos(FileGetAttr(sFile), "D") Then
       Free(aSel)
       If b_Alt Then
          obj.FileName = sFile     
          s = obj.GetValue(0)	# duration
          Free(obj)
       Else
          If DllCall(sLib & '\MediaInfo_Open', 'ptr', hMI, "wstr", sFile, "uint") <> 1 Then
             Return ShowHint("MediaInfo.dll failed to open file " &  sFile)                            
          EndIf
          s = DllCall(sLib & '\MediaInfo_Get', _
              'ptr', hMI, "int", 0, "int", 0, "wstr", "Duration/String3", "int", 1, "int", 0, "wstr")
       EndIf   
       Return ShowHint("Файл: " & sName & auCRLF & "Длительность: " & s) 
    Endif
    ShowHint("Загрузка")
    MI_BREAK = false
    MI_DURATION = 1    
    Local i, j, k, dirs = 0, files = 0, errs = 0, item = 0, aFiles = List(), seconds = 0, b_Hint = 0
    Local depth = (b_Ctrl ? 1 : 0)  
    b_Win = false
    # загрузка файлов в массив    
    If aSel.Count > 0 Then
       For j = 0 To aSel.Count - 1
          sFile = sPath & aSel[j]
          If FileExist(sFile) Then 
             k += 1
             aFiles.Add(sFile)
             If StrPos(FileGetAttr(sFile), "D") Then ListDirectory(sFile, aFiles, depth)
          EndIf   
       Next
       sName = "S/A: " & aSel.Count & "/" & k	# Selected / Available
    Else
       aFiles.Add(sFile)
       ListDirectory(sFile, aFiles, depth)
    EndIf
    Free(aSel)
    If aFiles.Count > 1 Then
       ShowHint("Depth: " & depth & auCRLF & _
                "CapsLock: " & (b_Caps ? "Yes" : "No") & auCRLF & _
                (b_Alt ? "TCMediaInfo.wdx" : "MediaInfo.dll") & auCRLF & _
                "Элементов: " & aFiles.Count & auCRLF & _                     
                "Подсчёт времени...")
    EndIf
    For i = 0 To aFiles.Count - 1
        If b_Win Then OutputDebugString("Duration.File:" & aFiles[i])
        If StrPos(FileGetAttr(aFiles[i]), "D") Then
           dirs += 1
           If b_Shift Then OutputDebugString("Duration.Directory:" & aFiles[i])
           Continue 
        EndIf
        item += 1    
        If b_Alt Then
           obj.FileName = aFiles[i]
           s = obj.GetValue(0,3)	# duration -> milliseconds
        Else
           ext = FileGetExt(aFiles[i])
           If Not b_Caps Then
              If Not StrPos(sExt, '.' & ext) Then 
                  If b_Shift Then OutputDebugString("Duration.Filter:" & aFiles[i])
                 Continue
              EndIf
           EndIf
           If DllCall(sLib & '\MediaInfo_Open', 'ptr', hMI, "wstr", aFiles[i], "uint") <> 1 Then
              errs += 1
              If b_Shift Then OutputDebugString("Duration.Error:" & aFiles[i])
              Continue        
           EndIf
           s = DllCall(sLib & '\MediaInfo_Get', _
               'ptr', hMI, "int", 0, "int", 0, "wstr", "Duration", "int", 1, "int", 0, "wstr")     
        EndIf
        If s > 0 Then
           seconds += s
           files += 1
        Else
           If b_Shift Then OutputDebugString("Duration.Empty:" & aFiles[i])
        EndIf
        If MI_BREAK = true Or (IsPressed (0x11) And IsPressed (0x1B)) Then
           ShowHint("Операция прервана")
           Sleep(700)
           Break
        EndIf
        If IsPressed(0x11) And IsPressed(0x12) Then
           b_Hint += 1
           If Round(GetUptime() - T2, 0) > 500 Then
              ShowHint( _
                         (b_Alt ? "TCMediaInfo.wdx" : "MediaInfo.dll") & auCRLF & _
                         "CapsLock: " & (b_Caps ? "Yes" : "No") & auCRLF & _
                         "Depth: " & depth & auCRLF & _
                         "File: " & i & " of " & aFiles.Count _
                       )
              T2 = GetUptime()
            EndIf
        Else 
            If b_Hint > 0 Then
               b_Hint = 0
               ShowHint("Depth: " & depth & auCRLF & _
                        "CapsLock: " & (b_Caps ? "Yes" : "No") & auCRLF & _
                        (b_Alt ? "TCMediaInfo.wdx" : "MediaInfo.dll") & auCRLF & _
                        "Элементов: " & aFiles.Count & auCRLF & _                     
                        "Подсчёт времени...")
            EndIf 
        EndIf             
    Next    
    If b_Alt Then Free(obj)
    If hMI <> 0 Then DllCall(sLib & '\MediaInfo_Delete', "Ptr", hMI)
    MI_BREAK = false
    MI_DURATION = 0
    seconds = Floor(seconds / 1000)
    Local day = Floor(seconds / 86400)
    Local hour = Floor((seconds - (day * 86400)) / 3600)
    Local min = Floor(((seconds - (day * 86400)) - (hour * 3600)) / 60)
    Local sec = seconds - (day * 86400 + hour * 3600 + min * 60)
    Local total = StrFormat("%02d:%02d:%02d:%02d",day,hour,min,sec)
    Local tl = Round(GetUptime() - T1, 0) / 1000    
    ShowHint("Имя: " & sName & auCRLF & _
             "Каталогов: " & dirs & auCRLF & _    
             "Элементов: " & aFiles.Count & auCRLF & _
             "Обработано: " & files & " из " & item & auCRLF & _
             "Ошибки DLL: " & errs & auCRLF & _
             "Секунды: " & seconds & auCRLF & _
             "Минуты: " & Round(seconds / 60, 2) & auCRLF & _
             "Часы: " & Round(seconds / 3600, 2) & auCRLF & _
             "Сутки: " & Round(seconds / 86400, 2) & auCRLF & _             
             "Время: " & Chr(0x2211) & " "& total & auCRLF & _
             "Powered by " & (b_Alt ? "TCMediaInfo.wdx" : "MediaInfo.dll") & auCRLF & _
             "Время операции: " &  StrFormat("%.3f", tl) & " sec")
    Free(aFiles)
EndFunc
[/cut]
Сделал заодно нечто подобное для торрент-файлов, но каталоги в глубину не обрабатываются, работает только с выделенными объектами на панели:

[cut]TOTALCMD#BAR#DATA
62014

%COMMANDER_EXE%
Подсчёт размера .torrent файлов


-1
[/cut]
[cut]

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

RegisterCommand 62014 "TorrentSize"

Func TorrentSize(lParam)
    Local T1 = GetUptime()
    Local obj = Plugin("TCTorrent")
    If ERROR <> 0 Then
       ShowHint("TCTorrent.wdx plugin error " & ERROR)
       Return
    Endif
    Local size = 0, files = 0
    Local aSel = List()
          aSel.Text = GetSelectedItems(3, 0)       
    Local sPath = RequestCopyDataInfo("SP")    
    Local sName = RequestCopyDataInfo("SN")        
    Local sFile = sPath & sName
    Local lRet = false
    If aSel.Count = 0 Then
       If Not FileExist(sFile) Then 
          ShowHint("Файл не существует " & sFile)
          lRet = true
       ElseIf StrPos(FileGetAttr(sFile), "D") Then
          ShowHint("Каталог " & sFile)
          lRet = true       
       Else
          obj.FileName = sFile
          files = obj.GetValue(1)
          size = obj.GetValue(2,0)	# size -> bytes
          Free(obj)
       Endif
    EndIf
    If lRet Then
       Free(obj, aSel)
       Return
    EndIf
    If aSel.Count > 10 Then
       ShowHint("Выделено: " & aSel.Count & auCRLF & "Подсчёт времени...")
    EndIf    
    Local j, k = 0, dirs = 0, torrs = 0
    If aSel.Count > 0 Then
       For j = 0 To aSel.Count - 1
          sFile = sPath & aSel[j]
          If FileExist(sFile) Then 
             k += 1
             If Not StrPos(FileGetAttr(sFile), "D") Then
               obj.FileName = sFile
               s = obj.GetValue(2,0)
               If s > 0 Then 
                  size += s
                  files += obj.GetValue(1)
                  torrs += 1 
               Endif  
             Else   
               dirs += 1
             Endif   
          EndIf
       Next
       sName = "S/A: " & aSel.Count & "/" & k	# Selected / Available
    Else
       Return ShowHint("" & sName & auCRLF & _
                       "Размер: " & SizeFormat(size, 1, 'G', 2, 1) & auCRLF & _
                       "Файлов: " & files) 
    EndIf
    Local tl = Round(GetUptime() - T1, 0) / 1000
    ShowHint((k = aSel.Count ? "" : "Файл " & sName & auCRLF) & _
             "Элементов: " & aSel.Count & auCRLF & _
             "Обработано: " & torrs & auCRLF & _
             "Каталогов: " & dirs & auCRLF & _              
             "Размер: " & SizeFormat(size, 0, 'M', 2) & auCRLF & _
             "Размер: " & SizeFormat(size, 0, 'G', 2) & auCRLF & _
             "Размер: " & SizeFormat(size, 0, 'T', 4) & auCRLF & _
             "Файлов: " & files & auCRLF & _                       
             "Powered by TCTorrent.wdx" & auCRLF & _
             "Время операции: " &  StrFormat("%.3f", tl) & " sec")
    Free(obj, aSel)    
EndFunc
[/cut]

Добавлено: Чт май 22, 2025 23:30
Orion9
ClipGet принимает максимум 16 Кб
Поправка: не 16, а скорее 32. Но DllCall как всегда выручает. По мотивам статьи:

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

Local hCB, pMem, obg, CF_UNICODETEXT = 13    
If DllCall("OpenClipboard", "handle", 0) Then
   hCB = DllCall("GetClipboardData", "uint", CF_UNICODETEXT, "handle")
   pMem = DllCall("GlobalLock", "handle", hCB, "ptr")
   obj = Buffer(1024*500, pMem)
   txt = obj.GetStr()
   Free(obj)
   DllCall("GlobalUnlock ", "handle", hCB)
   DllCall("CloseClipboard")
Else
   MsgBox("Clipboard Error" )
EndIf
Здесь буфер размером в 500 Кб. Правильно я понимаю, что это просто "окно" для чтения, а на самом деле память не выделяется?

Добавлено: Пт май 23, 2025 19:26
Loopback
Orion9
Проблема та же, что была в ProcessExecGetOutput. Доработаю, механизм уже есть. И надо будет проверить, в каких еще функциях может появиться текст большого размера на выходе. Навскидку это только GetWMIValue/WMIQuery.
Orion9 писал(а): что это просто "окно" для чтения, а на самом деле память не выделяется?
Да, она уже выделена GetClipboardData. Если вторым параметром указан адрес, то размер это просто границы диапазона. Для теста ок, но по-хорошему этот размер надо получать c помощью GlobalSize.

Добавлено: Пт май 23, 2025 19:34
Orion9
Вот же, а... Случайно отредактировал предыдущий пост, вместо отправки нового.

Loopback, там было про проблему с функцией ClipGet, которая режет текст из буфера. Используя DllCall удалось обойти это ограничение:

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

Local hCB, pMem, obg, CF_UNICODETEXT = 13    
If DllCall("OpenClipboard", "handle", 0) Then
   hCB = DllCall("GetClipboardData", "uint", CF_UNICODETEXT, "handle")
   pMem = DllCall("GlobalLock", "handle", hCB, "ptr")
   obj = Buffer(1024*500, pMem)
   txt = obj.GetStr()
   Free(obj)
   DllCall("GlobalUnlock ", "handle", hCB)
   DllCall("CloseClipboard")
Else
   MsgBox("Clipboard Error" )
EndIf
Но в приведенном коде используется другое ограничение буфера размером в 500 Кб. Правильно я понимаю, что это просто "окно" для чтения из памяти, на самом деле память в таких случаях не выделяется? А как вообще правильно сделать, ведь объем текстовых данных в буфере может быть ещё больше?

Короче, вдохновившись новым лозунгом Autorun "Пока Гислер думает, Autorun делает", соорудил небольшую функцию:
[cut]TOTALCMD#BAR#DATA
62015

%COMMANDER_EXE%
Подсчёт колонок|Ctrl - Запуск потока|Ctrl + ESC - Отмена


-1
[/cut]
[cut]

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

RegisterCommand 62015 "Summarize"

Global SUM_THREAD = 0, SUM_MX = 0, SUM_MY = 0

Func Summarize(lParam)
    If SUM_THREAD > 0 Then
       SUM_THREAD = 0
       Sleep(250)
       Return
    EndIf
    If Not IsPressed(0x11) Then
       MouseGetPos("SUM_MX", "SUM_MY")
       SumUpColumns(1)
       Return
    EndIf
    # CTRL запуск потока   
    RunThread ThreadSumCustomView    
EndFunc    

Func ThreadSumCustomView()
    Local i
    SUM_THREAD = 1    
    MouseGetPos("SUM_MX", "SUM_MY")    
    While SUM_THREAD > 0    
       SumUpColumns(0)
       For i = 1 To 10
           If SUM_THREAD = 0 Or (IsPressed (0x11) And IsPressed (0x1B)) Then
              SUM_THREAD = 0
              ShowHint("Операция прервана")
              Break
           EndIf   
           Sleep(50)
       Next
    Wend
    SUM_THREAD = 0
EndFunc

Func SumUpColumns(lWait = 0)
    Local txt, i, j, T1 = GetUptime()
    #If GetSelectedItems(3, 0) = '' And GetCurrentItem(3, 2) = 0 Then   
    If GetCurrentItem(3, 2) = 0 Then
       ShowHint("Index = 0", SUM_MX, SUM_MY, 1000, 1)
       Sleep(500)
       Return
    EndIf    
    ShowHint("Clipboard Waiting", SUM_MX, SUM_MY, (lWait ? "" : 1000), (lWait ? "" : 1))
    Local bReady = false, sMsg   
    Local aSel = List(), aCol = List(), aVal = List(), aRow = List()
    aSel.Count = 0
    aCol.Count = 0
    aVal.Count = 0
    aRow.Count = 0          
    SendCommand(2090, 0, 1)	# cm_CopyHdrFileDetailsToClip
    #Sleep(100)
    #txt = ClipGet()
    Local hCB, pMem, obg, CF_UNICODETEXT = 13    
    If DllCall("OpenClipboard", "handle", 0) Then
       hCB = DllCall("GetClipboardData", "uint", CF_UNICODETEXT, "handle")
       pMem = DllCall("GlobalLock", "handle", hCB, "ptr")
       obj = Buffer(1024*500, pMem)
       txt = obj.GetStr()
       Free(obj)
       DllCall("GlobalUnlock ", "handle", hCB)
       DllCall("CloseClipboard")
    Else
       sMsg = "Clipboard Error"  
    EndIf
    If StrLen(txt) > 0 Then 
       bReady = true
       aSel.Text = txt
    EndIf
    If StrPos(aSel[0], auTAB) = 0 Then
       bReady = false
       sMsg = "Clipboard Data Incorrect"
    Endif
    #FileWrite(TEMP & "\temp.tmp", txt)
    #MsgBox("hCB: " & hCB & auCRLF & "Len: " & StrLen(txt) & auCRLF & "txt")    
    #Return
    If Not bReady Then
       If lWait Then
          ShowHint(sMsg, SUM_MX, SUM_MY)
       Else
          ShowHint(sMsg, SUM_MX, SUM_MY, 1000, 1)
       Endif      
       Free(aSel, aCol, aRow, aVal)       
       Return
    Endif
    aCol.Split(aSel[0], auTAB)
    aVal.Count = aCol.Count
    For j = 0 To aVal.Count - 1
        aVal[j] = ""
    Next
    txt = ""
    For j = 1 To aSel.Count - 1 
        aRow.Split(aSel[j], auTAB)
        For i = 0 To aRow.Count - 1
            If StrPos(aRow[i], Chr(160)) Then
               aRow[i] = StrReplace(aRow[i], Chr(160), "")
            EndIf   
            If IsNumber(aRow[i]) Then aVal[i] += aRow[i]
        Next
    Next
    For j = 0 To aCol.Count - 1 
        txt &= aCol[j] & ": " & aVal[j] & auCRLF        
    Next
    Local TL = Round(GetUptime() - T1, 0) / 1000    
    txt &= "---" & auCRLF & _
           "Selected: " & aSel.Count - 1 & auCRLF & _
           "Powered by Autorun.wdx" & auCRLF & _
           "Время операции: " & StrFormat("%.3f", TL) & " sec"
    Free(aSel, aCol, aRow, aVal)
    If lWait Then
       ShowHint(StrTrim(txt), SUM_MX, SUM_MY)
    Else
       ShowHint(StrTrim(txt), SUM_MX, SUM_MY, 1000, 1)
    Endif   
EndFunc
[/cut]
Функция еще сырая и абсолютно тестовая, но, кажется, делает то, что от неё требуется. Запуск с CTRL удобен на небольших списках, когда выделенных позиций меньше 100 или где-то около 100, тогда данные обновляются в подсказке в реальном времени. Но чем больше выделено, тем больше тормозов, поэтому на больших списках лучше пользоваться одиночным кликом, так надежнее. Но надо будет с этим что-то придумать.

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

Ну, что-то у меня совсем не заладилось )
по-хорошему этот размер надо получать c помощью GlobalSize.
Не знаю, успею ли глянуть. А можно пример? Вопрос тот же: ак вообще правильно сделать, ведь объем текстовых данных в буфере может быть ещё больше?

Кстати, были кое-какие проблемы с объектом List. Пока не инициализировал принудительно:

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

    aSel.Count = 0
    aCol.Count = 0
    aVal.Count = 0
    aRow.Count = 0          
Были какие-то глюки и непонятные результаты.

Добавлено: Пт май 23, 2025 21:02
Loopback
Orion9 писал(а):Случайно отредактировал предыдущий пост, вместо отправки нового.
Я утром успел прочитать, просто не было возможности ответить. Мой ответ выше - первая часть про ClipGet, вторая про вашу функцию.
Orion9 писал(а): А как вообще правильно сделать, ведь объем текстовых данных в буфере может быть ещё больше?
Примерно так (не тестировал):

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

pMem = DllCall("GlobalLock", "handle", hCB, "ptr")
size = DllCall("GlobalSize", "handle", hCB, "uint_ptr")
obj = Buffer(size, pMem)
Orion9 писал(а):Кстати, были кое-какие проблемы с объектом List.
Какие именно? Нужна все же какая-то конкретика.