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

Добавлено: Пт фев 06, 2026 12:12
FallenAngel
Orion9
TCFS2, точно, спасибо.

Про CompareTool расскажу что хочу. Решение пока не могу придумать. Идея, условно:
if exist WinMergeU.exe then (выполняем как обычно CompareTool=WinMergeU.exe) else (предлагаем скачать перекинув на условный BAT-ник).

Добавлено: Пт фев 06, 2026 18:13
Orion9
FallenAngel
А что тут думать? Нужно брать и делать, как вы написали )

Сначала считать ключ CompareTool и проверить путь в его значении: если путь существует, то выполнить cm_CompareFilesByContent, если не существует, то вывести MsgBox с вопросом, нужно ли скачивать WinMerge.

Опционально можно добавить возможность сравнения внутренним инструментом, если пользователь откажется от скачивания.

Небольшой пример:

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

RegisterCommand 60050 "CompareTwoFiles"

Func CompareTwoFiles()
    Local sTool
    # чтение ключа    
    sTool = IniRead("~/R", COMMANDER_INI, "Configuration", "CompareTool", 0)
    # раскрытие переменных окружения в значении ключа, если они есть
    sTool = Set(sTool)
    # проверка существования файла    
    If FileExist(sTool) Then
       # файл существует
       # послать команду сравнения и выйти    
       SendCommand(2022)    # cm_CompareFilesByContent
       Return
    EndIf
    # файл не существует    
    MsgBox("Инструмент сравнения не найден " & sTool & auCRLF & auCRLF & _
           "Загрузить из Интернета?", "Autorun", 3+0+48) 
    # кнопка "Да" не была нажата, выйти из скрипта             
    If EXTENDED <> 6 Then Return
    # запуск батника для загрузки                  
    ShellExec(COMMANDER_PATH & "\Scripts\WinMergedownload.bat")    
EndFunc
Как у вас со скриптингом, можете дальше развить этот пример, а то я через неделю только освобожусь.

Кстати, вместо батника можно использовать функцию WinInetDownloadFile для загрузки файла прямо из Autorun

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

Local url = "https://downloads.sourceforge.net/winmerge/winmerge-2.16.54-x64-exe.zip"
WinInetDownloadFile(url, TEMP & "\winmerge.zip")
WinMerge загрузится во временный каталог в архиве winmerge.zip. Останется распаковать этот архив в нужное место и сообщить пользователю об окончании операции.

Добавлено: Вс фев 08, 2026 10:03
FallenAngel
Orion9
Спасибо, работает отлично. Единственное, т.к. сейчас Нет и Отмена делают одно и тоже, то лучше чтобы Нет открывал внутренний инструмент.
Orion9 писал(а): Как у вас со скриптингом
На bat\ps1 я неплохо пишу. Это не проблема.

Orion9 писал(а): Что касается Листера, если задача стоит добиться, чтобы его окно при открытии имело тот же размер и положение
Может тогда лучше упростить? Просто повесить хоткей: нажал - и листер стал размером с окно ТС.

Добавил переменную gTitleRound для указания количества знаков после точки у сетевого трафика.
Может тогда и InfoHeader.aucfg обновите с учетом этой новинки?

Добавлено: Вс фев 08, 2026 13:09
Orion9
FallenAngel
Если у вас со скриптами PowerShell получается работать, то уверен, что и с Autorun получится. Вопрос времени.

Чтобы добавить действие на кнопку "Нет", нужно обработать код с номером 7 в макросе EXTENDED. Из справки Autorun на функцию MsgBox:

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

После закрытия диалога сообщения функция присвоит макросу EXTENDED числовой код, идентифицирующий нажатую кнопку: 

1 «OK» 
2 «Отмена» 
3 «Прервать» 
4 «Повтор» 
5 «Пропустить» 
6 «Да» 
7 «Нет» 
10 «Повторить» 
11 «Продолжить» 

Возможные сценарии при нажатии клавиши ESC: 

Если есть кнопка «Отмена», функция присвоит 2 макросу EXTENDED.
Если нет кнопок «Отмена» и «OK», никакого эффекта не будет.
Если нет кнопки «Отмена», но есть «OK», функция присвоит 1 макросу EXTENDED.
Функция не всегда работает на 64-битной версии Total Commander. 
Т.е. нужно добавить:

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

# кнопка "Нет"
If EXTENDED = 7 Then SendCommand(2040)    #cm_IntCompareFilesByContent 
# кнопка "Да" не была нажата, выйти из скрипта             
If EXTENDED <> 6 Then Return 
Кстати, для этой задачи хорошо может подойти MsgBoxLinks, вместо стандартного MsgBox. Но там код чуть сложнее, пока не могу его написать и протестировать. Позднее попробую.
Может тогда лучше упростить? Просто повесить хоткей: нажал - и листер стал размером с окно ТС.
Можно так сделать. Нужно зарегистрировать отдельный код на функцию SyncListerPosition и создать для него em_команду или сразу повесить функцию на хоткей.

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

RegisterCommand 60051 "SyncListerPosition"
# Alt + "["
SetHotkeyAction /K:A /V:219 SyncListerPosition

Func SyncListerPosition()
    Static c = 0
    If IniRead("~/R", COMMANDER_INI, "Lister", "Maximized", 0) = 0 Then
        WinGetPos("x", "y", "w", "h")
        IniWrite("~/R", COMMANDER_INI, "Lister", "x", x)
        IniWrite("~/R", COMMANDER_INI, "Lister", "y", y)
        IniWrite("~/R", COMMANDER_INI, "Lister", "dx", w)
        IniWrite("~/R", COMMANDER_INI, "Lister", "dy", h)
        c += 4
        #ShowHint("Запись ключей: " & c, SYSINFO_DESKTOPWIDTH/2-50, SYSINFO_DESKTOPHEIGHT/2, 1000, 1)
    EndIf 
EndFunc
Посмотрите в справочнике SetHotkeyAction.
Может тогда и InfoHeader.aucfg обновите с учетом этой новинки?
Так он уже обновлен в последней версии на предыдущей странице, пропустили? :)

Добавлено: Вс фев 08, 2026 19:32
FallenAngel
Orion9 писал(а): c += 4
Наверное, нужно обнулять, т.к. сейчас КАЖДЫЙ раз +4
Orion9 писал(а):Так он уже обновлен в последней версии на предыдущей странице, пропустили? :)
Там он вместе с погодой (ну или я не туда смотрю). Речь про оригинал по моей ссылке в 68 строк.

Добавлено: Вс фев 08, 2026 23:33
Orion9
FallenAngel
c += 4 не обнуляется, потому что цель была увидеть общее количество записей в ини-файл. Подключите последний модуль, захватите окно за заголовок и поводите им по экрану — количество записей быстро перевалит за сотни. Я боялся, что будет еще больше. Хотя в принципе все это не страшно.
Там он вместе с погодой (ну или я не туда смотрю).
Так вы и хотели вроде с погодой, даже ссылку давали на github :) Но если хотите совсем уж легковесный заголовок, то переменную для окруления десятичных можно и не вводить. Сразу лучше в строку вывода внести изменения:

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

 gInfoHeader = gTitle & '        ' & Date("d MMMM, ddd") & '.    CPU: ' & StrFormat("%02d", GetCPUUsage()) & '%    RAM: ' & GetMemStats() & '%    NET: ↓ ' & Round(nRecvSpeed / 1000, 2) & '  ↑ ' & Round(nSendSpeed / 1000, 2) & ' mb/s' 
Т.е. вместо 1 теперь 2. Так реально лучше смотрится, на мой взгляд.

Добавлено: Пн фев 09, 2026 11:17
Orion9
FallenAngel
Вчера спешил, не подумал, что окно листера надо искать. Должно быть примерно так:

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

SetHotkeyAction /K:C /H:0 /DM /S "AlignListerWindow"

Func AlignListerWindow()
    If IniRead("~/R", COMMANDER_INI, "Lister", "Maximized", 0) = 0 Then
        WinGetPos("x", "y", "w", "h")
        IniWrite("~/R", COMMANDER_INI, "Lister", "x", x)
        IniWrite("~/R", COMMANDER_INI, "Lister", "y", y)
        IniWrite("~/R", COMMANDER_INI, "Lister", "dx", w)
        IniWrite("~/R", COMMANDER_INI, "Lister", "dy", h)
        Local hWnd = WinFind(0, "TLister")
        If hWnd = 0 Then Return ShowHint("Окно листера не найдено")
        WinSetPos(x, y, w, h, "", hWnd)        
    EndIf
EndFunc 
CTRL+0 подстраивает окно листера под позицию окна тотала при условии, что окно листера не развернуто во весь экран. Так вы хотели?

Добавлено: Пн фев 09, 2026 11:31
FallenAngel
Orion9 писал(а): Т.е. вместо 1 теперь 2. Так реально лучше смотрится, на мой взгляд.
Хм... не уверен что лучше. Нужно подумать. Но вот навеяло другую идея походу: почему "в спокойствии" у нас "0" ? По идеи должно быть "0.0".

Orion9 писал(а): If hWnd = 0 Then Return ShowHint("Окно листера не найдено")
Это строка тут лишняя, ИМХО.
Да, круто что окно Листера теперь сразу подстраивается, единственное, теряет фокус (возможно, нужно просто передать его обратно).

Добавлено: Пн фев 09, 2026 12:27
Orion9
FallenAngel
Нужно просто отформатировать строку через StrFormat. Т.е. вместо:

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

Round(nRecvSpeed / 1000, 2) & '  ↑ ' & Round(nSendSpeed / 1000, 2) & ' mb/s'
Должно быть:

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

StrFormat("%02.2f", Round(nRecvSpeed / 1000, 2)) & '  ↑ ' & StrFormat("%02.2f", Round(nSendSpeed / 1000, 2)) & ' mb/s'
Может вам так больше понравится.
Это строка тут лишняя, ИМХО.
Ну если лишняя, можно убрать )

А что на счет фокуса? У меня он не теряется. Но если у вас теряется, можно принудительно его вернуть через WinSetState:

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

WinSetPos(x, y, w, h, "", hWnd)
WinSetState(23, hWnd)
А вот и линксы, как обещал:
[cut]

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

RegisterCommand 60043 "CompareTwoFiles"
RegisterCommand 60044 "CompareTwoFilesDialog" 

Func CompareTwoFiles()
    Local sTool, nDialogMode = 1
    # чтение ключа    
    sTool = IniRead("~/R", COMMANDER_INI, "Configuration", "CompareTool", "")
    # раскрытие переменных окружения
    sTool = Set(sTool)
    # проверка существования файла    
    If FileExist(sTool) Then
       # файл существует
       # послать команду сравнения и выйти    
       SendCommand(2022)    # cm_CompareFilesByContent
       Return
    EndIf
    If nDialogMode = 1 Then Return CompareTwoFilesDialog(sTool)
    # файл не существует    
    MsgBox("Инструмент сравнения не найден " & sTool & auCRLF & auCRLF & _
           "Загрузить из Интернета?", "Autorun", 3+0+48)
    # нажата кнопка "Нет"
    If EXTENDED = 7 Then SendCommand(2040)    # cm_IntCompareFilesByContent             
    # не нажата кнопка "Да", выйти из скрипта             
    If EXTENDED <> 6 Then Return
    # запуск батника для загрузки                  
    ShellExec(COMMANDER_PATH & "\Scripts\WinMergeDownload.bat")    
EndFunc

Func CompareTwoFilesDialog(FilePath)
    Local lst = List()
    lst.Add("Сравнить внутренним" &  auLF & "Будет использован внутренний инструмент сравнения Total Commander")    
    lst.Add("Загрузить из интернета" & auLF & "https://winmerge.org/downloads/?lang=ru")
    Local nRet = MsgBoxLinks(1, "Инструмент сравнения не найден", FilePath, lst)       
    Free(lst)
    If nRet = 2 Then Return                              
    If nRet = 1001 Then 
       SendCommand(2040)    # cm_IntCompareFilesByContent
    Else
       ShellExec(COMMANDER_PATH & "\Scripts\WinMergeDownload.bat")          
    EndIf  
EndFunc

#{
    Icon:
      1 - Warning
      2 - Error
      3 - Information
      4 - Shield
      
    ByRef: List() of 1-3 elements
              
    Returns: 1001-1003, 2 for cancel
#}
Func MsgBoxLinks(Icon, Header, Message, ByRef Links)
   If Abs(Icon) > 4 Then Icon = 2
   Icon = 65536 - Icon
   If Links.Count = 0 Then Return MsgBox("Links not specified")
   Local Res, Ret
   Local buf = Buffer(auX64 ? 160 : 96), _
         ttl = Buffer(16), _
         hdr = Buffer(StrLen(Header)*2+2), _
         msg = Buffer(StrLen(Message)*2+2), _
         lnk = Buffer(auX64 ? 48 : 24)
   ttl.SetStr("Autorun" & Chr(0))          
   hdr.SetStr(Header & Chr(0))
   msg.SetStr(Message & Chr(0))
   Local b1 = Buffer(512), b2 = Buffer(512), b3 = Buffer(512)
   b1.Zero()
   b2.Zero()
   b3.Zero()      
   b1.SetStr(Links[0] & Chr(0))
   If Links.Count > 1 Then b2.SetStr(Links[1] & Chr(0))
   If Links.Count > 2 Then b3.SetStr(Links[2] & Chr(0))   
   lnk.Zero()
   lnk.SetNum(0, "int", 1001, _
                 "ptr", b1.ptr, _
                 "int", 1002, _
                 "ptr", b2.ptr, _
                 "int", 1003, _
                 "ptr", b3.ptr)
   buf.Zero()
   buf.SetNum(0, "uint", buf.size, _   
                 "hwnd", AUTORUN_TCHANDLE, _
                 "handle", 0, _
                 "dword", 0x0019, _
                 "dword", 0x0008, _
                 "ptr", ttl.ptr, _
                 "ptr", Icon, _
                 "ptr", hdr.ptr, _
                 "ptr", msg.ptr, _
                 "uint", Links.Count, _
                 "ptr", lnk.ptr)
   Res = DllCall("TaskDialogIndirect", _
                 "ptr", buf.ptr, _   
                 "int*", @Ret, _   
                 "int*", 0, _   
                 "bool*", 0, "hresult")
   Free(buf, ttl, hdr, msg, lnk, b1, b2, b3)              
   Return Ret
EndFunc
[/cut]
Ну не персик ли? :)

[cut="PS"]Если персик не нужен, можно изменить nDialogMode = 1 на любое значение[/cut]

Добавлено: Пн фев 09, 2026 14:18
FallenAngel
Orion9 писал(а): Может вам так больше понравится.
Пока так тестирую:

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

StrFormat("%02.1f", Round(nRecvSpeed / 1000, 1)) & '  ↑ ' & StrFormat("%02.1f", Round(nSendSpeed / 1000, 1)) & ' mb/s'

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

WinSetState(23, hWnd)
Да, так все ОК.

Добавлено: Вт фев 10, 2026 12:54
Orion9
Прошелся тут по последним страницам. Удивительно, ссылки на 11,000 сохранилась, а ссылка на 13К не доступна. Непорядок творится в королевстве. Перезаливка.
https://www.upload.ee/files/19062762/TC.11.56.7z.html
[cut="Warning"]Сайт находится в 16-20 Кб блоке. С территории России без средств обхода скачать архив не получится. Адрес площадки пока не меняю, вдруг блокировку еще снимут. [/cut]
Обновил модуль Icons.aucfg.
[cut]

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

Pragma IncludeOnce

# 64000-65000

Global TrayShow = 0, TrayLog = List()
Global Tray1001 = 0, Tray1002 = 0, Tray1003 = 0, Tray1010 = 0

Global gNotifyIcon = COMMANDER_EXE
#Global gNotifyIcon = COMMANDER_PATH & "\Ini\Newsbar\notify-yes.ico"

# регистрация уникальных кодов
Global Code1003 = DllCall("RegisterWindowMessageW", "wstr", "TrayNotify1003", "uint")
Global Code1010 = DllCall("RegisterWindowMessageW", "wstr", "TrayNotify1010", "uint")

# коды кнопок
RegisterCommand 64000 "ToggleTrayIcon"
RegisterCommand 64001 "ChangeTrayIcon"
RegisterCommand 64002 "NotifyInfo"
RegisterCommand 64003 "RotateIcon"

# функции обратного вызова
SetMessageAction /P 99999 "AutorunTrayAction"
SetMessageAction /P %"Code1003" "MinTrayAction"
SetMessageAction /P %"Code1010" "TrayNotifyInfo"

# сворачивание в трей
ControlSetMouseAction /K:C /L /H:8 0 "MinimizeToTray"

# иконки окна уведомлений
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  

# системные сообщения 
Const WM_MOUSEMOVE     = 0x0200, _
      WM_LBUTTONDOWN   = 0x0201, _
      WM_LBUTTONUP     = 0x0202, _
      WM_LBUTTONDBLCLK = 0x0203, _
      WM_RBUTTONDOWN   = 0x0204, _
      WM_RBUTTONUP     = 0x0205, _
      WM_RBUTTONDBLCLK = 0x0206, _   
      WM_MBUTTONDOWN   = 0x0207, _
      WM_MBUTTONUP     = 0x0208, _      
      WM_MBUTTONDBLCLK = 0x0209
      
# старт
TrayIcon()      

# главная иконка      
Func TrayIcon()
    Local ico, idx = 0
    # чтение конфигурации
    IniRead TrayShow %AUTORUN_INI% "TrayIcon" "Show" 1
    If Not TrayShow Then
       Return
    EndIf
    IniRead ico %AUTORUN_INI% "TrayIcon" "Icon"
    IniRead idx %AUTORUN_INI% "TrayIcon" "Index" 0
    # раскрытие переменной окружения    
    ico = Set(ico)
    # откат к умолчаниям
    If ico = "" Or Not FileExist(ico) Then
       idx = 0
       ico = COMMANDER_EXE
    EndIf
    Local sHint = "Autorun " & FileGetVersion(AUTORUN_PATH & "\Autorun.wdx", "FileVersion")
    If auX64 Then sHint &= " x64"
    # дескриптор иконки      
    Local hIco = DllCall("shell32\ExtractIconW", _
                         "ptr", AUTORUN_TCHANDLE, _
                         "wstr", ico, "uint", idx, "ptr")
    # установка в трей с идентификатором 1001              
    If NotifyIcon("add", 1001, 99999, hIco, sHint) Then Tray1001 = 1
    If hIco > 0 Then DllCall("DestroyIcon", "ptr", hIco)        
EndFunc      

# главная иконка Autorun
# функция обратного вызова
Func AutorunTrayAction(hWnd, uMsg, wParam, lParam) 
    Local bCtrl = IsPressed(0x11)
    Switch lParam
       # движение над иконкой    
       Case WM_MOUSEMOVE
          If LAST_HINT_WINDOW = 0 Then          
             If bCtrl Then ShowHint(GetState("threads"), "", "", 2000, 1)
          EndIf
       # левый клик       
       Case WM_LBUTTONDOWN
          If bCtrl Then
             ClipPut(GetState("threads"))
             ShowHint(GetState("threads"))
             #ShowDarkHint(GetState("threads"))             
             Return
          EndIf
          AutorunMenu(0)
      # правый клик
      Case WM_RBUTTONDOWN
           If bCtrl Then 
              ShowDarkHint(GetState("libs"))
           Else   
              ShowSetEnviroments()
           EndIf   
      # клик по сообщению        
      Case NIN_BALLOONUSERCLICK  
           MsgBox("Baloon click")
           #WinSetState(23)
      # клик "закрыть сообщение"
      Case NIN_BALLOONTIMEOUT
           MsgBox("Time-out")           
    EndSwitch
EndFunc

# иконка уведомлений
Func TrayNotifyInfo(hWnd, uMsg, wParam, lParam)
    Switch lParam
       Case WM_LBUTTONDOWN
           ShowPopupMenu /D /F "TrayNotifyInfoMenu"           
       Case WM_RBUTTONDOWN
           #NotifyIcon("delete", 1010)
       Case NIN_BALLOONUSERCLICK
       
       Case NIN_BALLOONTIMEOUT
    EndSwitch
EndFunc

# меню уведомлений
Func TrayNotifyInfoMenu()
    Local txt, nIco, sIco, sTime, sInfo, sType, sFunc, i = 0, j = 0
    For i = TrayLog.Count - 1 To 0 Step -1
        nIco = -1
        sIco = "-1"
        sTime = StrPart(TrayLog[i], Chr(0), 1)        
        sInfo = StrPart(TrayLog[i], Chr(0), 2)
        sType = StrPart(TrayLog[i], Chr(0), 4)
        If BitAND(sType, 3) = 1 Then 
           nIco = 76
        ElseIf BitAND(sType, 3) = 2 Then 
           nIco = 79    
        ElseIf BitAND(sType, 3) = 3 Then 
           nIco = 93       
        EndIf
        If nIco <> -1 Then sIco = "imageres.dll," & nIco
        sFunc = "TrayNotifyItem " & i & " " & nIco
        txt &= 'MENUITEM "' & sTime & " " & sInfo & '", em_aucmd ' & sIco & " " & sFunc & auCRLF
        j += 1
        If j = 30 Then Break 
    Next
    If TrayLog.Count = 0 Then txt &= 'MENUITEM "<Пусто>", em_aucmd' & auCRLF
    txt &= 'MENUITEM SEPARATOR' & auCRLF
    txt &= 'MENUITEM "Копировать", em_aucmd -1 TrayNotifyCopy' & auCRLF
    txt &= 'MENUITEM "Скрыть иконку", em_aucmd -1 NotifyIcon "delete" 1010' & auCRLF    
    Return txt
EndFunc

Func TrayNotifyItem(nItem, nIco)
    Local nFlag
    Local sInfo = StrPart(TrayLog[nItem], Chr(0), 2)
    Local sText = StrPart(TrayLog[nItem], Chr(0), 3)
    Switch nIco
      Case 76
         nFlag = 64
      Case 79
         nFlag = 48
      Case 93
         nFlag = 16
      Else
         nFlag = ''
    EndSwitch
    MsgBox(sText, sInfo, nFlag)
EndFunc

Func TrayNotifyCopy()
    Local txt = TrayLog.Text
    txt = StrReplace(txt, Chr(0), ";")
    ClipPut(txt)
    ShowTransHint("Скопировано в буфер")
EndFunc

# переключение иконки
Func ToggleTrayIcon(lParam)
    If Tray1001 > 0 Then
       IniWrite %AUTORUN_INI% "TrayIcon" "Show" 0
       If NotifyIcon("delete", 1001) Then Tray1001 = 0
    Else
       IniWrite %AUTORUN_INI% "TrayIcon" "Show" 1
       TrayIcon()
    EndIf
    If Tray1001 > 0 Then
       ShowTransHint("Иконка включена")
    Else
       ShowTransHint("Иконка отключена")    
    EndIf
EndFunc

# иконка свернутого окна
Func MinTrayAction(hWnd, uMsg, wParam, lParam)
    If lParam = WM_LBUTTONDOWN Then
       If NotifyIcon("delete", 1003) Then Tray1003 = 0
       WinSetState(5)
       WinSetState(23)
    ElseIf lParam = WM_RBUTTONDOWN Then
       
    EndIf
EndFunc

# сворачивание
Func MinimizeToTray()
    Local sHint = "Click to restore Total Commander"
    Local hIcon =  SendMessage(AUTORUN_TCHANDLE, 0x7f, 2, 0)
    If hIcon = 0 Then 
       MsgBox("Не удалось получить дескриптор иконки", "Autorun", 48)
       Return
    EndIf
    If NotifyIcon("add", 1003, Code1003, hIcon, sHint) Then Tray1003 = 1
    WinSetState(0)
EndFunc

# тестовая функция
Func ChangeTrayIcon(lParam)
    Static sDLL = GetKnownFolderPath("System") & "\shell32.dll", i = 10    
    If Tray1001 = 0 Then
       ShowHint("Иконка отключена")
       Return
    EndIf
    i += 1
    Local hIco
    hIco = DllCall("shell32\ExtractIconW", _
                   "ptr", AUTORUN_TCHANDLE, _
                   "wstr", sDLL, "uint", i, "ptr")
    If IsPressed(0x11) Then
       If NotifyIcon("add", 1002, 99999, hIco, "Autorun #2", "Second icon added", "Autorun", NIIF_INFO) Then
          Tray1002 = 1
       EndIf   
    Else
       If IsPressed(0x12) Then hIco = 0
       NotifyIcon("set", 1001, 99999, hIco, "", "Icon changed to shell32.dll, " & i, "", NIIF_WARNING)
       Sleep(2000)
       NotifyIcon("set", 1001,, hIco,, "Icon changed to shell32.dll, " & i,, NIIF_ERROR)
    EndIf   
    If hIco > 0 Then DllCall("DestroyIcon", "ptr", hIco, "bool")        
EndFunc

# тест уведомлений
Func NotifyInfo(lParam)
     Local bCtrl = IsPressed(0x11)
     Static i = 0
     i += 1
     If i > 3 Then i = 1
     If bCtrl Then i = 1
     If i = 1 Then 
        NotifyInfoMessage("Test message", "Test 1", NIIF_INFO)
        If Not bCtrl Then Return        
        Sleep(2000)
     EndIf   
     If bCtrl Or i = 2 Then 
        txt = "Pragma Include %COMMANDER_PATH%\Ini\Scripts\Test.aucfg"
        NotifyInfoMessage(txt, "Test 2", NIIF_WARNING + NIIF_NOSOUND)
        If Not bCtrl Then Return        
        Sleep(2000)
     EndIf
     If bCtrl Or i = 3 Then 
        txt = "Test message line 1" & auCRLF & "Test message line 2"
        NotifyInfoMessage(txt, "Test 3", NIIF_ERROR)
     EndIf   
EndFunc

# проверка существования иконки
# функция имеет проблемы на Win10
Func IsIconOnTray(uId)
    Local rc = Buffer(16)    # RECT
    rc.Zero()
    Local id = Buffer(auX64 ? 40 : 28)
    id.Zero()
    If auX64 Then
       id.SetNum(0,  "dword", id.size)
       id.SetNum(8,  "hwnd",  AUTORUN_TCHANDLE)
       id.SetNum(16, "uint",  uId)
    Else
       id.SetNum(0, "dword", id.size, _
                    "hwnd",  AUTORUN_TCHANDLE, _
                    "uint",  uId)
    EndIf                 
    Local hr = DllCall("Shell32.dll\Shell_NotifyIconGetRect", "ptr", id.ptr, "ptr", rc.ptr) 
    Free(id, rc)
    Return hr = 0 ? true : false
EndFunc

#{
     Вызов системных уведомлений:
     
           InfoText - текст уведомления (255 символов)
          InfoTitle - текст заголовка уведомления (63 символов)
           InfoType - флаги NIIF_* меняющие вид и поведение окна уведомлений 
            
     Функция использует идентификатор "1010"
#}
Func NotifyInfoMessage(InfoText, InfoTitle = "Autorun", InfoType = 0)
    Local Offset = auX64 ? 40 : 24, sIcon
    Static buf = Buffer(auPtrSize = 4 ? 956 : 976)
    Static nFlags = BitOR(NIF_MESSAGE, NIF_ICON, NIF_INFO, NIF_TIP)
    If FileExist(gNotifyIcon) Then
       sIcon = gNotifyIcon
    Else
       sIcon = COMMANDER_EXE
    EndIf   
    Static hIco = DllCall("shell32\ExtractIconW", _
                          "ptr", AUTORUN_TCHANDLE, _
                          "wstr", sIcon, "uint", 0, "ptr")
    NotifyIcon("delete", 1010)
    buf.Zero()
    If auX64 Then
        buf.SetNum(0,  "dword", buf.size)
        buf.SetNum(8,  "hwnd", AUTORUN_TCHANDLE)
        buf.SetNum(16, "uint", 1010, _
                       "uint", nFlags, _
                       "uint", Code1010)             
        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", nFlags, _
                      "uint", Code1010, _
                      "ptr",  hIco)
        buf.SetNum(280, "dword", 1, _
                        "dword", 1)

    EndIf
    buf.SetStr("Notify Messages" & Chr(0), Offset)
    buf.SetStr(StrLeft(InfoText, 255) & Chr(0),  Offset + 264)   # 256 + 4 + 4 
    Offset += 264
    If InfoTitle <> "" Then       
       buf.SetStr(StrLeft(InfoTitle, 63) & Chr(0), Offset + 516)   # 512 + 4
    EndIf
    Offset += 516
    If InfoType > 0 Then
       buf.SetNum(Offset + 128, "dword", InfoType)   # 128
    EndIf
    TrayLog.Add(Time() & Chr(0) & InfoTitle & Chr(0) & InfoText & Chr(0) & InfoType)
    Tray1010 = 1
    Return DllCall("Shell32.dll\Shell_NotifyIconW", "uint", 0, "ptr", buf.ptr)
EndFunc

#{
     Добавление иконки в трей:
   
            sAction - тип действия ("add", "set", "delete")
                uID - внутренний уникальный идентификатор иконки
          nCallback - код зарегистрированного сообщения для функции обратного вызова
              hIcon - дескриптор иконки (только для "add")
               sTip - текст подсказки в трее
              sText - текст уведомления (только для "add" и "set")
             sTitle - текст заголовка уведомления
              nType - флаги NIIF_* (изменение вида и поведения окна уведомлений)
            
     https://learn.microsoft.com/en-us/windows/win32/api/shellapi/ns-shellapi-notifyicondataw            
#}
Func NotifyIcon(sAction, uID, nCallback = 0, hIcon = 0, sTip = "", sText = "", sTitle = "", nType = 0)
    Local nAction, nFlags
    Static TIP_MAXCHAR = 127, INFO_MAXCHAR = 255, TITLE_MAXCHAR = 63
    Static buf = Buffer(auPtrSize = 4 ? 956 : 976)   
    buf.Zero()
    nFlags = BitOR(    hIcon = 0 ? 0 : NIF_ICON, _
                   nCallback = 0 ? 0 : NIF_MESSAGE, _
                StrLen(sTip) = 0 ? 0 : NIF_TIP, _
               StrLen(sText) = 0 ? 0 : NIF_INFO    )
    Switch sAction
       Case "add"
          nAction = 0
       Case "set"
          nAction = 1
      Case "delete"
          nAction = 2
       Else
          Return 
    EndSwitch
    If auX64 Then
        buf.SetNum(0, "dword", buf.size)
        buf.SetNum(8, "hwnd",  AUTORUN_TCHANDLE)
        buf.SetNum(16, "uint", uID, _
                       "uint", nFlags, _
                       "uint", nCallback)
        buf.SetNum(32, "ptr",  hIcon)
    Else
        buf.SetNum(0, "dword", buf.size, _   
                       "hwnd", AUTORUN_TCHANDLE, _         
                       "uint", uID, _
                       "uint", nFlags, _
                       "uint", nCallback, _
                        "ptr", hIcon) 
    EndIf
    Local nOffset = auX64 ? 40 : 24   # PtrSize*2 + 4*4 + padding for X64
    If sTip <> 0 Then
       buf.SetStr(StrLeft(sTip, TIP_MAXCHAR) & Chr(0), nOffset)
    EndIf   
    If sText <> 0 Then
       nOffset += 264        
       buf.SetStr(StrLeft(sText, INFO_MAXCHAR) & Chr(0), nOffset)   # 256 + 4 + 4       
       If sTitle <> 0 Then
          buf.SetStr(StrLeft(sTitle, TITLE_MAXCHAR) & Chr(0), nOffset + 516)   # 512 + 4
       EndIf
       If nType > 0 Then
          buf.SetNum(nOffset + 644, "dword", nType)   # 128
       EndIf
    EndIf    
    Return DllCall("Shell32.dll\Shell_NotifyIconW", "uint", nAction, "ptr", buf.ptr, "bool")
EndFunc
 
Global ROTATE_THREAD = 0

Func RotateIcon()
    If ROTATE_THREAD > 0 Then
       ROTATE_THREAD = 0
       Return
    EndIf   
    RunThread("ThreadRotateIcon", IsPressed(0x11))
EndFunc    

Func ThreadRotateIcon(FileIcons)
    ROTATE_THREAD = 1
    Local hIcon = SendMessage(AUTORUN_TCHANDLE, 0x7f, 2, 0)
    While ROTATE_THREAD > 0 
        Sleep(150)
        If FileIcons Then
           IconRotateDraw()
        Else   
           RotateDeviceContext()
        EndIf   
    Wend
    SendMessage(AUTORUN_TCHANDLE, 0x80, 0, hIcon)    
    WinRedraw(2)
EndFunc

Func IconRotateDraw()
    Static nIco = 0
    nIco += 1
    If nIco > 4 Then nIco = 1
    Local sFile = COMMANDER_PATH & "\Icons\TC\" & nIco & ".ico"
    Local hIco = DllCall("shell32\ExtractIconW", _
                          "ptr", AUTORUN_TCHANDLE, _
                          "wstr", sFile, _
                          "uint", 0, _
                          "ptr")    
    Local hWnd = WinFind(AUTORUN_TCHANDLE, _
                        (AUTORUN_TCARCH = 32 ? "TMyPanel" : "Window"), _
                        (AUTORUN_TCARCH = 32 ? 6 : 10))
    # Local hWnd = RequestInfo(15)
    # hDC = DllCall("GetDC", "ptr", hWnd, "ptr")  
    Local hDC = DllCall("GetWindowDC", "ptr", hWnd, "ptr")
    Local x, y, w, h    
    WinGetPos("x", "y", "w", "h", hWnd)                     
    DllCall("DrawIconEx", _
                   "ptr", hDC, _
                   "int", w - 20, _
                   "int", 1, _
                   "ptr", hIco, _
                   "int", 16, _
                   "int", 16,  _
                   "uint", 0, _
                   "ptr", 0,  _
                   "uint", 0x0003)                   
    SendMessage(AUTORUN_TCHANDLE, 0x80, 0, hIco)
    SendMessage(AUTORUN_TCHANDLE, 0x80, 1, hIco)
    DllCall("ReleaseDC", "ptr", AUTORUN_TCHANDLE, "ptr", hDC)                
    DllCall("DestroyIcon", "ptr", hIco)
EndFunc

Func RotateDeviceContext()
    Static c = 0
    c += 1
    If c > 4 Then c = 1
    Local x, y, w, h
    Local hIco = SendMessage(AUTORUN_TCHANDLE, 0x7f, 2, 0)
    Local hWnd = WinFind(AUTORUN_TCHANDLE, _
                        (AUTORUN_TCARCH = 32 ? "TMyPanel" : "Window"), _
                        (AUTORUN_TCARCH = 32 ? 6 : 10))
    Local DC = DllCall("GetDC", "int", 0)
    Local nDPI = DllCall("GetDeviceCaps", "handle", DC, "int", 88)
    Local nScale = DllCall("MulDiv", "int", 100, "int", nDPI, "int", 96)
    Local nSize = Round(16*nScale/100,0)
    # Local hWnd = RequestInfo(15)                        
    Local hdcWndDC = DllCall("GetWindowDC", "ptr", hWnd, "ptr")
    Local hdcMemDC = DllCall("CreateCompatibleDC", "ptr", hdcWndDC, "ptr")
    Local hdcBltDC = DllCall("CreateCompatibleDC", "ptr", hdcWndDC, "ptr")    
    Local hbmDB1 = DllCall("CreateCompatibleBitmap", "ptr", hdcWndDC, "int", nSize, "int", nSize,"ptr")
    Local hbmDB2 = DllCall("CreateCompatibleBitmap", "ptr", hdcWndDC, "int", nSize, "int", nSize,"ptr")    
    DllCall("SelectObject", "ptr", hdcMemDC, "ptr", hbmDB1, "ptr")    
    DllCall("SelectObject", "ptr", hdcBltDC, "ptr", hbmDB2, "ptr")        
    DllCall("DrawIconEx", _
                   "ptr", hdcMemDC, _
                   "int", 1, _
                   "int", 1, _
                   "ptr", hIco, _
                   "int", nSize, _
                   "int", nSize,  _
                   "uint", 0, _
                   "ptr", 0,  _
                   "uint", 0x0003)
    Local pt = Buffer(24)
    pt.Zero()
    If c = 1 Then
       pt.SetNum(0, "int", nSize, "int", 0, _
                    "int", 0,  "int", 0, _
                    "int", nSize, "int", nSize)
    ElseIf c = 2 Then
       pt.SetNum(0, "int", nSize, "int", nSize, _
                    "int", nSize, "int", 0, _
                    "int", 0,  "int", nSize)
    ElseIf c = 3 Then
       pt.SetNum(0, "int", 0,  "int", nSize, _
                    "int", nSize, "int", nSize, _
                    "int", 0,  "int", 0)
    ElseIf c = 4 Then
       pt.SetNum(0, "int", 0,  "int", 0, _
                    "int", 0,  "int", nSize, _
                    "int", nSize, "int", 0)
    EndIf                               
    DllCall("PlgBlt", _
                   "ptr", hdcBltDC, _
                   "ptr", pt.Ptr, _
                   "ptr", hdcMemDC, _ 
                   "int", 0, _
                   "int", 0, _ 
                   "int", nSize, _
                   "int", nSize, _
                   "ptr", 0, _
                   "int", 0, "int", 0)
    WinGetPos("x", "y", "w", "h", hWnd)                     
    DllCall("BitBlt", _
                   "ptr", hdcWndDC, _
                   "int", w - 20, _
                   "int", 2, _
                   "int", nSize, _
                   "int", nSize, _
                   "ptr", hdcBltDC, _
                   "int", 1, _
                   "int", 1,  _
                   "dword", 13369376)                   
    DllCall("DeleteObject", "ptr", hbmDB1)
    DllCall("DeleteObject", "ptr", hbmDB2)    
    DllCall("DeleteObject", "ptr", hdcMemDC)
    DllCall("DeleteObject", "ptr", hdcBltDC)
    DllCall("ReleaseDC", "ptr", AUTORUN_TCHANDLE, "ptr", hdcWndDC)
EndFunc
[/cut]
В модуле есть две универсальные функции для показа системных уведомлений и создания иконок в трее: NotifyInfoMessage и NotifyIcon. Коды кнопок 64000-64002 тестирую функционал. Созданные идентификаторы иконок нужно добавить в секцию финализации, чтобы при выхода из ТС они не оставались висеть в трее.
[cut="Pragma AutorunFinalizeSection"]

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

# удаление иконок
If Tray1001 > 0 Then NotifyIcon("delete", 1001)
If Tray1002 > 0 Then NotifyIcon("delete", 1002)
If Tray1003 > 0 Then NotifyIcon("delete", 1003)
If Tray1010 > 0 Then NotifyIcon("delete", 1010)
[/cut]
Модуль можно накатить на тот, что в сборке, для удобного и быстрого теста.

Добавлено: Вт фев 10, 2026 14:44
FallenAngel
Orion9 писал(а):Перезаливка.
Почему просто не использовать приличный хостинг? terabox.com, mega.nz или pixeldrain.com ?
Orion9 писал(а):Обновил модуль Icons.aucfg.
Добавляйте небольшое описание в самом начале файла, модуль возможно и интересный, но не ясно что он делает (как и остальные 13к строк).

Повторюсь, намного полезнее (понятнее, удобнее, логичнее, ...) было бы выкладывать просто модулями. Возможно и найдется самурай, который скачает сборку и осилит часть написанного... но это 1-2 юзера (с огромным запасом свободного времени) и не более.

Добавлено: Вт фев 10, 2026 18:01
Orion9
FallenAngel писал(а):Возможно и найдется самурай, который скачает сборку и осилит часть написанного
Cамурай звучит громо, скорее фрик, как я )

А я ведь предупреждал Loopback, что у нас с Autorun всё всьерёз и надолго, а он мне не верил ) Могу только повторить свой пледж и коммитмент:
[cut]Till death us do part, Auto :mrgreen:[/cut]
FallenAngel писал(а):terabox.com, mega.nz или pixeldrain.com
Первый всё в том же 16 Кб блоке, остальные, судя по всему, в белых списках, но требуют регистрацию, а оно мне вроде как не надо.

Сделать перманентную ссылку на том же гугл диске (или любом другом диске) не проблема. Но не раньше, чем Loopback сделает перманентную ссылку на финальную версию Autorun :)

А пока все это для тестов и эксперементов. Если кто-то может извлечь из этого пользу — без проблем. Буду только рад, что помимо меня кому-то ещё пригодилось.
FallenAngel писал(а):Добавляйте небольшое описание в самом начале файла, модуль возможно и интересный, но не ясно что он делает (как и остальные 13к строк).
Ранний функционал довольно хорошо документирован, просто на последние модули я не нашел возможности сделать описание. Думал, сейчас у меня будет большое окно и смогу наверстать упущенное, но нет, судя по всему, опять не получится.

Но не все так плохо. В этом топике осталось практически всё, правда мотать придется чуть дальше.

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

Когда-то, глянув на этот модуль, я подумал, надо быть реально повернутым, чтобы полностью в нем разобраться. Однако сейчас я на него смотрю и он мне кажется довольно простым и понятным. Одна мальнькая ремарка: с тех пор прошло два года :)

Так что верно говорят, что путь осилит идущий, а нет кто с него сходит или топчется на месте)

P.s. Ссылка на модуль за CloudFlare, т.ч. приложу его сюда.
[cut]

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

;https://www.autohotkey.com/boards/viewtopic.php?t=113308

;======================================================================================================================
; ToolTipOptions        -  additional options for ToolTips
;
; Tooltip control       -> https://learn.microsoft.com/en-us/windows/win32/controls/tooltip-control-reference
; TTM_SETMARGIN         = 1050
; TTM_SETTIPBKCOLOR     = 1043
; TTM_SETTIPTEXTCOLOR   = 1044
; TTM_SETTITLEW         = 1057
; WM_SETFONT            = 0x30
; SetClassLong()        -> https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setclasslongw
; ======================================================================================================================
Class ToolTipOptions {
   ; -------------------------------------------------------------------------------------------------------------------
   Static HTT := DllCall("User32.dll\CreateWindowEx", "UInt", 8, "Str", "tooltips_class32", "Ptr", 0, "UInt", 3
                       , "Int", 0, "Int", 0, "Int", 0, "Int", 0, "Ptr", A_ScriptHwnd, "Ptr", 0, "Ptr", 0, "Ptr", 0)
   Static SWP := CallbackCreate(ObjBindMethod(ToolTipOptions, "_WNDPROC_"), , 4) ; subclass window proc
   Static OWP := 0                                                               ; original window proc
   Static ToolTips := Map()
   ; -------------------------------------------------------------------------------------------------------------------
   Static BkgColor := ""
   Static TktColor := ""
   Static Icon := ""
   Static Title := ""
   Static HFONT := 0
   Static Margins := ""
   ; -------------------------------------------------------------------------------------------------------------------
   Static Call(*) => False ; do not create instances
   ; -------------------------------------------------------------------------------------------------------------------
   ; Init()          -  Initialize some class variables and subclass the tooltip control.
   ; -------------------------------------------------------------------------------------------------------------------
   Static Init() {
      If (This.OWP = 0) {
         This.BkgColor := ""
         This.TktColor := ""
         This.Icon := ""
         This.Title := ""
         This.Margins := ""
         If (A_PtrSize = 8)
            This.OWP := DllCall("User32.dll\SetClassLongPtr", "Ptr", This.HTT, "Int", -24, "Ptr", This.SWP, "UPtr")
         Else
            This.OWP := DllCall("User32.dll\SetClassLongW", "Ptr", This.HTT, "Int", -24, "Int", This.SWP, "UInt")
         OnExit(ToolTipOptions._EXIT_, -1)
         Return This.OWP
      }
      Else
         Return False
   }
   ; -------------------------------------------------------------------------------------------------------------------
   ;  Reset()        -  Close all existing tooltips, delete the font object, and remove the tooltip's subclass.
   ; -------------------------------------------------------------------------------------------------------------------
   Static Reset() {
      If (This.OWP != 0) {
         For HWND In This.ToolTips.Clone()
            DllCall("DestroyWindow", "Ptr", HWND)
         This.ToolTips.Clear()
         If This.HFONT
            DllCall("DeleteObject", "Ptr", This.HFONT)
         This.HFONT := 0
         If (A_PtrSize = 8)
            DllCall("User32.dll\SetClassLongPtrW", "Ptr", This.HTT, "Int", -24, "Ptr", This.OWP, "UPtr")
         Else
            DllCall("User32.dll\SetClassLongW", "Ptr", This.HTT, "Int", -24, "Int", This.OWP, "UInt")
         This.OWP := 0
         Return True
      }
      Else
         Return False
   }
   ; -------------------------------------------------------------------------------------------------------------------
   ; SetColors()     -  Set or remove the text and/or the background color for the tooltip.
   ; Parameters:
   ;     BkgColor    -  color value like used in Gui.BackColor(...)
   ;     TxtColor    -  see above.
   ; -------------------------------------------------------------------------------------------------------------------
   Static SetColors(BkgColor := "", TxtColor := "") {
      This.BkgColor := BkgColor = "" ? "" : BGR(BkgColor)
      This.TxtColor := TxtColor = "" ? "" : BGR(TxtColor)
      BGR(Color, Default := "") { ; converts colors to BGR
         ; HTML Colors (BGR)
         Static HTML := {AQUA:   0xFFFF00, BLACK: 0x000000, BLUE:   0xFF0000, FUCHSIA: 0xFF00FF, GRAY:  0x808080,
                         GREEN:  0x008000, LIME:  0x00FF00, MAROON: 0x000080, NAVY:    0x800000, OLIVE: 0x008080,
                         PURPLE: 0x800080, RED:   0x0000FF, SILVER: 0xC0C0C0, TEAL:    0x808000, WHITE: 0xFFFFFF,
                         YELLOW: 0x00FFFF}
         If HTML.HasProp(Color)
            Return HTML.%Color%
         If (Color Is String) && IsXDigit(Color) && (StrLen(Color) = 6)
            Color := Integer("0x" . Color)
         If IsInteger(Color)
            Return ((Color >> 16) & 0xFF) | (Color & 0x00FF00) | ((Color & 0xFF) << 16)
         Return Default
      }
   }
   ; -------------------------------------------------------------------------------------------------------------------
   ; SetFont()       -  Set or remove the font used by the tooltip.
   ; Parameters:
   ;     FntOpts     -  font options like Gui.SetFont(Options, ...)
   ;     FntName     -  font name like Gui.SetFont(..., Name)
   ; -------------------------------------------------------------------------------------------------------------------
   Static SetFont(FntOpts := "", FntName := "") {
      Static HDEF := DllCall("GetStockObject", "Int", 17, "UPtr") ; DEFAULT_GUI_FONT
      Static LOGFONTW := 0
      If (FntOpts = "") && (FntName = "") {
         If This.HFONT
            DllCall("DeleteObject", "Ptr", This.HFONT)
         This.HFONT := 0
         LOGFONTW := 0
      }
      Else {
         If (LOGFONTW = 0) {
            LOGFONTW := Buffer(92, 0)
            DllCall("GetObject", "Ptr", HDEF, "Int", 92, "Ptr", LOGFONTW)
         }
         HDC := DllCall("GetDC", "Ptr", 0, "UPtr")
         LOGPIXELSY := DllCall("GetDeviceCaps", "Ptr", HDC, "Int", 90, "Int")
         DllCall("ReleaseDC", "Ptr", HDC, "Ptr", 0)
         If (FntOpts != "") {
            For Opt In StrSplit(RegExReplace(Trim(FntOpts), "\s+", " "), " ") {
               Switch StrUpper(Opt) {
                  Case "BOLD":      NumPut("Int", 700, LOGFONTW, 16)
                  Case "ITALIC":    NumPut("Char",  1, LOGFONTW, 20)
                  Case "UNDERLINE": NumPut("Char",  1, LOGFONTW, 21)
                  Case "STRIKE":    NumPut("Char",  1, LOGFONTW, 22)
                  Case "NORM":      NumPut("Int", 400, "Char", 0, "Char", 0, "Char", 0, LOGFONTW, 16)
                  Default:
                     O := StrUpper(SubStr(Opt, 1, 1))
                     V := SubStr(Opt, 2)
                     Switch O {
                        Case "C":
                           Continue ; ignore the color option
                        Case "Q":
                           If !IsInteger(V) || (Integer(V) < 0) || (Integer(V) > 5)
                              Throw ValueError("Option Q must be an integer between 0 and 5!", -1, V)
                           NumPut("Char", Integer(V), LOGFONTW, 26)
                        Case "S":
                           If !IsNumber(V) || (Number(V) < 1) || (Integer(V) > 255)
                              Throw ValueError("Option S must be a number between 1 and 255!", -1, V)
                           NumPut("Int", -Round(Integer(V + 0.5) * LOGPIXELSY / 72), LOGFONTW)
                        Case "W":
                           If !IsInteger(V) || (Integer(V) < 1) || (Integer(V) > 1000)
                              Throw ValueError("Option W must be an integer between 1 and 1000!", -1, V)
                           NumPut("Int", Integer(V), LOGFONTW, 16)
                        Default:
                           Throw ValueError("Invalid font option!", -1, Opt)
                     }
                  }
               }
            }
         NumPut("Char", 1, "Char", 4, "Char", 0, LOGFONTW, 23) ; DEFAULT_CHARSET, OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS
         NumPut("Char", 0, LOGFONTW, 27) ; FF_DONTCARE
         If (FntName != "")
            StrPut(FntName, LOGFONTW.Ptr + 28, 32)
         If !(HFONT := DllCall("CreateFontIndirectW", "Ptr", LOGFONTW, "UPtr"))
            Throw OSError()
         If This.HFONT
            DllCall("DeleteObject", "Ptr", This.HFONT)
         This.HFONT := HFONT
      }
   }
   ; -------------------------------------------------------------------------------------------------------------------
   ; SetMargins()    -  Set or remove the margins used by the tooltip
   ; Parameters:
   ;     L, T, R, B  -  left, top, right, and bottom margin in pixels.
   ; -------------------------------------------------------------------------------------------------------------------
   Static SetMargins(L := 0, T := 0, R := 0, B := 0) {
      If ((L + T + R + B) = 0)
         This.Margins := 0
      Else {
         This.Margins := Buffer(16, 0)
         NumPut("Int", L, "Int", T, "Int", R, "Int", B, This.Margins)
      }
   }
   ; -------------------------------------------------------------------------------------------------------------------
   ; SetTitle()      -  Set or remove the title and/or the icon displayed on the tooltip.
   ; Parameters:
   ;     Title       -  string to be used as title.
   ;     Icon        -  icon to be shown in the ToolTip.
   ;                    This can be the number of a predefined icon (1 = info, 2 = warning, 3 = error
   ;                    (add 3 to display large icons on Vista+) or a HICON handle.
   ; -------------------------------------------------------------------------------------------------------------------
   Static SetTitle(Title := "", Icon := "") {
      Switch {
         Case (Title = "") && (Icon != ""):
            This.Icon := Icon
            This.Title := " "
         Case (Title != "") && (Icon = ""):
            This.Icon := 0
            This.Title := Title
         Default:
            This.Icon := Icon
            This.Title := Title
      }
   }
   ; -------------------------------------------------------------------------------------------------------------------
   ; For internal use only!
   ; -------------------------------------------------------------------------------------------------------------------
   Static _WNDPROC_(hWnd, uMsg, wParam, lParam) {
      ; WNDPROC -> https://learn.microsoft.com/en-us/windows/win32/api/winuser/nc-winuser-wndproc
      Switch uMsg {
         Case 0x0411: ; TTM_TRACKACTIVATE - just handle the first message after the control has been created
            If This.ToolTips.Has(hWnd) && (This.ToolTips[hWnd] = 0) {
               If (This.BkgColor != "")
                  SendMessage(1043, This.BkgColor, 0, hWnd)                ; TTM_SETTIPBKCOLOR
               If (This.TxtColor != "")
                  SendMessage(1044, This.TxtColor, 0, hWnd)                ; TTM_SETTIPTEXTCOLOR
               If This.HFONT
                  SendMessage(0x30, This.HFONT, 0, hWnd)                   ; WM_SETFONT
               If (Type(This.Margins) = "Buffer")
                  SendMessage(1050, 0, This.Margins.Ptr, hWnd)             ; TTM_SETMARGIN
               If (This.Icon != "") || (This.Title != "")
                  SendMessage(1057, This.Icon, StrPtr(This.Title), hWnd)   ; TTM_SETTITLE
               This.ToolTips[hWnd] := 1
            }
         Case 0x0001: ; WM_CREATE
            DllCall("UxTheme.dll\SetWindowTheme", "Ptr", hWnd, "Ptr", 0, "Ptr", StrPtr(""))
            This.ToolTips[hWnd] := 0
         Case 0x0002: ; WM_DESTROY
            This.ToolTips.Delete(hWnd)
      }
      Return DllCall(This.OWP, "Ptr", hWnd, "UInt", uMsg, "Ptr", wParam, "Ptr", lParam, "UInt")
   }
   ; -------------------------------------------------------------------------------------------------------------------
   Static _EXIT_(*) {
      If (ToolTipOptions.OWP != 0)
         ToolTipOptions.Reset()
   }
}
[/cut]

Добавлено: Ср фев 11, 2026 19:35
Orion9
FallenAngel
Если вкратце.

Функция NotifyIcon принимает следующие параметры:

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

  sAction - тип действия ("add", "set", "delete")
      uID - внутренний уникальный идентификатор иконки
nCallback - код зарегистрированного сообщения для функции обратного вызова
    hIcon - дескриптор иконки (только для "add")
     sTip - текст подсказки в трее
    sText - текст уведомления (только для "add" и "set")
   sTitle - текст заголовка уведомления
    nType - флаги NIIF_* (изменение вида и поведения окна уведомлений)
    
Func NotifyIcon(sAction, uID, nCallback = 0, hIcon = 0, sTip = "", sText = "", sTitle = "", nType = 0)
Чтобы добавить иконку в трей, нужен режим "add" и еще 3 параметра. Четвертый параметр sTip тоже желателен, т.к. он отвечает за текст подсказки в трее, но его в принципе можно не указывать.

Все сводится к одному вызову, например:

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

NotifyIcon("add", 1005, gCode, hIco, "Иконка Autorun")
Первый и последний параметры нам уже понятны. Осталось разобраться за что именно отвечают uID = 1005, nCallback = gCode, hIcon = hIco.

uID - это просто внутренний код иконки, взятый буквально от балды. Приложение может держать в трее несколько иконок, оболочке Windows и нам самим нужно как-то их отличать. Код может быть любым числом. Здесь он 1005.

nCallback - это код, который использует оболочка, когда она посылает сообщения о событиях в трее назад приложению. И снова этот код мы должны сами придумать, но только в определенном диапазоне. Чтобы не забивать этим голову, мы просим систему выделить нам уникальный незанятый код. Каким именно будет этот код, нам не важно, мы просто запоминаем его в переменной gCode:

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

Global gCode = DllCall("RegisterWindowMessageW", "wstr", "TrayNotify1005", "uint")
Теперь получив нужный код, мы должны внутри приложения привязать его к реальной функции, которая будет запускаться каждый раз, когда оболочка шлет нам сообщения с этим кодом.

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

SetMessageAction /P %"gCode" "TestrayAction"
Внутри функции мы должны обрабатывать те сообщения, события которых нам интересны. Это прежде всего события левого и правого клика, но можно обрабатывать и другие события.

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

Func TestrayAction(hWnd, uMsg, wParam, lParam)
    If lParam = WM_LBUTTONDOWN Then
       MsgBox("Клик по иконке")
    ElseIf lParam = WM_RBUTTONDOWN Then
       ShowPopupMenu("~/D", COMMANDER_PATH & "\Bars\Vertical.bar")
    EndIf
EndFunc
Левый клик по иконке выводит MsgBox, правый отображает меню из Vertical.bar. Можно использать и другие команды, em_команды, mnu и bar файлы — здесь есть, где фантазии развернуться.

Остается только иконка. Дескриптор иконки hIcon можно получать по-разному. Самый простой способ — послать сообщение окну ТС с кодом 0x7f, тогда тотал вернет дескриптор своей иконки:

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

hIcon = SendMessage(AUTORUN_TCHANDLE, 0x7f, 2, 0)
Можно также получить дескриптор функцией ExtractIcon, вызывать которую нужно через DllCall

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

sIco = GetKnownFolderPath("System") & "\shell32.dll"
hIco = DllCall("shell32\ExtractIconW", _
               "ptr", AUTORUN_TCHANDLE, _
               "wstr", sIco, "uint", 15, "ptr")
В вызовах DllCall нет ничего страшного. По сути, это просто запрос к Windows, чтобы она выполнила за нас определенные действия. В данном случае таким действием является извлечение иконки с индексом 15 из shell32.dll и передача нам дескриптора.

Нам, в свою очередь, остается передать этот дескриптор функции NotifyIcon вместе с другими параметрами. Итого, полученный код будет выглядеть так:

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

# регистрация кода для функции обратного вызова
Global gCode = DllCall("RegisterWindowMessageW", "wstr", "TrayNotify1005", "uint")
# привязка функции обратного вызова к полученному коду
SetMessageAction /P %"gCode" "TestrayAction"

# функция обратного вызова
Func TestrayAction(hWnd, uMsg, wParam, lParam)
    If lParam = WM_LBUTTONDOWN Then
       #MsgBox("Клик по иконке")
       NotifyIcon("set", 1005, gCode, hIco, "", "Test message", "", NIIF_WARNING)
    ElseIf lParam = WM_RBUTTONDOWN Then
       ShowPopupMenu("~/D", COMMANDER_PATH & "\Bars\Vertical.bar")
    EndIf
EndFunc

# путь к иконке
sIco = GetKnownFolderPath("System") & "\shell32.dll"
# получение дескриптора иконки с индексом 15
hIco = DllCall("shell32\ExtractIconW", _
               "ptr", AUTORUN_TCHANDLE, _
               "wstr", sIco, "uint", 15, "ptr")
               
# установка в трей с идентификатором 1005   
NotifyIcon("add", 1005, gCode, hIco, "Иконка Autorun")
Вот так. Несколько строк и [s]дело в шляпе[/s] иконка в трее :)

Однако, чтобы этот фрагмент кода заработал самостоятельно, без привязки к модулю Icons.aucfg, необходимо перенести из него все константы и, собственно, саму функцию NotifyIcon:
[cut]

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

# иконки окна уведомлений
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  

# системные сообщения 
Const WM_MOUSEMOVE     = 0x0200, _
      WM_LBUTTONDOWN   = 0x0201, _
      WM_LBUTTONUP     = 0x0202, _
      WM_LBUTTONDBLCLK = 0x0203, _
      WM_RBUTTONDOWN   = 0x0204, _
      WM_RBUTTONUP     = 0x0205, _
      WM_RBUTTONDBLCLK = 0x0206, _   
      WM_MBUTTONDOWN   = 0x0207, _
      WM_MBUTTONUP     = 0x0208, _      
      WM_MBUTTONDBLCLK = 0x0209
      
#{
     Добавление иконки в трей:
   
            sAction - тип действия ("add", "set", "delete")
                uID - внутренний уникальный идентификатор иконки
          nCallback - код зарегистрированного сообщения для функции обратного вызова
              hIcon - дескриптор иконки (только для "add")
               sTip - текст подсказки в трее
              sText - текст уведомления (только для "add" и "set")
             sTitle - текст заголовка уведомления
              nType - флаги NIIF_* (изменение вида и поведения окна уведомлений)
            
     https://learn.microsoft.com/en-us/windows/win32/api/shellapi/ns-shellapi-notifyicondataw            
#}
Func NotifyIcon(sAction, uID, nCallback = 0, hIcon = 0, sTip = "", sText = "", sTitle = "", nType = 0)
    Local nAction, nFlags
    Static TIP_MAXCHAR = 127, INFO_MAXCHAR = 255, TITLE_MAXCHAR = 63
    Static buf = Buffer(auPtrSize = 4 ? 956 : 976)   
    buf.Zero()
    nFlags = BitOR(    hIcon = 0 ? 0 : NIF_ICON, _
                   nCallback = 0 ? 0 : NIF_MESSAGE, _
                StrLen(sTip) = 0 ? 0 : NIF_TIP, _
               StrLen(sText) = 0 ? 0 : NIF_INFO    )
    Switch sAction
       Case "add"
          nAction = 0
       Case "set"
          nAction = 1
      Case "delete"
          nAction = 2
       Else
          Return 
    EndSwitch
    If auX64 Then
        buf.SetNum(0, "dword", buf.size)
        buf.SetNum(8, "hwnd",  AUTORUN_TCHANDLE)
        buf.SetNum(16, "uint", uID, _
                       "uint", nFlags, _
                       "uint", nCallback)
        buf.SetNum(32, "ptr",  hIcon)
    Else
        buf.SetNum(0, "dword", buf.size, _   
                       "hwnd", AUTORUN_TCHANDLE, _         
                       "uint", uID, _
                       "uint", nFlags, _
                       "uint", nCallback, _
                        "ptr", hIcon) 
    EndIf
    Local nOffset = auX64 ? 40 : 24   # PtrSize*2 + 4*4 + padding for X64
    If sTip <> 0 Then
       buf.SetStr(StrLeft(sTip, TIP_MAXCHAR) & Chr(0), nOffset)
    EndIf   
    If sText <> 0 Then
       nOffset += 264        
       buf.SetStr(StrLeft(sText, INFO_MAXCHAR) & Chr(0), nOffset)   # 256 + 4 + 4       
       If sTitle <> 0 Then
          buf.SetStr(StrLeft(sTitle, TITLE_MAXCHAR) & Chr(0), nOffset + 516)   # 512 + 4
       EndIf
       If nType > 0 Then
          buf.SetNum(nOffset + 644, "dword", nType)   # 128
       EndIf
    EndIf    
    Return DllCall("Shell32.dll\Shell_NotifyIconW", "uint", nAction, "ptr", buf.ptr, "bool")
EndFunc

# регистрация кода для функции обратного вызова
Global gCode = DllCall("RegisterWindowMessageW", "wstr", "TrayNotify1005", "uint")
# привязка функции обратного вызова к полученному коду
SetMessageAction /P %"gCode" "TestrayAction"

# функция обратного вызова
Func TestrayAction(hWnd, uMsg, wParam, lParam)
    If lParam = WM_LBUTTONDOWN Then
       If IsPressed(0x11) Then 
          NotifyIcon("set", 1005, gCode, hIco, "", "Test message", "", NIIF_WARNING)
          Return
       EndIf   
       MsgBox("Клик по иконке")
    ElseIf lParam = WM_RBUTTONDOWN Then
       ShowPopupMenu("~/D", COMMANDER_PATH & "\Bars\Vertical.bar")
    EndIf
EndFunc

# путь к иконке
sIco = GetKnownFolderPath("System") & "\shell32.dll"
# получение дескриптора иконки с индексом 15
hIco = DllCall("shell32\ExtractIconW", _
               "ptr", AUTORUN_TCHANDLE, _
               "wstr", sIco, "uint", 15, "ptr")
               
# установка в трей с идентификатором 1005   
NotifyIcon("add", 1005, gCode, hIco, "Иконка Autorun")
[/cut]
Работает у вас?

В этот код я добавил обработку CTRL:

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

If IsPressed(0x11) Then 
   NotifyIcon("set", 1005, gCode, hIco, "", "Test message", "", NIIF_WARNING)
   Return
EndIf
Теперь клик по иконке с Ctrl отображает системное уведомление. Функция NotifyIcon может не только добавлять или менять иконку в трее, но также показывать системные уведомления. Единственное условие — иконка в трее должна существовать. Именно поэтому, чтобы была возможность вызвать системные уведомления без привязки к конкретной иконке, реализована функция NotifyInfoMessage.

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

#{
     Вызов системных уведомлений:
     
           InfoText - текст уведомления (255 символов)
          InfoTitle - текст заголовка уведомления (63 символов)
           InfoType - флаги NIIF_* меняющие вид и поведение окна уведомлений 
            
     Функция использует идентификатор "1010"
#}
Func NotifyInfoMessage(InfoText, InfoTitle = "Autorun", InfoType = 0)
Функция заранее резервирует идентификатор иконки и не требует указания дескриптора — дескриптор берется из файла, указанного в gNotifyIcon. Если файл не существует, будет использоваться иконка тотала. Для обратного вызова также получается код и сохраняется в переменную Code1010. Функция обратного вызова определена как TrayNotifyInfo. Данная функция обрабатывает левый клик по иконке в трее, где отображается меню лога уведомлений. Это еще одна особенность NotifyInfoMessage — она сохраняет лог уведомлений в текущей сессии. Т.о. даже если уведомление было пропущено (закрыто по таймауту) в трее можно будет увидеть его след:
[cut]

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

lobal gNotifyIcon = COMMANDER_EXE
#Global gNotifyIcon = COMMANDER_PATH & "\Ini\Newsbar\notify-yes.ico"

# регистрация уникального кода
Global Code1010 = DllCall("RegisterWindowMessageW", "wstr", "TrayNotify1010", "uint")

SetMessageAction /P %"Code1010" "TrayNotifyInfo"

# иконка уведомлений
Func TrayNotifyInfo(hWnd, uMsg, wParam, lParam)
    Switch lParam
       Case WM_LBUTTONDOWN
           ShowPopupMenu /D /F "TrayNotifyInfoMenu"           
       Case WM_RBUTTONDOWN
           #NotifyIcon("delete", 1010)
       Case NIN_BALLOONUSERCLICK
       
       Case NIN_BALLOONTIMEOUT
    EndSwitch
EndFunc

# меню уведомлений
Func TrayNotifyInfoMenu()
    Local txt, nIco, sIco, sTime, sInfo, sType, sFunc, i = 0, j = 0
    For i = TrayLog.Count - 1 To 0 Step -1
        nIco = -1
        sIco = "-1"
        sTime = StrPart(TrayLog[i], Chr(0), 1)        
        sInfo = StrPart(TrayLog[i], Chr(0), 2)
        sType = StrPart(TrayLog[i], Chr(0), 4)
        If BitAND(sType, 3) = 1 Then 
           nIco = 76
        ElseIf BitAND(sType, 3) = 2 Then 
           nIco = 79    
        ElseIf BitAND(sType, 3) = 3 Then 
           nIco = 93       
        EndIf
        If nIco <> -1 Then sIco = "imageres.dll," & nIco
        sFunc = "TrayNotifyItem " & i & " " & nIco
        txt &= 'MENUITEM "' & sTime & " " & sInfo & '", em_aucmd ' & sIco & " " & sFunc & auCRLF
        j += 1
        If j = 30 Then Break 
    Next
    If TrayLog.Count = 0 Then txt &= 'MENUITEM "<Пусто>", em_aucmd' & auCRLF
    txt &= 'MENUITEM SEPARATOR' & auCRLF
    txt &= 'MENUITEM "Копировать", em_aucmd -1 TrayNotifyCopy' & auCRLF
    txt &= 'MENUITEM "Скрыть иконку", em_aucmd -1 NotifyIcon "delete" 1010' & auCRLF    
    Return txt
EndFunc

Func TrayNotifyItem(nItem, nIco)
    Local nFlag
    Local sInfo = StrPart(TrayLog[nItem], Chr(0), 2)
    Local sText = StrPart(TrayLog[nItem], Chr(0), 3)
    Switch nIco
      Case 76
         nFlag = 64
      Case 79
         nFlag = 48
      Case 93
         nFlag = 16
      Else
         nFlag = ''
    EndSwitch
    MsgBox(sText, sInfo, nFlag)
EndFunc

Func TrayNotifyCopy()
    Local txt = TrayLog.Text
    txt = StrReplace(txt, Chr(0), ";")
    ClipPut(txt)
    ShowTransHint("Скопировано в буфер")
EndFunc

#{
     Вызов системных уведомлений:
     
           InfoText - текст уведомления (255 символов)
          InfoTitle - текст заголовка уведомления (63 символов)
           InfoType - флаги NIIF_* меняющие вид и поведение окна уведомлений 
            
     Функция использует идентификатор "1010"
#}
Func NotifyInfoMessage(InfoText, InfoTitle = "Autorun", InfoType = 0)
    Local Offset = auX64 ? 40 : 24, sIcon
    Static buf = Buffer(auPtrSize = 4 ? 956 : 976)
    Static nFlags = BitOR(NIF_MESSAGE, NIF_ICON, NIF_INFO, NIF_TIP)
    If FileExist(gNotifyIcon) Then
       sIcon = gNotifyIcon
    Else
       sIcon = COMMANDER_EXE
    EndIf   
    Static hIco = DllCall("shell32\ExtractIconW", _
                          "ptr", AUTORUN_TCHANDLE, _
                          "wstr", sIcon, "uint", 0, "ptr")
    NotifyIcon("delete", 1010)
    buf.Zero()
    If auX64 Then
        buf.SetNum(0,  "dword", buf.size)
        buf.SetNum(8,  "hwnd", AUTORUN_TCHANDLE)
        buf.SetNum(16, "uint", 1010, _
                       "uint", nFlags, _
                       "uint", Code1010)             
        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", nFlags, _
                      "uint", Code1010, _
                      "ptr",  hIco)
        buf.SetNum(280, "dword", 1, _
                        "dword", 1)

    EndIf
    buf.SetStr("Notify Messages" & Chr(0), Offset)
    buf.SetStr(StrLeft(InfoText, 255) & Chr(0),  Offset + 264)   # 256 + 4 + 4 
    Offset += 264
    If InfoTitle <> "" Then       
       buf.SetStr(StrLeft(InfoTitle, 63) & Chr(0), Offset + 516)   # 512 + 4
    EndIf
    Offset += 516
    If InfoType > 0 Then
       buf.SetNum(Offset + 128, "dword", InfoType)   # 128
    EndIf
    TrayLog.Add(Time() & Chr(0) & InfoTitle & Chr(0) & InfoText & Chr(0) & InfoType)
    Tray1010 = 1
    Return DllCall("Shell32.dll\Shell_NotifyIconW", "uint", 0, "ptr", buf.ptr)
EndFunc
[/cut]
Это только сами функции и их вызов. В реальности код разрастается из-за различных условий и проверок. Например, вряд ли есть необходимость всегда держать иконку в трее, а значит надо как-то учитывать это при запуске — считывать ключ из ини-файла, смотреть его значение, загружать или не загружать иконку в зависимости от этого значения и т.д. Именно поэтому модуль получился больше, чем эти две функции, ведь это мой рабочий модуль, т.е. модуль, который я использую у себя в ТС. Кое-что там лишнее и переплетается с другими модулями, поэтому сам модуль не универсальный, универсальны только эти две функции.

Как-то так :)

Добавлено: Чт фев 12, 2026 13:02
Orion9
Внес некоторые изменения в модуль MediaInfo.aucfg.

Раньше была возможность разделить панель на три области, чтобы отображать в каждой области свою подсказку. Теперь при удержании Shift в подсказке также отображаются данные из плагинов, тем самым общее количество подсказок возросло до шести!

Если добавить сюда переключение полей (функция реализована в модуле как SetHintFields, вызывается по Alt+F12), количество подсказок можно увеличивать до беспредела :)

Однако пока Alt+F12 ограничивается только переключением на ExifHint и JoinHint, при этом JoinHint больше не актульна, хотя может быть использована в качестве примера вывода данных в окно подсказки.

Алгоритм вывода данных из плагинов ранее был реализован в функции JoinHint. Я перенес его в модуль MediaInfo.aucfg и добавил небольшие изменения.

Как это работает. В подсказку выводятся все непустые поля из заранее указанных плагинов в файле WdxHint.cfg. Пример файла:

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

File.wdx;UnicodeTest.wdx;FileX.wdx
Permissions.wdx;NTLinks.wdx;NTFSFileStreams.wdx
anytag.wdx
Script.wdx;WinScriptsAdv.wdx
Соответственно, первая строка в файле выводит в первую область панели данные из плагинов File.wdx, UnicodeTest.wdx и FileX.wdx. Вторая строка выводит во вторую область панели, а третья в третью. Комбинация плагинов в строке может быть любой. Путь к файлу WdxHint.cfg указывается в глобальной переменной наряду с другими параметрами подсказки MediaInfo:

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

Global gHintPipe     =   "", _
       gHintClipb    =   true, _
       gHintLines    =   60, _
       gHintWait     =   0, _      
       gHintShift    =   "duration.txt", _
       gHintCaps     =   true
       
Global gHintWdx      =   0, _        
       gHintWdxList  =   List(), _
       gHintWdxCfg   =   COMMANDER_PATH & "\Ini\WdxHint.cfg"
       
Global gHintLayout   =   List(10, 75, 15), _
       gHintContent  =   List("size.txt", "Inform", "hint.txt"), _
       gHintSleep    =   List(0, 0, 0)
Переменные должны быть объявлены в модуле autorun.cfg, желательно сразу после инструкции Pragma AutorunPluginFields, чтобы избежать ошибки инициализации при запуске ТС (об этом подробнее по первой ссылке).

gHintPipe, gHintWdx и gHintWdxList используются для внутренних целей, менять их значения не нужно. Остальных переменные описавались ранее, как и примеры их использования:
[cut]gHintClipb - копировать в буфер всю подсказку при отображении в страничном режиме
gHintLines - число строк подсказки, больше 60 быть не может
gHintWait - задержка для всех секций перед отображением
gHintShift - шаблон для смены "на лету", только для левой секции
gHintCaps - если false, подсказка будет отображаться без CapsLock или Ctrl
gHintLayout - размер каждой секции в процентах
gHintContent - шаблон для каждой секции; если пустой "", в секцию ничего не попадает, кроме самой подсказки ТС или проводника; если "Inform" - стандартный отчет MediaInfo; если имя файла, то шаблон из файла (файлы должны находиться в каталоге MediaInfo.dll, существование файла не проверяется, в случае отсутствия сообщений об ошибке не будет, отобразится сырой результат отчета MediaInfo).
gHintSleep - задержка для каждой секции.

Пара примеров.

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

gHintCaps     =   false       
gHintLayout   =   List(15, 70, 15)       
gHintContent  =   List("Inform", "", "duration.txt") 
gHintSleep    =   List(500, 0, 0)
Подсказка будет вызываться всегда (CapsLock отключен). Левая секция - 15%, Центр - 70%, Правая секция - 15%. В левой секции стандартный отчет MediaInfo с задержкой 500 млс, в центре ничего не выводится, в правой секции шаблон duration.txt.

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

gHintCaps     =   true
gHintLayout   =   List(10, 80, 10)       
gHintContent  =   List("size.txt", "hint.txt", "Inform")       
gHintSleep    =   List(0, 0, 0)
Подсказка вызывается дополнительно (CapsLock включен). Левая секция - 10%, Центр - 80%, Правая секция - 10%. В левой секции шаблон size.txt, в центре шаблон hint.txt, в правой секции стандартный отчет MediaInfo. Отдельных задержек перед вызовом нет.

Дополнительно шаблон в левой секции может меняться "на лету" с тем, что указан в gHintShift. Комбинация для смены Alt+5.

Кроме Alt+5 действуют еще 4 комбинации: Alt+1..Alt+4. Каждая из этих комбинаций выводит одну страницу последней подсказки и опционально копирует ее в буфер. Такое применение удобно на очень больших подсказках, которые обрезаются на 60-и строках, оставляя одну страницу просмотра. Нажав Alt+2 можно переместиться на вторую страницу и так далее. Всего четыре страницы.[/cut]
В подсказке используются шаблоны MediaInfo: текстовые файлы с собственной разметкой и синтаксисом. Внося изменения в эти файлы, можно подстраивать под себя вывод из MediaInfo.dll. Сейчас используются три шаблона hint.txt, size.txt и duration.txt, можно создать другие или полностью передлать эти.
[cut="hint.txt"]General;"Name: %FileName%.%FileExtension%\nSize: %FileSize/String4%\n[Duration: %Duration%]\n[Duration: %Duration/String1%]\n[Duration: %Duration/String5%]\n
[General: %BitRate/String%][(%BitRate_Mode%)][, %Format%]$if(%Cover%,\, Cover)$if(%IsStreamable%,\, Streamable)[, %FrameRate/String%]"\n

Video;Video #%StreamKindID%: [%Width%x%Height%][, %Format%][(%Format/Family%)][, %Language%][, %AspectRatio/String% (%AspectRatio%)][, %Resolution/String%][, %FrameRate/String%][, %BitRate/String%][(%BitRate_Mode%)]\n
Video_Begin;
Video_Middle;
Video_End;\n

Audio;Audio #%StreamKindID%: [%Channel(s)%ch][, %SamplingRate/String%][, %BitRate/String%][ (%BitRate_Mode%)][, %Format%][, %Format_Profile%][, %Language%][, %FrameRate/String%]\n
Audio_Begin;
Audio_Middle;
Audio_End;\n

Text;Text #%StreamKindID%: %Format%[, %Language/String3%][, %StreamSize/String3%][, %BitRate/String%][, %FrameRate/String%][, %SamplingRate/String%]\n
Text_End;\n

Chapters;Chapters #%StreamKindID%: %Total% chapters\n

Image;Image #%StreamKindID%: [%Width%x%Height%pix][, %Format%][, %StreamSize/String5%][, %ColorSpace% (%ChromaSubsampling%)][, %BitDepth/String%][, %ColorSpace_ICC%]\n

Menu;Menu #%StreamKindID%: [%MenuID/String%][, Format:%Format/String%][ Chapters: \[%Chapters_Pos_Begin%-%Chapters_Pos_End%\]][/cut]
[cut="size.txt"]General;Name: %FileName%.%FileExtension%\r\nSize: %FileSize/String4%\r\nGeneral: [%StreamSize/String%]\r\n

Video;Video #%StreamKindID%: [%StreamSize/String%]\r\n
Audio;Audio #%StreamKindID%: %StreamSize/String%[ - %Language/String%][ | %Title%]\r\n[/cut]
[cut="duration.txt"]General;Name: %FileName%.%FileExtension%\r\nSize: %FileSize/String4%\r\nGeneral:\r\n [Duration: %Duration%]\r\n [Duration: %Duration/String1%]\r\n [Duration: %Duration/String5%]\r\n

Video;Video #%StreamKindID%: \r\n [Duration: %Duration%]\r\n [Duration: %Duration/String1%]\r\n [Duration: %Duration/String5%]\r\n
Audio;Audio #%StreamKindID%: \r\n [Duration: %Duration%]\r\n [Duration: %Duration/String1%]\r\n [Duration: %Duration/String5%]\r\n[/cut]
Шаблоны должны находиться в каталоге библиотеки.

Также в файле autorun.cfg самой первой строкой должны быть прописаны поля подсказки в инструкции Pragma AutorunPluginFields:

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

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"
Указанные 60 полей должны быть предварительно добавлены в секцию HintsCustomField основной конфигурации ТС, т.е. в секции должны быть два щаблона *.* по 30 полей каждый, которые должны ее замыкать.

Возможные сценарии.

1) Если в секции нет других расширений. Достаточно просто применить готовый вариант:
[cut]

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

[HintsCustomField]
0exts=*.*
0fields=[=autorun.C1]\n[=autorun.C2]\n[=autorun.C3]\n[=autorun.C4]\n[=autorun.C5]\n[=autorun.C6]\n[=autorun.C7]\n[=autorun.C8]\n[=autorun.C9]\n[=autorun.C10]\n[=autorun.C11]\n[=autorun.C12]\n[=autorun.C13]\n[=autorun.C14]\n[=autorun.C15]\n[=autorun.C16]\n[=autorun.C17]\n[=autorun.C18]\n[=autorun.C19]\n[=autorun.C20]\n[=autorun.C21]\n[=autorun.C22]\n[=autorun.C23]\n[=autorun.C24]\n[=autorun.C25]\n[=autorun.C26]\n[=autorun.C27]\n[=autorun.C28]\n[=autorun.C29]\n[=autorun.C30]
1exts=*.*
1fields=[=autorun.C31]\n[=autorun.C32]\n[=autorun.C33]\n[=autorun.C34]\n[=autorun.C35]\n[=autorun.C36]\n[=autorun.C37]\n[=autorun.C38]\n[=autorun.C39]\n[=autorun.C40]\n[=autorun.C41]\n[=autorun.C42]\n[=autorun.C43]\n[=autorun.C44]\n[=autorun.C45]\n[=autorun.C46]\n[=autorun.C47]\n[=autorun.C48]\n[=autorun.C49]\n[=autorun.C50]\n[=autorun.C51]\n[=autorun.C52]\n[=autorun.C53]\n[=autorun.C54]\n[=autorun.C55]\n[=autorun.C56]\n[=autorun.C57]\n[=autorun.C58]\n[=autorun.C59]\n[=autorun.C60]
[/cut]
2) Если в секции есть другие шаблоны и маски. Нужно добавить по последнему номеру, например:
[cut]

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

[HintsCustomField]
0exts=>NTFS Ссылка
0fields=[=ntlinks.Obj_Type]\n[=ntlinks.Obj_RealPath.Natural]
1exts=*.apk
1fields=Название: [=apk.Application Name ru]\nВерсия: [=apk.Version] ([=apk.Version Code])\nПакет: [=apk.Package]\nAndroid: [=apk.Min SDK Name]
2exts=*.aucfg; autorun.cfg;*.ini;*.ahk; ;*.au3; *.vbs; *.bat; *.ps1
2fields=Число строк: [=linebreakinfo.CRLF Count]\nБайт прочитано: [=linebreakinfo.Bytes Read]
Ключи в секции заканчиваются вторым номером (0ext-2ext). Новые ключи нужно добавлять с третьего:

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

[HintsCustomField]
0exts=>NTFS Ссылка
0fields=[=ntlinks.Obj_Type]\n[=ntlinks.Obj_RealPath.Natural]
1exts=*.apk
1fields=Название: [=apk.Application Name ru]\nВерсия: [=apk.Version] ([=apk.Version Code])\nПакет: [=apk.Package]\nAndroid: [=apk.Min SDK Name]
2exts=*.aucfg; autorun.cfg;*.ini;*.ahk; ;*.au3; *.vbs; *.bat; *.ps1
2fields=Число строк: [=linebreakinfo.CRLF Count]\nБайт прочитано: [=linebreakinfo.Bytes Read]
3exts=*.*
3fields=[=autorun.C1]\n[=autorun.C2]\n[=autorun.C3]\n[=autorun.C4]\n[=autorun.C5]\n[=autorun.C6]\n[=autorun.C7]\n[=autorun.C8]\n[=autorun.C9]\n[=autorun.C10]\n[=autorun.C11]\n[=autorun.C12]\n[=autorun.C13]\n[=autorun.C14]\n[=autorun.C15]\n[=autorun.C16]\n[=autorun.C17]\n[=autorun.C18]\n[=autorun.C19]\n[=autorun.C20]\n[=autorun.C21]\n[=autorun.C22]\n[=autorun.C23]\n[=autorun.C24]\n[=autorun.C25]\n[=autorun.C26]\n[=autorun.C27]\n[=autorun.C28]\n[=autorun.C29]\n[=autorun.C30]
4exts=*.*
4fields=[=autorun.C31]\n[=autorun.C32]\n[=autorun.C33]\n[=autorun.C34]\n[=autorun.C35]\n[=autorun.C36]\n[=autorun.C37]\n[=autorun.C38]\n[=autorun.C39]\n[=autorun.C40]\n[=autorun.C41]\n[=autorun.C42]\n[=autorun.C43]\n[=autorun.C44]\n[=autorun.C45]\n[=autorun.C46]\n[=autorun.C47]\n[=autorun.C48]\n[=autorun.C49]\n[=autorun.C50]\n[=autorun.C51]\n[=autorun.C52]\n[=autorun.C53]\n[=autorun.C54]\n[=autorun.C55]\n[=autorun.C56]\n[=autorun.C57]\n[=autorun.C58]\n[=autorun.C59]\n[=autorun.C60]
[/cut]
После добавления полей в секцию, нужно зайти в диалог Настройки -> Содержимое панелей -> Пользовательские и поставить галочку "Объединять все подсказки, подходящие по типу файлов".

Всё. Предварительная часть установки завершена. Осталось подключить модуль.

Модуль подключается стандартно, директивой Pragma Include, в основной конфигурации Autorun:

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

Pragma Include %COMMANDER_PATH%\Ini\Scripts\MediaInfo.aucfg
После перезапуска ТС альтернативная подсказка из модуля должна отображаться на панелях при наведении курсора мыши на нужный файл с удержанием CTRL или включеном CapsLock. Однако CTRL работает только в 32-битной версии ТС из-за некоторых ограничений.

Окно панели поделится на 3 части: 10% справа, 75% центр, 15% слева. В каждой области отображается своя подсказка из MediaInfo.dll. Удержание Shift переключает отображение на плагины. Поскольку Shift не работает в TCx64, как и CTRL, переключением доступно только через Win+CapsLock. Данная комбинация работает и в TCx32.

Кроме подсказки, в модуле реализована кнопка для подсчета времени воспроизведения у выделенных файлов и каталогов. Код кнопки 71000. Подробнее о функционале здесь

Сам по себе модуль автономный, если не считать манипуляций описанных выше, связанных прежде всего с установкой подсказки в качестве альтернативной. В заголовке модуля требуется только указать корректные пути к библиотеке MediaHint.dll и утилите ExiTool:

[cut="MediaInfo.aucfg"]

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

Pragma IncludeOnce

# 71000-71099

RegisterCommand 71000 "Duration"
RegisterCommand 71010 "MediaInfoTest"

# Alt+F12
SetHotkeyAction /K:A /H:F12 SetHintFields

# Alt+1(..5)
SetHotkeyAction /K:A /H:1 /DM /S ShowPipeEx 0 1
SetHotkeyAction /K:A /H:2 /DM /S ShowPipeEx 0 2
SetHotkeyAction /K:A /H:3 /DM /S ShowPipeEx 0 3
SetHotkeyAction /K:A /H:4 /DM /S ShowPipeEx 0 4
SetHotkeyAction /K:A /H:5 /DM /S LeftsideHintType

# Win+CapsLock
SetHotkeyAction /V:20 /K:W MediaHintType

Global gMediaInfoX32 = COMMANDER_PATH & "\Ini\Tools\Libs\MediaInfo_i386.dll"
Global gMediaInfoX64 = COMMANDER_PATH & "\Ini\Tools\Libs\MediaInfo.dll"
Global gMediaExifX32 = COMMANDER_PATH & "\Plugins\wlx\ExifToolView\exiftool.exe"

Global gDurationInfo = 0, gDurationBreak = false, gDurationBaloon = 1, gDurationTXT

Global gFilterAudio = ".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"

Global gFilterVideo = ".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"

Global gBTipWndProc, gBTipWnd = Map(), gBTip = 0
Global gBText = Buffer(1024*4),  gBTool = Buffer(auX64 ? 56 : 40)
Global gBTipX, gBTipY
Global gBTipWP = Callback("BTipWndProc", "hwnd;uint;wparam;lparam")

Func Duration(lParam)
    If gDurationInfo > 0 Then
       gDurationBreak = true
       Sleep(250)
       Return
    EndIf
    If gDurationBaloon Then
       gBTip = CreateBaloonTip("MediaInfo", 1, 200)
       If gBTip = 0 Then Return MsgBox("Не удалось создать окно подсказки BaloonTip")       
    EndIf   
    RunThread DurationInfo
EndFunc

Func DurationInfo()
    Local T1 = GetUptime(), T2 = T1
    Local bCaps = BitAND(DllCall("GetKeyState", "int", 0x14, "short"), 1)    
    Local bCtrl = IsPressed(0x11), bShift = IsPressed(0x10), bAlt = IsPressed(0x12)
    Static sLib = auX64 ? gMediaInfoX64 : gMediaInfoX32
    Static hLib = DllCall("LoadLibrary", "wstr", sLib, "ptr")
    Static sExt = gFilterAudio & " " & gFilterVideo
    If Not bAlt Then
       If hLib = 0 Then 
          ShowMediaHint("Error LoadLibrary " & sLib)
          Return 
       EndIf
       Static pNew = DllCall("GetProcAddress", "Ptr", hLib, "Str", "MediaInfo_New", "Ptr")
       Static pOpen = DllCall("GetProcAddress", "Ptr", hLib, "Str", "MediaInfo_Open", "Ptr")
       Static pGet = DllCall("GetProcAddress", "Ptr", hLib, "Str", "MediaInfo_Get", "Ptr")
       Static pDel = DllCall("GetProcAddress", "Ptr", hLib, "Str", "MediaInfo_Delete", "Ptr")
       Local hMI = DllCall(pNew, "Ptr")
       If hMI = 0 Then 
          ShowMediaHint("MediaInfo.dll couldn't create new object")
          Return
       Endif
    Else     
       Local obj = Plugin("TCMediaInfo")
       If ERROR <> 0 Then
          ShowMediaHint("TCMediaInfo.wdx plugin error " & ERROR)
          Return
       Endif
    EndIf
    Local sDuration
    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 
          ShowMediaHint("Файл не существует " & sFile)
          Free(aSel)
          If bAlt Then Free(obj)
          Return
       Endif
    EndIf       
    If aSel.Count = 0 And Not StrPos(FileGetAttr(sFile), "D") Then
       Free(aSel)
       If bAlt Then
          obj.FileName = sFile     
          sDuration = obj.GetValue(0)	# duration
          Free(obj)
       Else
          If DllCall(pOpen, "Ptr", hMI, "Wstr", sFile, "Uint") <> 1 Then
             Return ShowMediaHint("MediaInfo.dll coudn't open file " &  sFile)                            
          EndIf
          sDuration = DllCall(pGet, "Ptr", hMI, "Int", 0, "Int", 0, _
                                    "Wstr", "Duration/String3", "Int", 1, "Int", 0, "Wstr")
       EndIf   
       Return ShowMediaHint("Файл: " & sName & auCRLF & "Длительность: " & sDuration, 1) 
    Endif
    gDurationInfo = 1    
    gDurationBreak = false
    ShowMediaHint("Загрузка", 1)    
    Local i, j, valid = 0
    Local dirs = 0, files = 0, errs = 0, item = 0, aFiles = List(), seconds = 0, b_Hint = 0
    Local depth = (bCtrl ? 1 : 0), ms = 0, vc, ac
    bExtLog = false
    # загрузка файлов в массив    
    If aSel.Count > 0 Then
       For j = 0 To aSel.Count - 1
          sFile = sPath & aSel[j]
          If FileExist(sFile) Then 
             valid += 1
             aFiles.Add(sFile)
             If StrPos(FileGetAttr(sFile), "D") Then 
                If ListMediaFiles(sFile, aFiles, depth) > 0 Then aFiles.Count = 0
             EndIf   
          EndIf   
       Next
       sName = aSel.Count & "/" & valid
    Else
       aFiles.Add(sFile)
       ListMediaFiles(sFile, aFiles, depth)
    EndIf
    Free(aSel)
    If aFiles.Count > 1 Then
       ShowMediaHint("Depth: " & depth & auCRLF & _
                     "Filter: " & (Not bShift ? "Yes" : "No") & auCRLF & _
                     (bAlt ? "TCMediaInfo.wdx" : "MediaInfo.dll") & auCRLF & _
                     "Элементов: " & aFiles.Count & auCRLF & _
                     "Подсчёт времени...", 1)
    EndIf
    T2 = GetUptime()
    DurationLogString("Duration.Name:" & sName)    
    DurationLogString("Duration.Items:" & aFiles.Count)
    For i = 0 To aFiles.Count - 1
        If bExtLog Then DurationLogString("Duration.File:" & aFiles[i])
        If StrPos(FileGetAttr(aFiles[i]), "D") Then
           dirs += 1
           If bExtLog Then DurationLogString("Duration.Directory:" & aFiles[i])
           Continue 
        EndIf
        item += 1
        If bAlt Then
           obj.FileName = aFiles[i]
           ms = obj.GetValue(0,3)	# duration -> milliseconds
        Else
           ext = FileGetExt(aFiles[i])
           If Not bShift Then
              If Not StrPos(sExt, '.' & ext) Then 
                 DurationLogString("Duration.Filter:" & aFiles[i])
                 Continue
              EndIf
           EndIf
           If DllCall(pOpen, "Ptr", hMI, "Wstr", aFiles[i], "Uint") <> 1 Then
              errs += 1
              DurationLogString("Duration.Error:" & aFiles[i])
              Continue        
           EndIf           
           ms = DllCall(pGet, "Ptr", hMI, "Int", 0, "Int", 0, _
                              "Wstr", "Duration", "Int", 1, "Int", 0, "Wstr")
           vc = DllCall(pGet, "Ptr", hMI, "Int", 0, "Int", 0, _
                              "Wstr", "VideoCount", "Int", 1, "Int", 0, "Wstr")
           ac = DllCall(pGet, "Ptr", hMI, "Int", 0, "Int", 0, _
                              "Wstr", "AudioCount", "Int", 1, "Int", 0, "Wstr")
           If ms > 0 And vc = "" And ac = "" Then
              DurationLogString("Duration.Warning:" & aFiles[i])
              Continue
           EndIf                              
        EndIf
        If ms > 0 Then
           seconds += ms
           files += 1
        Else
           DurationLogString("Duration.Empty:" & aFiles[i])
        EndIf
        If gDurationBreak = true Or (IsPressed (0x11) And IsPressed (0x1B)) Then
           ShowMediaHint("Операция прервана", 1)
           Sleep(500)
           If gDurationBaloon Then
              Static buf = Buffer(160)
              buf.Zero()
              buf.SetStr("Прервано" & Chr(0))
              SendMessage(gBTip, 1057, 3, buf.ptr)              
           EndIf   
           Break
        EndIf
        If Round(GetUptime() - T2, 0) > 500 Then
           ShowMediaHint("Depth: " & depth & auCRLF & _
                         "Filter: " & (Not bShift ? "Yes" : "No") & auCRLF & _
                         (bAlt ? "TCMediaInfo.wdx" : "MediaInfo.dll") & auCRLF & _
                         "Элементов: " & aFiles.Count & auCRLF & _
                         "Обработка: " & i & " из " & aFiles.Count, 1)
           T2 = GetUptime()
         EndIf
    Next    
    If bAlt Then Free(obj)
    If Not bAlt And hMI <> 0 Then DllCall(pDel, "Ptr", hMI)
    gDurationInfo = 0    
    gDurationBreak = false
    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 
    Local txt = "Имя: " & 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 " & (bAlt ? "TCMediaInfo.wdx" : "MediaInfo.dll") & auCRLF & _
                "Время операции: " &  StrFormat("%.3f", tl) & " sec"
    DurationLogString(txt & auCRLF & auCRLF)
    Free(aFiles)
    ClipPut(txt)
    ShowMediaHint(txt, 1)
EndFunc

Func ListMediaFiles(sPath, ByRef aList, nDepth = 1)
    Local sFile, nAttr
    Local ffd = Buffer(604), bEsc = false
    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
                     ListMediaFiles(sPath & "\" & sFile, aList, nDepth - 1)
                  EndIf   
               EndIf
            Else
               aList.Add(sPath & "\" & sFile)
            Endif     
            If DllCall("FindNextFileW", "handle", hf, "ptr", ffd.Ptr) = 0 Then Break
            If IsPressed (0x1B) Then
                bEsc = true
                Break
            EndIf    
        Wend
        DllCall("FindClose", "handle", hf)
    Endif 
    Free(ffd)
    Return bEsc
EndFunc

Func DurationLogString(LogString)
    gDurationTXT &= LogString & auCRLF
EndFunc

Func ShowMediaHint(MediaHint, Persist = false)
    # удаление подсказки тотала
    If gDurationBaloon And gBTip > 0 Then
       Local hTip = WinFind(0, "TToolTip")
       If hTip > 0 Then SendMessage(hTip, 0x0010, 0, 0)
       UpdateBaloonTip(gBTip, MediaHint)
       Return
    EndIf
    SetHintParam("ShowHint", "Font", 11, "Arial")
    #SetHintParam("ShowHint", "BackColor", 0x000000)
    #SetHintParam("ShowHint", "Text", 0xFFFFFF)
    SetHintParam("ShowHint", "DarkBackColor", 0xFF0000)
    SetHintParam("ShowHint", "DarkText", 0xFFFFFF)
    If Persist Then
       ShowHint(MediaHint)    
    Else    
       ShowHint(MediaHint, "", "", 3000, 1)
    EndIf   
    Sleep(10)
    SetHintParam("ShowHint", "Reload")  
EndFunc

Func MediaInfoTest(lParam)
    Local sFile = RequestCopyDataInfo("SP") & RequestCopyDataInfo("SN")
    If Not FileExist(sFile) Then 
       ShowHint("Файл не существует " & sFile)
       Return 
    Endif    
    If StrPos(FileGetAttr(sFile), "D") Then
       ShowHint("Каталог " & sFile)
       Return 
    Endif
    Static sLib = auX64 ? gMediaInfoX64 : gMediaInfoX32
    Static hLib = DllCall("LoadLibrary", "wstr", sLib, "ptr")
    If hLib = 0 Then 
       ShowHint("Error LoadLibrary " & sLib)
       Return
    EndIf
    Static sPath = FileGetDir(sLib)
    Static sLang ="file://" & sPath & "\ru.csv"
    Static pNew = DllCall("GetProcAddress", "Ptr", hLib, "Str", "MediaInfo_New", "Ptr")
    Static pOpen = DllCall("GetProcAddress", "Ptr", hLib, "Str", "MediaInfo_Open", "Ptr")
    Static pGet = DllCall("GetProcAddress", "Ptr", hLib, "Str", "MediaInfo_Get", "Ptr")
    Static pDel = DllCall("GetProcAddress", "Ptr", hLib, "Str", "MediaInfo_Delete", "Ptr")
    Static pOpt = DllCall("GetProcAddress", "Ptr", hLib, "Str", "MediaInfo_Option", "Ptr")
    Static pInfo = DllCall("GetProcAddress", "Ptr", hLib, "Str", "MediaInfo_Inform", "Ptr")    
    Local hMI = DllCall(pNew, "Ptr")
    If hMI = 0 Then 
       ShowHint("MediaInfo.dll couldn't create new object")
       Return 
    Endif
    If DllCall(pOpen, 'ptr', hMI, "wstr", sFile, "uint") <> 1 Then
       DllCall(pDel, "Ptr", hMI)        
       Return ShowHint("MediaInfo.dll couldn't open file " &  sFile)
    EndIf
    Local sMI
    sMI &= "Duration: "
    sMI &= DllCall(pGet, 'ptr', hMI, "int", 0, "int", 0, "wstr", "Duration", "int", 1, "int", 0, "wstr") & auCRLF
    sMI &= "Video: "    
    sMI &= DllCall(pGet, 'ptr', hMI, "int", 0, "int", 0, "wstr", "VideoCount", "int", 1, "int", 0, "wstr") & auCRLF
    sMI &= "Audio: "    
    sMI &= DllCall(pGet, 'ptr', hMI, "int", 0, "int", 0, "wstr", "AudioCount", "int", 1, "int", 0, "wstr") & auCRLF
    sMI &= "Recorded: "    
    sMI &= DllCall(pGet, 'ptr', hMI, "int", 0, "int", 0, "wstr", "Recorded_Date", "int", 1, "int", 0, "wstr") & auCRLF
    sMI &= "Cover: "    
    sMI &= DllCall(pGet, 'ptr', hMI, "int", 0, "int", 0, "wstr", "Cover", "int", 1, "int", 0, "wstr") & auCRLF
    sMI &= "Cover Mime: "    
    sMI &= DllCall(pGet, 'ptr', hMI, "int", 0, "int", 0, "wstr", "Cover_Mime", "int", 1, "int", 0, "wstr") & auCRLF 
    sMI &= "Track/Position: "    
    sMI &= DllCall(pGet, 'ptr', hMI, "int", 0, "int", 0, "wstr", "Track/Position", "int", 1, "int", 0, "wstr")
    If IsPressed(0x11) Then
       sMI = ""
       DllCall(pOpt, "Ptr", hMI, "Wstr", "Complete", "Wstr", "0", "Wstr")
       DllCall(pOpt, 'Ptr', hMI, "Wstr", "Language", "Wstr", sLang, "Wstr")       
       DllCall(pOpt, "Ptr", hMI, "Wstr", "Output", "Wstr", "Inform", "Wstr")       
       Local lst = List()
       lst.Text = DllCall(pInfo, "Ptr", hMI, "Uint", 0, "Wstr")
       For line in lst
          sMI &= FixSpaces(line) & auCRLF
       Next
       Free(lst)
    EndIf
    DllCall(pDel, "Ptr", hMI)
    If gDurationBaloon Then
       gBTip = CreateBaloonTip("MediaInfo", 1, 300)
       If gBTip = 0 Then Return MsgBox("Не удалось создать окно подсказки BaloonTip")
    EndIf
    If StrTrim(sMI) = "" Then sMI = "нет данных"
    ShowMediaHint(sMI)
EndFunc

Func CreateBaloonTip(TipTitle, TipIcon, TipWidth)
    Static TTM_TRACKACTIVATE   = 1041, _
           TTM_TRACKPOSITION   = 1042, _
           TTM_SETMAXTIPWIDTH  = 1048, _
           TTM_SETTITLE        = 1057, _
           TTM_ADDTOOL         = 1074
    Local hTip
    hTip = DllCall("CreateWindowExW", _
                 "dword", 8, _
                 "wstr", "tooltips_class32", _
                 "wstr", "", _
                 "dword", 0x00000c0, _
                 "int", 0, "int", 0, "int", 0, "int", 0, _
                 "handle", AUTORUN_TCHANDLE, _
                 "handle", 0, "handle", 0, "ptr", 0, "handle")
    If hTip = 0 Then Return 0
    MouseGetPos("gBTipX","gBTipY")
    #gBTipWnd.Set(hTip, MakeInt(gBTipX, gBTipY, 2))
    gBTipWndProc = DllCall("SetWindowLong" & (auX64 ? "PtrW" : "W"), _
                           "hwnd", hTip, "int", -4, "long_ptr", gBTipWP.Ptr, "ptr")
    Local buf = Buffer(160)
    buf.Zero()
    buf.SetStr(TipTitle & Chr(0))
    SendMessage(hTip, TTM_SETTITLE, TipIcon, buf.ptr)
    gBText.Zero()
    gBText.SetStr("Инициализация" & Chr(0))    
    gBTool.Zero()                              # TTTOOLINFO
    If auX64 Then
       gBTool.SetNum(0, "uint", gBTool.size, _
                        "uint", 0x00a0, _
                        "hwnd", AUTORUN_TCHANDLE, _
                        "uint_ptr", 0)
       gBTool.SetNum(48, "ptr", gBText.ptr)    
    Else
       gBTool.SetNum(0, "uint", gBTool.size, _
                        "uint", 0x00a0, _
                        "hwnd", AUTORUN_TCHANDLE, _
                        "uint_ptr", 0, _
                        "long", 0, _
                        "long", 0, _
                        "long", 0, _
                        "long", 0, _
                        "ptr", 0, _
                        "ptr", gBText.ptr)
    EndIf
    SendMessage(hTip, TTM_SETMAXTIPWIDTH, 0, TipWidth)
    SendMessage(hTip, TTM_ADDTOOL, 0, gBTool.ptr)
    SendMessage(hTip, TTM_TRACKACTIVATE, 1, gBTool.ptr)
    SendMessage(hTip, TTM_TRACKPOSITION, 0, MakeInt(gBTipX, gBTipY, 0))
    Free(buf)
    Return hTip 
EndFunc

Func UpdateBaloonTip(hTip, TipText)
    Static TTM_UPDATETIPTEXT = 1081
    gBText.Zero()
    gBText.SetStr(TipText & Chr(0)) 
    SendMessage(gBTip, TTM_UPDATETIPTEXT, 0, gBTool.ptr)
EndFunc

Func BTipWndProcTest(hWnd, uMsg, wParam, lParam)
    Static IsDrag = 0, _
           MK_LBUTTON      = 0x0001, _
           WM_MOUSEMOVE    = 0x0200, _
           WM_LBUTTONDOWN  = 0x0201, _
           WM_LBUTTONUP    = 0x0202, _
           TTM_TRACKPOSITION = 1042
    Static tx, ty       
    Switch uMsg
       Case WM_LBUTTONDOWN 
          If IsDrag = 0 Then
             MouseGetPos("tx","ty")
             If DllCall("DragDetect", "hwnd", hWnd, "int64", MakeInt(ty, tx, 2)) Then                
                IsDrag = 1
                DllCall("SetCapture", "hwnd", hWnd, "hwnd")     
             EndIf
          EndIf         
       Case WM_LBUTTONUP
          If IsDrag = 1 Then
             WinGetPos("gBTipX", "gBTipY", "", "", hWnd)                                         
             IsDrag = 0
             DllCall("ReleaseCapture") 
          Else
             DllCall("DestroyWindow", "handle", hWnd)                      
          EndIf  
       Case WM_MOUSEMOVE
          If IsDrag = 1 And BitAND(wParam, MK_LBUTTON) Then
             MouseGetPos("x","y")             
             SendMessage(hWnd, TTM_TRACKPOSITION, 0, MakeInt(gBTipX+(x-tx), gBTipY+(y-ty), 0))    
          EndIf       
    EndSwitch
    Return DllCall("CallWindowProcW", _
                   "ptr", gBTipWndProc, "hwnd", hWnd, "uint", uMsg, "wparam", wParam, "lparam", lParam)
EndFunc

Func BTipWndProc(hWnd, uMsg, wParam, lParam)
    Static IsDrag = 0, _
           WM_CREATE       = 0x0001, _
           WM_DESTROY      = 0x0002, _
           WM_SHOWWINDOW   = 0x0018, _     
           MK_LBUTTON      = 0x0001, _
           WM_MOUSEMOVE    = 0x0200, _
           WM_LBUTTONDOWN  = 0x0201, _
           WM_LBUTTONUP    = 0x0202, _
           WM_RBUTTONUP    = 0x0205, _           
           TTM_TRACKACTIVATE = 1041, _
           TTM_TRACKPOSITION = 1042
    Static SM_CXDRAG  = 68, _
           SM_CYDRAG  = 69, _
           DragWidth  = GetSystemMetrics(SM_CXDRAG), _
           DragHeight = GetSystemMetrics(SM_CYDRAG), _
           StartX     = 0, _
           StartY     = 0
    Local x, y
    Static tx, ty, buf = Buffer(8)
    Switch uMsg
       Case WM_SHOWWINDOW
          If wParam = 0 Then DllCall("DestroyWindow", "handle", hWnd)
       Case WM_DESTROY
          If gDurationInfo Then
             gDurationBreak = true
             MsgBox("Операция отменена")
             Return 0
          EndIf
       Case WM_LBUTTONDOWN
          If IsDrag = 0 Then
             buf.Zero()
             x = BitAND(lParam, 0xFFFF)
             y = BitAND(BitShift(lParam,16), 0xFFFF)
             StartX = x
             StartY = y
             buf.SetNum(0, "int", x, "int", y)
             DllCall("ClientToScreen", "hwnd", hWnd, "ptr", buf.ptr)
             tx = buf.GetNum(0)
             ty = buf.GetNum(4)
             IsDrag = 1
             DllCall("SetCapture", "hwnd", hWnd, "hwnd")
          EndIf          
       Case WM_LBUTTONUP
          If IsDrag = 1 Then
             WinGetPos("gBTipX", "gBTipY", "", "", hWnd)
             IsDrag = 0
             DllCall("ReleaseCapture")
          Else   
             DllCall("DestroyWindow", "handle", hWnd)             
          EndIf  
       Case WM_MOUSEMOVE
          If IsDrag = 1 And BitAND(wParam, MK_LBUTTON) Then
             x = BitAND(lParam, 0xFFFF)
             y = BitAND(BitShift(lParam,16), 0xFFFF)
             If Abs(x - startX) > DragWidth Or Abs(y - startY) > DragHeight Then
                buf.Zero()             
                buf.SetNum(0, "int", x, "int", y)
                DllCall("ClientToScreen", "hwnd", hWnd, "ptr", buf.ptr)
                x = buf.GetNum(0)
                y = buf.GetNum(4)                 
                SendMessage(hWnd, TTM_TRACKPOSITION, 0, MakeInt(gBTipX+(x-tx), gBTipY+(y-ty), 0))
             EndIf   
          EndIf
       Case WM_RBUTTONUP
            BTipShowMenu(hWnd)
    EndSwitch
    Return DllCall("CallWindowProcW", _
                   "ptr", gBTipWndProc, "hwnd", hWnd, "uint", uMsg, "wparam", wParam, "lparam", lParam)
EndFunc

Func BTipShowMenu(hWnd)
    ShowPopupMenu /D /F /I:0 "BTipCreateMenu"
EndFunc

Func BTipCreateMenu()
    Local txt
    txt &= 'MENUITEM "Лог операций", em_aucmd ' & (gDurationTXT <> "" ?  "" : "/D") & ' -1 DurationText' & auCRLF
    Return txt
EndFunc

Func DurationText()
    Local sLogFile = TEMP & "\duration.txt"
    FileWrite(sLogFile, gDurationTXT)
    ShellExec(sLogFile)
EndFunc

Func MediaHint(FileName, FieldIndex, UnitIndex)
    If FieldIndex > gHintLines Then Return
    If StrPos(FileGetAttr(FileName), "D") Then Return "" 
    Local bCaps = DllCall("GetKeyState", "int", 0x14, "short")
    Local bCtrl = BitAND(bCaps, 1), bShift = IsPressed(0x10)

    If gHintWdx Then bShift = true    
    If Not bCtrl then bCtrl = IsPressed (0x11)
    If gHintCaps And Not bCtrl Then Return
    
    Static sLib = auX64 ? gMediaInfoX64 : gMediaInfoX32
    Static hLib = DllCall("LoadLibrary", "Wstr", sLib, "Ptr")
    
    Static sPath = FileGetDir(sLib) & "\"
    Static sLang ="file://" & sPath & "ru.csv", _
           sHint ="file://" & sPath & "hint.txt", _
           sSize ="file://" & sPath & "size.txt"
                   
    Static bLang = true, nWait = 0, _           
           pNew, pOpen, pDel, pOpt, pInfo
           
    If hLib = 0 And FieldIndex > 1 Then Return
    
    If FieldIndex = 1 Then

       If hLib = 0 Then Return "Error LoadLibrary " & sLib
       
       Idx = 0
       pNew = DllCall("GetProcAddress", "Ptr", hLib, "Str", "MediaInfo_New", "Ptr")
       pOpen = DllCall("GetProcAddress", "Ptr", hLib, "Str", "MediaInfo_Open", "Ptr")
       pDel = DllCall("GetProcAddress", "Ptr", hLib, "Str", "MediaInfo_Delete", "Ptr")
       pOpt = DllCall("GetProcAddress", "Ptr", hLib, "Str", "MediaInfo_Option", "Ptr")
       pInfo = DllCall("GetProcAddress", "Ptr", hLib, "Str", "MediaInfo_Inform", "Ptr")
       nWait = 0
       bLang = true
       sMode = "Inform"
       If gHintWait > 0 Then Sleep(gHintWait)
       
       Local x, y, w, nCase = 0
       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 gHintLayout[0]-1
                 nCase = 0
             Case gHintLayout[0] To gHintLayout[0]+gHintLayout[1]-1
                 nCase = 1             
             Case 100-gHintLayout[2] To 100
                 nCase = 2            
          EndSwitch
          nWait = gHintSleep[nCase]
          sMode = gHintContent[nCase]        
       EndIf
       
       If bShift Then
          If gHintWdxList.Count <= nCase Then 
             gHintPipe = "" & auCRLF & "Autorun: No data in " & gHintWdxCfg
             Return
          EndIf
          gHintPipe = ""
          Static aWdx = List()
          aWdx.Count = 0
          aWdx.Split(gHintWdxList[nCase], ";")
          For i = 0 To aWdx.Count - 1
              gHintPipe &= MediaHintWDX(aWdx[i], FileName)
          Next
          gHintPipe = StrTrim(gHintPipe)
          If gHintPipe = "" Then 
             gHintPipe = "" & auCRLF & "Autorun: No data in " & gHintWdxCfg
             Return
          EndIf   
          gHintPipe = "" & auCRLF & gHintPipe
          Return
       EndIf
             
       If nWait > 0 Then Sleep(nWait)
       If sMode = "" Then
          gHintPipe = ""
          Return
       ElseIf sMode <> "Inform" Then
          bLang = false
          sMode ="file://" & sPath & sMode
       EndIf
       
       Local hMI = DllCall(pNew, "Ptr")
       If hMI = 0 Then Return "MediaInfo.dll couldn't create new object"
              
       If DllCall(pOpen, 'Ptr', hMI, "Wstr", FileName, "Uint") <> 1 Then
          DllCall(pDel, "Ptr", hMI)        
          Return "MediaInfo.dll couldn't open file " &  FileName
       EndIf       
       
       If bShift Then 
          DllCall(pOpt, 'Ptr', hMI, "Wstr", "Complete", "Wstr", "1", "Wstr")
       Else
          DllCall(pOpt, 'Ptr', hMI, "Wstr", "Complete", "Wstr", "0", "Wstr")       
       EndIf
       
       DllCall(pOpt, "Ptr", hMI, "Wstr", "Output", "Wstr", sMode, "Wstr")              
       DllCall(pOpt, 'Ptr', hMI, "Wstr", "Language", "Wstr", (Not bLang ? "" : sLang), "Wstr")
       
       gHintPipe = DllCall(pInfo, 'Ptr', hMI, "Uint", 0, "Wstr")
       #{
       If StrPos(sMode, "hint.txt") Then
          gHintPipe = StrReplace(gHintPipe, "DTS HD Master Audio", "DTS-HD MA")
          gHintPipe = StrReplace(gHintPipe, "Dolby Digital", "DD")           
       EndIf       
       #}
       If Not bLang Then gHintPipe = StrReplace(gHintPipe, Chr(32), Chr(160))       
       
       DllCall(pOpt, 'Ptr', hMI, "Wstr", "Language", "Wstr", "", "Wstr")
       DllCall(pDel, "Ptr", hMI)
    Endif
    If Not bLang Then Return StrPart(gHintPipe, auCRLF, FieldIndex)
    Return FixSpaces(StrPart(gHintPipe, auCRLF, FieldIndex))
EndFunc

Func FixSpaces(String)
    Local a, b
    If Not StrPos(String, ":") Then Return String    
    a = StrPart(String, ":", 1)
    b = StrPart(String, ":", 2) & StrPart(String, ":", 3)    
    Return StrTrim(a) & ":  " & FixCodePage(StrTrim(b)) 
EndFunc

Func FixCodePage(String)
   Local bUsedRepl
   # iso-8859-1 = 28591
   DllCall("WideCharToMultiByte", _
           "uint", 28591, _
           "dword", 0, _
           "wstr", String, _
           "int", -1, _
           "ptr", 0, _
           "int", 0, _
           "ptr", 0, _
           "bool*", @bUsedRepl)
    If bUsedRepl Then Return String
    Local nSize = StrLen(String)
    Local buf = Buffer(nSize)
    buf.Zero()
    DllCall("WideCharToMultiByte", _
            "uint", 28591, _
            "dword", 0, _
            "wstr", String, _
            "int", -1, _
            "ptr", buf.ptr, _
            "int", nSize, _
            "ptr", 0, _
            "ptr", 0)
    Local fixed = buf.GetStr(0, nSize, "ANSI")       
    Free(buf)
    Return fixed
EndFunc

Func FixCodePageEx(String)
   Local bUsedRepl
   # iso-8859-1 = 28591
   DllCall("WideCharToMultiByte", _
           "uint", 28591, _
           "dword", 0, _
           "wstr", String, _
           "int", -1, _
           "ptr", 0, _
           "int", 0, _
           "ptr", 0, _
           "bool*", @bUsedRepl)
    If bUsedRepl Then Return String
    Local nSize = StrLen(String)
    Local buf = Buffer(nSize)
    buf.Zero()
    buf.SetStr(String, 0, nSize, "CP:28591")
    Local fixed = buf.GetStr(0, nSize, "ANSI")       
    Free(buf)
    Return fixed
EndFunc

Func ShowPipeEx(lParam, nPage)
    Local sPipe, nLines = gHintLines
    Local aPipe = List(), j, nSect = 0
    Local nEnd = nPage*nLines, nStart = nEnd - nLines
    If gHintClipb Then ClipPut(gHintPipe)    
    aPipe.Text = gHintPipe
    For j = 0 To aPipe.Count - 1
        If aPipe[j] = "" Then nSect += 1
        If (j + 1) >= nStart And (j + 1) <= nEnd Then 
           sPipe &= StrLeft(FixSpaces(aPipe[j]),84) & auCRLF
        EndIf
    Next
    Free(aPipe)
    If sPipe = "" Then sPipe = "Out of range"
    ShowHint(StrTrim(sPipe))
    #Return -1
EndFunc

Func LeftsideHintType()
    Static init = gHintContent[0]
    If gHintContent[0] = init Then    
       gHintContent[0] = gHintShift
    Else
       gHintContent[0] = init
    EndIf
    ShowRedInfoHint("Left side is " & gHintContent[0])
EndFunc

Func SetHintFields()
    Local i, sHint
    Static c = 0, aHints = List("MediaHint", "ExifHint", "JoinHint")
    c += 1
    If c > 2 Then c = 0
    sHint = aHints[c]
    For i = 1 to 60
        SetFieldsParam("Func", "C" & i, sHint)
    Next
    ShowRedInfoHint("Поля подсказки " & sHint)
EndFunc

Func ShowRedInfoHint(HintText)
    SetHintParam("ShowHint", "Font", 15, "Arial")
    SetHintParam("ShowHint", "BackColor", 0xFF0000)
    SetHintParam("ShowHint", "Text", 0xFFFFFF)
    ShowHint(HintText, 0, 0, 1000, 1)
    WinAlign(LAST_HINT_WINDOW)    
    Sleep(100)
    SetHintParam("ShowHint", "Reload") 
EndFunc

Func ExifHint(FileName, FieldIndex, UnitIndex)
    If FieldIndex > gHintLines Then Return
    If StrPos(FileGetAttr(FileName), "D") Then Return

    Local bCtrl = BitAND(DllCall("GetKeyState", "int", 0x14, "short"), 1)    
    If Not bCtrl then bCtrl = IsPressed (0x11)
    If gHintCaps And Not bCtrl Then Return
          
    If FieldIndex = 1 Then
       ProcessExecGetOutput gHintPipe %gMediaExifX32% ' -G -S -lang ru "%FileName%"'
    Endif     
    Return StrPart(gHintPipe, auLF, FieldIndex)
EndFunc

ReadWdxHintCfg()

Func ReadWdxHintCfg()
    gHintWdxList.Count = 0
    gHintWdxList.LoadFromFile(gHintWdxCfg)
EndFunc

Func MediaHintWDX(WdxName, Filename)
    If Not FileExist(Filename) Then 
       Return auCRLF & auCRLF & "Файл не существует " & Filename
    Endif
    Local obj = Plugin(WdxName)
    If ERROR <> 0 Then 
       Return auCRLF & auCRLF & "Autorun: " & WdxName & " plugin error " & ERROR    
    EndIf
    Local i, txt, data    
    obj.FileName = Filename    
    For i = 0 To obj.FieldCount -1
        data = obj.GetValue(i)       
        If data = "" Then Continue        
        txt &= obj.FieldName(i) & ":  " & data & auCRLF
    Next    
    Free(obj)    
    txt = StrTrim(txt)
    If StrLen(txt) > 0 Then txt = auCRLF & auCRLF & txt
    Return txt  
EndFunc

Func MediaHintType()
    # переключение переменной
    gHintWdx = Not gHintWdx
    ShowRedInfoHint("WdxHint.cfg " & (gHintWdx ? "включен" : "выключен"))
EndFunc
[/cut]