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

Добавлено: Вс янв 04, 2026 13:10
Orion9
FallenAngel
На комментарии просто времени не хватило, хотел сделать рабочий вариант, чтобы вы могли попробовать, и вроде даже успел )

А сам функционал был уже в модуле Title.aucfg, мне просто пришлось освежить немного память и взять кое-что оттуда. Там, конечно, всё сильно нагромождено переменными и раздутым телом цикла, но сам принцип тот же. Запуск заголовка происходит после считывания ключа ShowTitle, а выход из цикла при присвоении переменной gShowTitle значения false.

Есть менее громоздкая версия, состоящая всего из 200 строк. По сути это первая версия заголовка, где алгоритм предается более наглядно, и в нем нет ничего лишнего: только пинг, память и процессор. Если к этому добавить короткую дату и скорость передачи трафика, то и получится заголовок, которым я постоянно пользуюсь.

Вообще, считаю, что Гислер мог и сам сделать такой функционал — он точно не помешал бы и безусловно украсил бы его детище. Но к сожалению, лучшие годы маестро на танцполе уже позади, а включать мозг 8-летней девочки при подходе к проблемам пользователей больше не получается :lol:

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

На счет погоды. Может быть, город и не потребуется. Я сначала думал, что нет возможности указать координаты, но сейчас более внимательно посмотрел, оказыается есть в справке об этом. Соответственно, можно указывать в запросе нужные координаты, например:
https://wttr.in/55.75,37.62?format=3
Что эквивалентно:
https://wttr.in/Moscow?format=3
И наверное, так будет лучше, чем просто указание города или автоматическое определение местоположения по IP.

Но все-равно строку запроса пользователю придется формировать самостоятельно. Для этого нужно знать все ключи однострочного вывода, которые будут сохраняться в ini-файле и считываться оттуда при запуске заголовка.

Интервал обновления тоже будет сохраняться. В общем, завтра попробую что-то сделать. Может с первого наскока чего и получится )

ZiabrevLV
У вас всё почти есть. Нужно только считать ключ DriveBarHide для определения текущего состояния, и в зависимости от его значения, выполнить ту или иную функцию. То есть:

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

Local sDriveBarHide
IniRead sDriveBarHide %COMMANDER_INI% "Configuration" "DriveBarHide" ""
If StrPos(sDriveBarHide, "-") Then
   IniWrite %COMMANDER_INI% "Configuration" "DriveBarHide" ""    
Else
   IniWrite %COMMANDER_INI% "Configuration" "DriveBarHide" "-"
EndIf 
CommandExec cm_VisTwoDriveButtons 2
CommandExec cm_VisTwoDriveButtons 1
Только я не проверял работоспособность, но кажется здесь нужно быть осторожным, т.к. ключ DriveBarHide может содержать подстроки, а значит их тоже нужно будет сохранять.

[cut]

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

[Configuration]
DriveBarHide=drive_list

Wincmd.ini Version: 7.03

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

    A..Z: кнопки с буквами дисков;
    0..9: кнопки с активными FTP-подключениями;
    \ (обратный слэш): кнопка "Сеть/FS-плагины" для открытия виртуальной папки 'Сеть';
    . (точка): пара кнопок для перехода в корневой и родительский каталог;
    - (чёрточка): метка текущего тома и сведения о дисковом пространстве;
    ; (точка с запятой): кнопки для USB-устройств без буквы диска (например, смартфонов).

Символы записываются подряд без разделителей, регистр букв значения не имеет. Пример: A\ (скрытие кнопок для дисковода А: и для сетевого окружения).

По умолчанию значение не задано (ничего не скрывается).

ПРИМЕЧАНИЯ:

В отличие от ключа Allowed, скрытые в этой панели диски остаются доступными в их выпадающем списке (ALT+F1/F2), в цепочках навигации, в дереве каталогов, при навигации с помощью команды "cd" или встроенных команд TC наподобие cm_GoToPreviousDrive/cm_GoToNextDrive. Эффект применения команды cm_OpenDriveByIndex (для переключения на диск с заданным в качестве параметра порядковым номером) зависит от того, отображается ли вообще панель дисков (если да, порядковый отсчёт идёт по ней с игнорированием скрытых кнопок, если нет — по всем пунктам списка дисков).

Метка текущего тома, сведения о дисковом пространстве и кнопки перехода в корневой/родительский каталог добавляются в панель дисков после основных кнопок ТОЛЬКО при отсутствии выпадающего меню дисков (DriveCombo=0), в противном случае они отображаются в строке с этим списком. Значения "." (точка) и "-" (чёрточка, символ дефиса/минуса) учитываются в ключе DriveBarHide ТОЛЬКО при размещении соответствующих элементов основного окна TC в одной панели с кнопками дисков.

Для скрытия дисков клиентского компьютера на терминальном сервере они перечисляются после символа доллара, а в сочетании с другими элементами указываются в последнюю очередь. Например, запись DriveBarHide=A$CDE означает скрытие кнопок для дисковода A: и для клиентских дисков C:, D:, E:.

Следует иметь в виду, что хотя кнопка "Сеть/FS-плагины" непосредственно открывает только список сетевых элементов и/или FS-плагинов, она отображается в панели дисков нажатой и при открытии в файловой панели TC ряда других виртуальных папок ('Рабочий стол', 'Мой компьютер', 'Корзина', 'Принтеры').
[/cut]
Т.е. возможно так будет правильнее:

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

RegisterCommand 63003 "SwitchShowHide"

Func SwitchShowHide(lParam)
    Local sDriveBarHide, sValue
    IniRead sDriveBarHide %COMMANDER_INI% "Configuration" "DriveBarHide" ""
    If StrPos(sDriveBarHide, "-") Then
       # удаление "-" из значения ключа
       sValue = StrReplace(sDriveBarHide, "-", "")
       IniWrite %COMMANDER_INI% "Configuration" "DriveBarHide" sValue
    Else
       # добавление "-" к значению ключа
       sValue = sDriveBarHide & "-"
       IniWrite %COMMANDER_INI% "Configuration" "DriveBarHide" sValue
    EndIf 
    CommandExec cm_VisTwoDriveButtons 2
    CommandExec cm_VisTwoDriveButtons 1
EndFunc

Добавлено: Вс янв 04, 2026 16:35
ZiabrevLV
Orion9
Да, первый вариант отлично работает.
Спасибо.

Добавлено: Пн янв 05, 2026 23:38
Orion9
FallenAngel
Мда. Как всегда легко и просто только на бумаге, а берешься делать сразу вслывает куча нюансов. С одними проверками загребешься: тут надо учесть, там надо учесть. Но все-таки доделал, раз уж взялся, тем более что кому-то тоже может пригодится.

Небольшой подвох с сервером: он очень плохо откликается, иногда погода вообще может не обновиться по таймауту. Пришлось немного по-другому сделать отображение процесса обновления, что по сути и отняло больше всего времени.

При наведении на заголовок всплывает посказка. Alt - добавляет информацию о файлах. Ctrl - отображает подсказку о погоде и прогнозе из дополнительно указанной ссылки. Правый клик вызывает меню.

Настройки должны храниться в файле Header.ini в подкаталоге Ini. Т.е. %COMMANDER_INI%\Ini\Header.ini

[cut][Title]
ShowTitle=1
Wttr=1

[Wttr]
Name=Zurich
Interval=3600
Url=https://wttr.in/47.37,8.55?format=%C+%t
Hint=https://wttr.in/47.37,8.55?T

; Examples:
; wttr.in/London?format=3
; wttr.in/London?format="%l:+%c+%t\n"

; Refer to the link below for the details:
; https://github.com/chubin/wttr.in?tab=r ... ine-output
;
; c Weather condition,
; C Weather condition textual name,
; x Weather condition, plain-text symbol,
; h Humidity,
; t Temperature (Actual),
; f Temperature (Feels Like),
; w Wind,
; l Location,
; m Moon phase,
; M Moon day,
; p Precipitation (mm/3 hours),
; P Pressure (hPa),
; u UV index (1-12),
;
; D Dawn*,
; S Sunrise*,
; z Zenith*,
; s Sunset*,
; d Dusk*,
; T Current time*,
; Z Local timezone.
;
;(*times are shown in the local timezone)[/cut]
Поскольку все эти "города, координаты и прочее" довольно приватная вещь, есть возможность сохранить информацию в дополнительном файле пользователя Wttr.ini, который расположен по умолчанию в LocalAppData\GHISLER\Wttr.ini.

Соответственно, если файл пользователя существует, настройки прежде всего будут считываться с него, кроме двух ключей секции [Title], т.е. ShowTitle и Wttr. Последний отключает погоду в заголоке при Wttr=0

[cut="Пример Wttr.ini"][Wttr]
Name=
Interval=1800
Url=https://wttr.in/47.37,8.55?format=%t
Hint=https://wttr.in/47.37,8.55?T[/cut]

Строку запроса можно настроить по-разному, все зависит от ключей сайта wttr.in.

Ошибки, конечно, могут быть: все сделать и протестировать за один день невозможно. Но радует, что кроме самого Autorun с последним модулем RegExp, для работы больше ничего не требуется.

[cut="InfoHeader.aucfg"]

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

Sleep(200)

# наведение на заголовок
ControlSetHint /F /D:350 /H:2 0 "InfoHeaderHint"

# правый клик
ControlSetMouseAction /R /H:2 0 "InfoHeaderMenu"

# Shift + клик по заголовку
ControlSetMouseAction /L /K:S /H:2 0 "ToggleInfoHeader"

Global gTitle = WinGetText(), gInfoHeader = gTitle, gShowTitle = 1, gTitleThread = 0

Global gWttr = 1, gWttrUrl, gWttrHint, gWttrName, gWttrInterval = 3600
Global gWttrSec = 0, gWttrData, gWttrTip = "", gWttrFile = TEMP & "\wttr.txt", gWttrDataUpdate = 0

Global gWttrIni = COMMANDER_PATH & "\Ini\Header.ini"
Global gWttrIniApp = GetKnownFolderPath("LocalAppData") & "\GHISLER\Wttr.ini"

# запуск
LaunchInfoHeader()

# WM_WINDOWPOSCHANGING
# реагирование заголовка на изменение позиции окна
SetMessageAction 70 "SetInfoHeader"

Func ToggleInfoHeader()
    # переключение переменной
    gShowTitle = Not gShowTitle
    # запись нового значения в Header.ini
    IniWrite %gWttrIni% "Title" "ShowTitle" %"gShowTitle"    
    Sleep(100)
    LaunchInfoHeader()
EndFunc
 
Func SetInfoHeader()
    WinSetText(gShowTitle ? gInfoHeader : gTitle)
EndFunc

Func LaunchInfoHeader()
    # чтение ключа
    IniRead gShowTitle %gWttrIni% "Title" "ShowTitle" 1
    # запуск заголовка, если он еще не запущен
    If gShowTitle And Not gTitleThread Then
       gInfoHeader = gTitle & '        ' & "Booting..."
       WinSetText(gInfoHeader)
       # подготовка конфигурации
       InfoHeaderReadIni()       
       Sleep(200)
       RunThread InfoHeader
    EndIf   
EndFunc

Func InfoHeaderReadIni()
    Local sWttrIni = gWttrIni, sUrl
    # чтение из Header.ini
    IniRead gWttr         %gWttrIni%   "Title"  "Wttr" 1
    IniRead sUrl          %sWttrIni%   "Wttr"   "Url" ""    
    # чтение из AppData\Wttr.ini
    If FileExist(gWttrIniApp) Then
       sWttrIni = gWttrIniApp
    EndIf
    IniRead gWttrUrl      %sWttrIni%   "Wttr"  "Url" ""
    IniRead gWttrHint     %sWttrIni%   "Wttr"  "Hint" ""    
    IniRead gWttrName     %sWttrIni%   "Wttr"  "Name" ""
    IniRead gWttrInterval %sWttrIni%   "Wttr"  "Interval" 3600         
    # нет данных в AppData
    # откат к значению из Header.ini
    If gWttrUrl = "" Then gWttrUrl = sUrl
    # проверка корректности значения интервала
    If Not IsInt(gWttrInterval) Then gWttrInterval = 0    
    if gWttrInterval < 900 Then gWttrInterval = 900
    # по умолчанию интервал считается законченным
    gWttrSec = gWttrInterval
    # проверка существования локального файла
    If FileExist(gWttrFile) Then
       # количество секунд с последнего обновления
       gWttrSec = Round((Now() - FileGetTime(gWttrFile)) / 10000000)
       # прошло меньше секунд, чем отведенный интервал
       If gWttrSec < gWttrInterval Then gWttrData = StrTrim(FileRead(gWttrFile))
       If gWttrName <> "" Then gWttrData = gWttrName & " " & gWttrData
    EndIf       
EndFunc

Func InfoHeader
   Local nRecvSpeed, nSendSpeed, T1, lst = List(" | "," / ","—"," \ ")
   Static c = 0
   # поток запущен
   gTitleThread = 1
   # выполнять пока gShowTitle = true
   While gShowTitle
      T1 = GetUptime()
      # погода включена и требуется обновление
      If gWttr And gWttrSec >= gWttrInterval Then 
         # сброс счетчика
         gWttrSec = 0
         # запуск обновления в отдельном потоке 
         RunThread "GetWttrData"
      EndIf
      GetNetSpeed(nRecvSpeed, nSendSpeed)
      # идет процесс обновления
      If gWttrDataUpdate Then
         c += 1
         If c > lst.Count Then c = 1
         #gWttrData = Weather updating: " & lst[c-1]     
         gWttrData = "Weather updating: " & gWttrSec & ' sec.'
      EndIf
      gInfoHeader = gTitle & '        ' & (gWttr ? gWttrData & '    ' : '') & Date("d MMMM, ddd") & '.    CPU: ' & StrFormat("%02d", GetCPUUsage()) & '%    RAM: ' & GetMemStats() & '%    NET: ↓ ' & Round(nRecvSpeed / 1000, 1) & '  ↑ ' & Round(nSendSpeed / 1000, 1) & ' mb/s'
      WinSetText(gInfoHeader)
      # разбитие секунды на более мелкие интервалы
      # для лучшего отклика при изменении переменной
      While GetUptime() < T1 + 1000
          If Not gShowTitle Then Break
          Sleep(50)
      Wend
      # увеличение счетчика до следующего обновления погоды
      gWttrSec += 1 
   Wend
   Free(lst)
   # поток остановлен   
   gTitleThread = 0
   # обновление заголовка
   SetInfoHeader()
EndFunc

Func GetWttrData()
    Local msg, sData
    # флаг операции
    gWttrDataUpdate = true
    gWttrTip = "Соединение..."
    # загрузка файла в тихом режиме
    Local code = WinInetDownloadFile(gWttrUrl, gWttrFile, 1)
    # снятие флага операции для заголовка
    gWttrDataUpdate = false    
    If code = -1 Then
       msg = "WinInet Error: " & code
    ElseIf code > 0 Then
       msg = "WinInet Error: " & code
       gWttrTip = msg & auCRLF & GetWinInetError(code)
    EndIf
    If code = 0 Then
       gWttrTip = "Обновлено"
       msg = FileRead(gWttrFile)
       If ERROR = 1 Then msg = "FileRead Error: "
    EndIf
    sData = StrTrim(msg)
    gWttrData = sData
    # добавление имени местоположения если указано
    If code = 0 And gWttrName <> "" Then gWttrData = gWttrName & " " & sData
    If code = 0 Then
       gWttrSec = 0
    Else
       # повтор через 30 секунд
       gWttrSec = gWttrInterval - 30
    EndIf       
    # загрузка данных для подсказки в тихом режиме
    code = WinInetDownloadFile(gWttrHint, TEMP & "\wttr.html", 1)
    If code <> 0 Then
       msg = "WinInet Error: " & code
       gWttrTip &= auCRLF & "Хинт: " &  msg & auCRLF & GetWinInetError(code)     
    EndIf
EndFunc

Func InfoHeaderHint()
    Local txt, file = TEMP & "\wttr.html"
    If Not IsPressed (0x11) Then
       If Not gShowTitle Then Return "Заголовок выключен"
       txt &= "Работает" & auCRLF
       txt &= "Погода " & (gWttr? "включена" : "отключена") & auCRLF
       txt &= "Интервал: " & gWttrInterval & " сек." & auCRLF                     
       txt &= "Обновление через: " & gWttrInterval - gWttrSec & " сек." & auCRLF       
       If gWttrTip <> "" Then txt &= "Статус: " & gWttrTip & auCRLF
       If IsPressed (0x12) Then
          txt &= "Файлы конфигурации:" & auCRLF
          txt &= gWttrIni & auCRLF
          txt &= gWttrIniApp & auCRLF
          txt &= "Временные файлы:" & auCRLF
          txt &= gWttrFile & auCRLF
          txt &= file & auCRLF
       EndIf   
       Return StrTrim(txt)
    Endif
    txt = FileRead(file)
    If ERROR = 1 Then Return "Ошибка чтения " & file
    # стиль подсказки
    SetHintParam("csh", "Font", 10, "Consolas")
    SetHintParam("csh", "BackColor", 0x001C1C1C)
    SetHintParam("csh", "Text", 0xF9F9F9)  
    # извлечение текста из html
    txt = StrReplace(txt, '"', '"')
    Static lst = List()
    lst.Count = 0
    lst.Text = RegExpGet(txt, '<pre>(.*?)</pre>', "$1")
    For i = 0 To lst.Count - 1
        If StrLen(lst[i]) > 125 Then lst[i] = StrLeft(lst[i], 122) & ".."
    Next
    # вернуть предыдущий стиль через 1 сек.        
    RunThread InfoHeaderHintReload 
    Return lst.Text
EndFunc

Func InfoHeaderHintReload()
    Sleep(1000)
    SetHintParam("sh", "Reload")
    SetHintParam("csh", "Reload")    
EndFunc

Func InfoHeaderMenu()
     ShowPopupMenu /D /F "InfoHeaderCreateMenu"
EndFunc

Func InfoHeaderCreateMenu()
    Local txt 
    txt &= 'MENUITEM "Включить", em_aucmd "%COMMANDER_EXE%,37" InfoHeaderStart' & auCRLF
    txt &= 'MENUITEM "Выключить", em_aucmd "%COMMANDER_EXE%,36" InfoHeaderStop' & auCRLF 
    txt &= 'MENUITEM "Обновить погоду", em_aucmd "shell32.dll,13" InfoHeaderUpdate' & auCRLF
    txt &= 'MENUITEM SEPARATOR' & auCRLF     
    txt &= 'MENUITEM "Настройки...",  em_aucmd -1 InfoHeaderEditIni' & auCRLF
    txt &= 'MENUITEM "Пользователь...",  em_aucmd -1 InfoHeaderEditIniApp' & auCRLF    
    txt &= 'MENUITEM "Даты и время",  em_aucmd "timedate.cpl,0" ShellExec rundll32.exe "shell32.dll,Control_RunDLL timedate.cpl"' & auCRLF    
    txt &= 'MENUITEM SEPARATOR' & auCRLF
    txt &= 'MENUITEM "Перейти к Wttr.ini", em_aucmd "WCMICONS.DLL,19" InfoHeaderGotoIniApp' & auCRLF
    txt &= 'MENUITEM "Перейти к Wttr.txt", em_aucmd -1 InfoHeaderGotoTemp "Wttr.txt"' & auCRLF
    txt &= 'MENUITEM "Перейти к Wttr.html", em_aucmd -1 InfoHeaderGotoTemp "Wttr.html"' & auCRLF
    txt &= 'MENUITEM SEPARATOR' & auCRLF    
    txt &= 'MENUITEM "Перейти к Header.ini", em_aucmd "WCMICONS.DLL,19" InfoHeaderGotoIni' & auCRLF    
    Return txt
EndFunc

Func InfoHeaderStart()
    # запись значения в Header.ini
    IniWrite %gWttrIni% "Title" "ShowTitle" 1   
    Sleep(100)
    LaunchInfoHeader()
EndFunc

Func InfoHeaderStop()
    IniWrite %gWttrIni% "Title" "ShowTitle" 0
    Sleep(100)
    LaunchInfoHeader()
EndFunc

Func InfoHeaderUpdate()
    # обновление через 1 сек.
    gWttrSec = gWttrInterval - 1
EndFunc

Func InfoHeaderEditIni()
    If FileExist(gWttrIni) Then
       ShellExec(gWttrIni)
    Else
       MsgBox("Файл не существует " & gWttrIni, "Autorun", 16)
    EndIf   
EndFunc

Func InfoHeaderGotoIni()
    CommandExec("~/CD:S", gWttrIni)
EndFunc

Func InfoHeaderEditIniApp()
    If FileExist(gWttrIniApp) Then
       ShellExec(gWttrIniApp)
    Else
       MsgBox("Файл не существует " & gWttrIniApp, "Autorun", 16)
    EndIf   
EndFunc

Func InfoHeaderGotoIniApp()
    CommandExec("~/CD:S", gWttrIniApp)
EndFunc

Func InfoHeaderGotoTemp(Filename)
    Local sFile = TEMP & "\" & Filename
    If FileExist(sFile) Then
       CommandExec("~/CD:S", sFile)
    Else
       MsgBox("Файл не существует " & sFile, "Autorun", 16)
    EndIf   
EndFunc

Func GetNetSpeed(ByRef nRecvSpeed, ByRef nSendSpeed)
    Static nIntfIdx = GetNetInterface()
    If nIntfIdx = -1 Then Return
 
    Static nPrevRecv = GetNetInterfaceInfo(nIntfIdx, 'Recv')
    Static nPrevSend = GetNetInterfaceInfo(nIntfIdx, 'Sent')
    Static nPrevTime = GetUptime()
 
    Local nRecv = GetNetInterfaceInfo(nIntfIdx, 'Recv')
    Local nSend = GetNetInterfaceInfo(nIntfIdx, 'Sent')
    Local nTime = GetUptime()
    Local nTimeDiff = nTime - nPrevTime
 
    nRecvSpeed = Round(Abs(nRecv - nPrevRecv) / nTimeDiff)
    nSendSpeed = Round(Abs(nSend - nPrevSend) / nTimeDiff)
 
    nPrevRecv = nRecv
    nPrevSend = nSend
    nPrevTime = nTime
EndFunc
[/cut]
Нужны только еще две дополнительные функции, которые можно найти 1-2 страницы назад, но я лучше их еще раз приложу.

[cut]

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

Func WinInetDownloadFile(URL, FileName, Silent = 0, Style = 0)
    Static GENERIC_WRITE = 0x40000000, _
           CREATE_ALWAYS = 2, FILE_ATTRIBUTE_NORMAL = 128
    Static INTERNET_OPEN_TYPE_PRECONFIG = 0, _
           INTERNET_FLAG_RELOAD = 0x80000000, _
           INTERNET_FLAG_NO_CACHE_WRITE = 0x04000000
    # Static INVALID_HANDLE_VALUE = auX64 ? 0xFFFFFFFFFFFFFFFF : 0xFFFFFFFF
    Static INVALID_HANDLE_VALUE = auX64 ? 18446744073709551615 : 4294967295
    Local hInt, hUrl, nSysErr, sAgent = "Autorun Downloader"
    If Not Silent Then
       tip("Internet connection.", 1, "Download", Style)
       Sleep(200)
    EndIf   
    hInt = DllCall("Wininet.dll\InternetOpenW", _
                   "wstr", sAgent, "dword", INTERNET_OPEN_TYPE_PRECONFIG, _
                   "ptr", 0, "ptr", 0, "ptr", 0, "handle")
    nSysErr = SYSERROR
    If Not (hInt > 0) Then
       If Not Silent Then          
          tip("Error: " & nSysErr & auCRLF & _
              "Message: " & GetWinInetError(nSysErr) & auCRLF & URL, 3, "Download", Style)
       EndIf       
       Return nSysErr
    EndIf
    hUrl = DllCall("Wininet.dll\InternetOpenUrlW", _
                   "handle", hInt, "wstr", URL, "ptr", 0, _
                   "dword", 0, "dword", INTERNET_FLAG_RELOAD, _
                   "dword_ptr", 0, "handle")
    nSysErr = SYSERROR    
    If Not (hUrl > 0) Then
       DllCall("Wininet.dll\InternetCloseHandle", "handle", hInt)
       If Not Silent Then
          tip("Error: " & nSysErr & auCRLF & _
              "Message: " & GetWinInetError(nSysErr) & auCRLF & URL, 3, "Download", Style)
       EndIf       
       Return nSysErr
    EndIf
    # HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER
    Local flags = BitOR(5, 0x20000000)
    Local size, sz = 8
    Local buf = Buffer(sz)
    buf.Zero()
    If DllCall("Wininet.dll\HttpQueryInfoW", _
                "handle", hUrl, "dword", flags, "ptr", buf.ptr, "dword*", @sz, "ptr", 0) Then
       size = buf.GetNum(0, "int64")
    EndIf  
    Free(buf)
    hFile = DllCall("CreateFileW", _
                    "wstr", FileName, "dword", GENERIC_WRITE, _
                    "dword", 0, "ptr", 0, "dword", CREATE_ALWAYS, _
                    "dword", FILE_ATTRIBUTE_NORMAL, "ptr", 0, "handle")
    nSysErr = SYSERROR
    If hFile <= 0 Or hFile = INVALID_HANDLE_VALUE Then
       DllCall("Wininet.dll\InternetCloseHandle", "handle", hUrl)
       DllCall("Wininet.dll\InternetCloseHandle", "handle", hInt)
       If Not Silent Then
          tip("File can't be created. Error: " & nSysErr & auCRLF & _
              GetWinInetError(nSysErr) & auCRLF & FileName, 3, "Download", Style)
       EndIf   
       Return nSysErr
    EndIf
    Local buf = Buffer(1024*2), bytes, written, total, prc
    buf.Zero()
    Local T1 = GetUptime(), T2 = T1, T3, nStyle = BitOR(Style, 1)
    Do
       bRes = DllCall("Wininet.dll\InternetReadFile", _
              "handle", hUrl, "ptr", buf.ptr, "dword", buf.size, "dword*", @bytes)
       If bRes And bytes > 0 Then
          DllCall("WriteFile", "handle", hFile, _
                  "ptr", buf.ptr, "dword", bytes, "dword*", @written, "ptr", 0)
          total += bytes
          If Not Silent And size > buf.size Then
              T3 = GetUptime()
              If Round(T3 - T2, 0) > 500 Then 
                 prc = "Download " & Round(total/size*100,0) & '%'
                 tip('Загружено: ' & SizeFormat(total, 1, 'M', 2) & ' ' & _
                     'из ' & SizeFormat(size, 1, 'M', 2), 1, prc, nStyle)
                 Sleep(10)
                 T2 = T3
              EndIf
          EndIf   
       EndIf
       If IsPressed(0x1B) Then bRes = false
    Until Not (bRes And bytes > 0)
    DllCall("CloseHandle", "handle", hFile)
    DllCall("Wininet.dll\InternetCloseHandle", "handle", hUrl)
    DllCall("Wininet.dll\InternetCloseHandle", "handle", hInt)
    T3 = Round(GetUptime() - T1, 0) / 1000
    T3 = "Время операции: " &  StrFormat("%.3f", T3) & " sec"
    If Not Silent Then
       If bRes Then
          txt = "Download successful." & auCRLF & FileName & auCRLF & T3
          tip(txt, 1, "Download", nStyle)          
       Else
          txt = "Download failed or ended with an error." & auCRLF & T3
          tip(txt, 3, "Download", nStyle)          
       EndIf
    EndIf   
    Return (bRes ? 0 : -1)
EndFunc

Func GetWinInetError(nCode)
    Local sMsg
    Local hModule = DllCall("GetModuleHandleW", "Wstr", "wininet.dll", "handle")
    If Not (hModule > 0) Then 
       Return "GetModuleHandle failed for wininet.dll. Error: " & SYSERROR
    EndIf   
    Local FORMAT_MESSAGE_FROM_HMODULE = 0x00000800
    Local FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100
    Local FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200
    Local FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000
    Local flags = BitOR(FORMAT_MESSAGE_FROM_HMODULE, _
                        FORMAT_MESSAGE_IGNORE_INSERTS, _
                        FORMAT_MESSAGE_FROM_SYSTEM)
    Local buf = Buffer(1024)
    buf.Zero()
    If DllCall("FormatMessageW", "dword", flags, "ptr", hModule, _
            "dword", nCode, "dword", 0, "ptr", buf.ptr, "dword", buf.size, "ptr", 0) Then
       sMsg = StrTrim(buf.GetStr())
    Else
       sMsg = "Failed to find error description"
    EndIf        
    Free(buf)
    Return sMsg
EndFunc

Global gTipText, gTipX, gTipY, gTipMenuItems
Global gTipWndProc, gTipWP = Callback("tipwp", "hwnd;uint;wparam;lparam")
#{
    nFlags:
       1 - Previous window position
       2 - Square tooltip window
       4 - Inactive tooltip window
       0 - Default 
#}
Func tip(sText, nIcon = 0, sTitle = "Tip", nFlags = 0, sMenuItems = "")
    Static TTM_TRACKACTIVATE   = 1041, _
           TTM_TRACKPOSITION   = 1042, _
           TTM_SETMAXTIPWIDTH  = 1048, _
           TTM_SETTITLE        = 1057, _
           TTM_ADDTOOL         = 1074, _
           TTM_UPDATETIPTEXT   = 1081
    Static txt = Buffer(1024*2)
    Static hTip = 0, buf = Buffer(auX64 ? 56 : 40)
    Local nMaxWidth = 200
    If hTip = 0 Then
       # TTS_BALLOON | TTS_CLOSE | TTS_NOPREFIX
       hTip = DllCall("CreateWindowExW", _
              "dword", 8, _
              "wstr", "tooltips_class32", _
              "wstr", "", "dword", 0x00000c2, _
              "int", 0, "int", 0, "int", 0, "int", 0, _
              "handle", 0, _
              "handle", 0, "handle", 0, "ptr", 0, "handle")
       If hTip = 0 Then
          MsgBox("Не удалось создать окно подсказки")
          Return 0
       EndIf
       gTipWndProc = DllCall("SetWindowLong" & (auX64 ? "PtrW" : "W"), _
                     "hwnd", hTip, "int", -4, "long_ptr", gTipWP.Ptr, "ptr")       
       txt.Zero()
       txt.SetStr(sTitle & Chr(0))
       SendMessage(hTip, TTM_SETTITLE, nIcon, txt.ptr)
       buf.Zero()
       # TTF_TRACK | TTF_ABSOLUTE | TTF_PARSELINKS
       buf.SetNum(0, "uint", buf.size, "uint", 0x10a0, "hwnd", hTip)
       buf.SetNum(auX64 ? 48 : 36, "ptr", txt.ptr)
       SendMessage(hTip, TTM_ADDTOOL, 0, buf.ptr)  
       SendMessage(hTip, TTM_SETMAXTIPWIDTH, 0, nMaxWidth)
    EndIf
    If sText = "" Then
       SendMessage(hTip, TTM_TRACKACTIVATE, 0, buf.ptr)
       Return hTip
    EndIf
    # additional menu items
    gTipMenuItems = sMenuItems
    # cartoon or square 
    Local op = (BitAND(nFlags, 2) ? 4 : 2)
    WinSetStyle(0x0000040, op, hTip)
    txt.Zero()
    txt.SetStr(sTitle & Chr(0))
    SendMessage(hTip, TTM_SETTITLE, nIcon, txt.ptr) 
    txt.Zero()
    txt.SetStr(sText & Chr(0))
    # the next call won't fail if the tip is not visible
    If WinGetState(2, hTip) = 0 Then
       SendMessage(hTip, TTM_TRACKACTIVATE, 0, buf.ptr)
    EndIf
    SendMessage(hTip, TTM_UPDATETIPTEXT, 0, buf.ptr)
    SendMessage(hTip, TTM_TRACKACTIVATE, 1, buf.ptr)
    WinGetPos("x", "y", "w", "h", hTip)    
    # the tip will appear at the mouse pointer by default
    If Not BitAND(nFlags, 1) Then MouseGetPos("x","y")
    # preventing the tip from going off screen
    If x + w > SYSINFO_DESKTOPWIDTH Then x = SYSINFO_DESKTOPWIDTH - w
    If y + h > SYSINFO_DESKTOPHEIGHT Then y = SYSINFO_DESKTOPHEIGHT - h    
    SendMessage(hTip, TTM_TRACKPOSITION, 0, MakeInt(x, y, 0))
    gTipX = x
    gTipY = y
    If Not BitAND(nFlags, 4) Then WinSetState(23, hTip)
    Return hTip 
EndFunc

Func tipwp(hWnd, uMsg, wParam, lParam)
    Static MK_LBUTTON = 0x0001, TTM_TRACKPOSITION = 1042
    Static DragWidth  = GetSystemMetrics(68)  #SM_CXDRAG
    Static DragHeight = GetSystemMetrics(69)  #SM_CYDRAG
    Static IsDrag = 0, StartX = 0, StartY = 0
    Local x, y
    Static tx, ty, buf = Buffer(8)
    #OutputDebugString("uMsg " & uMsg)
    Switch uMsg
       Case 0x004E  # WM_NOTIFY
          Static tag = 16    + _
                       48*2  + _
                       (2048 + 32 + 3)*2
          hdr = Buffer(tag + (auX64 ? 24 : 12), lParam)
          code = hdr.GetNum(auX64 ? 16 : 8, "uint")
          If code = 0xFFFFFDF5 Then            
              link = hdr.GetStr(auX64 ? 136 : 124)              
              If StrPos(link, "://") Then
                 # MsgBox("Open the link?" & auCRLF & auCRLF & link, "Autorun", 3+32+0+262144)
                 # If EXTENDED <> 6 Then Return                         
                 ShellExec(link)
              Else   
                 Eval(link)
              EndIf
           EndIf
       Case 0x0018  # WM_SHOWWINDOW
           If wParam = 0 Then gTipMenuItems = ""
       Case 0x0201  # 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 0x0202  # WM_LBUTTONUP
          If IsDrag = 1 Then
             WinGetPos("gTipX", "gTipY", "", "", hWnd)
             IsDrag = 0
             DllCall("ReleaseCapture")
          EndIf  
       Case 0x0200  # 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(gTipX+(x-tx), gTipY+(y-ty), 0))
             EndIf   
          EndIf
       Case 0x0205  # WM_RBUTTONUP
           gTipText = WinGetText(hWnd)
           ShowPopupMenu /D /F /I:0 "tipmnu"
       Case 0x0100  # WM_KEYDOWN
           If wParam = 0x1B Then WinSetState(0, hWnd)
    EndSwitch
    Return DllCall("CallWindowProcW", _
                   "ptr", gTipWndProc, "hwnd", hWnd, "uint", uMsg, "wparam", wParam, "lparam", lParam)
EndFunc

Func tipmnu()
    Local txt
    txt &= 'MENUITEM "Copy text", em_aucmd ' & (gTipText <> "" ?  "" : "/D") & ' -1 tipcopy' & auCRLF
    If gTipMenuItems <> "" Then
       txt &= 'MENUITEM SEPARATOR' & auCRLF
       txt &= gTipMenuItems        
    EndIf
    Return txt
EndFunc

Func tipcopy()
    #ClipPut %"gTipText"
    ClipPut(gTipText)
EndFunc
[/cut]
P.S.
Заголовок мне понравился, очень понравился. Даже завидую тем, кто первый раз его подключит )

Добавлено: Ср янв 07, 2026 10:59
FallenAngel
Orion9
Работает. Но я не разобрался как менять город.

Я думал, что достаточно в Wttr.ini (или Header.ini при отсутствии) указать свой город в Name=
...но ничего не поменялось.

Так же, Name должен отображаться и в заголовке. Вообще по выбору города - нужно упростить, сделать более юзер френдли. К примеру, в ПКМ меню добавить простую форму ввода города.


UPD. Поигрался с Url - но тоже безрезультатно.

Вообще в InfoHeader.aucfg сильно не хватает "шапки". Нужно написать версия, автор и т.д.
"Данный файл нужно подключать к плагину Autorun.wdx след. способом...
Настройка:
п1.
п2.
п3.
...

Обратная связь: ссылка на форум, почту, гитхаб
"

Добавлено: Ср янв 07, 2026 23:31
Orion9
FallenAngel писал(а):Работает.
Это главное.

А подсказка с прогнозом на три дня у вас тоже работает? Cервер хорошо откликается?
FallenAngel писал(а):Но я не разобрался как менять город.
В принципе город и координаты можно не указывать. Сервис попытается установить местоположение по ip, если в запросе не будет этих данных, например:
https://wttr.in/?T
https://wttr.in/?format=%l:+%C+%t

Первую ссылку можно добавить в "Hint" в качестве подсказки, а вторую использовать для однострочного вывода в заголовок.

Предполагается, что пользователь сам создаст и сохранит ссылку, которая ему нужна (температура, давление, влажность, ветер и т.д.) в Header.ini или Wttr.ini. Ключ "Name" нужен для вывода произвольного текста в заголовок, к примеру, если вы хотите вывести в заголовок не имя города, а что-то другое, типа "Погода:". Если ключ пуст, дополнительный текст не появится.
FallenAngel писал(а):Вообще по выбору города - нужно упростить, сделать более юзер френдли. К примеру, в ПКМ меню добавить простую форму ввода города.
Это не реалистично сделать на Autorun. Даже при помощи специального объекта, упрощающего работу с GUI (если таковой бы имелся) и наличия базы данных городов (если таковая имелась бы), пришлось бы изрядно отвлечься на работу с этой базой, что далеко выходило бы за рамки простого скрипта. Да и зачем оно? Ведь город можно явно указать в запросе, или использовать более точные координаты для определения местоположения.
FallenAngel писал(а):Вообще в InfoHeader.aucfg сильно не хватает "шапки"
Если скрипт пройдет проверку временем, можно будет над этим подумать. Да, нужно добавить туда хотя бы версию Autorun, которая требуется для его работы, с этим я согласен.

Добавлено: Чт янв 08, 2026 12:49
Orion9
FallenAngel
Похоже, я неправильно прочитал ваше последнее сообщение. Подумал, что речь идет о выборе города из выпадающего списка, но вы, очевидно, имели в виду простой ввод в поле редактирования. Извиняюсь за это.

В общем, даже такое окно сделать на Autorun будет не просто. В редких случаях, когда это действительно требуется, можно использовать vbs-скрипт с вызывом функции Inputbox:
test.vbs

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

Wscript.Echo Inputbox("Введите имя города:","Wttr.in","")
В Autorun такой скрипт вызывается через ProcessExecGetOutput, например:

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

vbs = '/c ECHO Wscript.Echo Inputbox("Введите имя города:","Wttr.in","' & city & '")>%TEMP%\wttr.vbs'
    
ProcessExecGetOutput out %COMSPEC% %vbs%
Результат записывается в переменную и сохраняется в ini.

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

Сейчас у меня Wttr.ini такой:

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

[Wttr]
Name=Погода:
;Interval=1800
Url=https://wttr.in/47.37,8.55?format=%t
Hint=https://wttr.in/47.37,8.55?T
Соответственно, результат такой:

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

Total Commander 11.55 - Orion 8        Погода: -2°C    8 января, Чт.    CPU: 11%    RAM: 21%    NET: ↓ 0.2  ↑ 0 mb/s
Нормально смотрится. Хотя, конечно, это для теста.

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

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

Location: 10, Hottingerstrasse, Hottingen, Kreis 7, Zürich, Bezirk Zürich, Zürich, 8032, Schweiz/Suisse/Svizzera/Svizra [47.370096700000005,8.550088820923422]
Строка слишком длинная, поэтому обрезается до 125 символов. Но сама информация полезная.

Добавлено: Чт янв 08, 2026 16:56
FallenAngel
Orion9
Да, теперь получилось. Наверное, https:// был важен.

Это все очень важно расписать поподробнее в "шапке" - простому юзеру со всем этим не разобраться.
Нет, так не работает, появляется <html>, т.е. подтягивается веб-страница.


Вообще со строкой gInfoHeader = gTitle нужно что то делать. Я это вижу как блок в самом начале для переменных юзера, там где
Global gWttrIni = COMMANDER_PATH & "\User\Header.ini"
Global gWttrIniApp = COMMANDER_PATH & "\User\Wttr.ini"

И да, это нужно разделить и пояснить вначале:
==============
НАСТРОЙКИ ЮЗЕРА
==============
...

Так вот, там нужно указать что мы хотим видеть, в какой последовательности и как разделять:
weather=1
clock=2
cpu=3
ram=4
net=5
separator=' '


Идея в том, чтобы юзер дальше заголовка вообще не спускался в код. Можно конечно и ini-шником.

Добавлено спустя 4 минуты:
Orion9 писал(а):
FallenAngel писал(а):Работает.
А подсказка с прогнозом на три дня у вас тоже работает? Cервер хорошо откликается?
Если скрипт пройдет проверку временем, можно будет над этим подумать. Да, нужно добавить туда хотя бы версию Autorun, которая требуется для его работы, с этим я согласен.

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

Error in file: D:\soft\Total Commander 64\Plugins\Autorun\InfoHeader.aucfg

[193] lst.Text = RegExpGet(txt, '<pre>(.*?)</pre>', "$1")

Function name not found: RegExpGet
Это с ctrl.

Добавлено: Чт янв 08, 2026 19:24
Orion9
FallenAngel
Потребуется последняя версия Autorun, желательно не ниже 2.2.21.3 от 15.10.2025:

https://total.darkhost.ru/files/autorun ... 1_beta.zip
https://total.darkhost.ru/files/temp/au ... 251015.zip

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

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

LoadLibrary Plugins\Autorun_Tweaks.dll
LoadLibrary Plugins\Autorun_Sysinfo.dll
LoadLibrary Plugins\Autorun_Runtime.dll
LoadLibrary Plugins\Autorun_Process.dll
LoadLibrary Plugins\Autorun_RegExp.dll
Отсутствие последнего модуля RegExp приводит к ошибке "Function name not found: RegExpGet".

Функция RegExpGet извлекает текст подсказки из html, т.ч. с этим все нормально.

А на счет разбития заголовка на блоки, так это и будет тот самый универсализм, которого я изначально пытался избежать. Имхо, Cpu и Ram нет смысла отделять друг от друга. Net убирать из заголовка тоже как-то не катит. Можно однако сделать включение и выключение погоды отдельным пунктом, это на мой взгляд, не помешает.

Но вы не спешите. Попользуйтесь немного. Станет лучше видно где и что улучшить.

Добавлено: Пт янв 09, 2026 11:15
FallenAngel
Orion9 писал(а): Отсутствие последнего модуля RegExp приводит к ошибке "Function name not found: RegExpGet".
Все равно <html>.
Orion9 писал(а):Можно однако сделать включение и выключение погоды отдельным пунктом, это на мой взгляд, не помешает.
Да, и меню должно быть на английском, по умолчанию. А то, часть на английском - часть на русском.

Кстати, пункты Настройки... Пользователь... должны открываться через cm_Edit /N (т.е. настроенным редактором юзера).

Добавлено: Пт янв 09, 2026 12:36
Orion9
FallenAngel писал(а):Все равно <html>.
Он и должен подтягиваться.

При запросе к серверу во временном каталоге сохраняется html-файл. Этот файл затем открывается и при помощи регулярного выражения из него извлекается текст, т.е. буквально текст подсказки:

[cut]

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

Weather report: 47.37,8.55

     \  /       Partly cloudy
   _ /"".-.     +6(2) °C       
     \_(   ).   ↗ 24 km/h      
     /(___(__)  10 km          
                0.0 mm         
                                                       ┌─────────────┐                                                       
┌──────────────────────────────┬───────────────────────┤  Fri 09 Jan ├───────────────────────┬──────────────────────────────┐
│            Morning           │             Noon      └──────┬──────┘     Evening           │             Night            │
├──────────────────────────────┼──────────────────────────────┼──────────────────────────────┼──────────────────────────────┤
│    \  /       Partly cloudy  │  _`/"".-.     Light rain sho…│  _`/"".-.     Patchy rain ne…│  _`/"".-.     Light snow     │
│  _ /"".-.     +6(0) °C       │   ,\_(   ).   +5(0) °C       │   ,\_(   ).   +2(-2) °C      │   ,\_(   ).   +1(-3) °C      │
│    \_(   ).   ↗ 24-41 km/h   │    /(___(__)  ↗ 24-35 km/h   │    /(___(__)  ↗ 17-31 km/h   │    /(___(__)  ↗ 15-27 km/h   │
│    /(___(__)  10 km          │      ‘ ‘ ‘ ‘  10 km          │      ‘ ‘ ‘ ‘  10 km          │      *  *  *  10 km          │
│               0.0 mm | 0%    │     ‘ ‘ ‘ ‘   0.2 mm | 100%  │     ‘ ‘ ‘ ‘   0.0 mm | 85%   │     *  *  *   0.0 mm | 62%   │
└──────────────────────────────┴──────────────────────────────┴──────────────────────────────┴──────────────────────────────┘
                                                       ┌─────────────┐                                                       
┌──────────────────────────────┬───────────────────────┤  Sat 10 Jan ├───────────────────────┬──────────────────────────────┐
│            Morning           │             Noon      └──────┬──────┘     Evening           │             Night            │
├──────────────────────────────┼──────────────────────────────┼──────────────────────────────┼──────────────────────────────┤
│      .-.      Heavy snow     │  _`/"".-.     Moderate or he…│      .-.      Heavy snow     │      .-.      Heavy snow     │
│     (   ).    +1(-4) °C      │   ,\_(   ).   +1(-4) °C      │     (   ).    0(-5) °C       │     (   ).    0(-5) °C       │
│    (___(__)   ↗ 21-30 km/h   │    /(___(__)  ↗ 24-32 km/h   │    (___(__)   ↗ 19-27 km/h   │    (___(__)   ↗ 19-27 km/h   │
│    * * * *    2 km           │     * * * *   7 km           │    * * * *    2 km           │    * * * *    2 km           │
│   * * * *     0.5 mm | 100%  │    * * * *    0.3 mm | 100%  │   * * * *     0.5 mm | 100%  │   * * * *     0.8 mm | 100%  │
└──────────────────────────────┴──────────────────────────────┴──────────────────────────────┴──────────────────────────────┘
                                                       ┌─────────────┐                                                       
┌──────────────────────────────┬───────────────────────┤  Sun 11 Jan ├───────────────────────┬──────────────────────────────┐
│            Morning           │             Noon      └──────┬──────┘     Evening           │             Night            │
├──────────────────────────────┼──────────────────────────────┼──────────────────────────────┼──────────────────────────────┤
│      .-.      Moderate snow  │      .-.      Light freezing │    \  /       Partly Cloudy  │               Mist           │
│     (   ).    -3 °C          │     (   ).    -1 °C          │  _ /"".-.     -2(-4) °C      │  _ - _ - _ -  -8(-10) °C     │
│    (___(__)   ↓ 1-2 km/h     │    (___(__)   ↙ 3 km/h       │    \_(   ).   ↙ 5-9 km/h     │   _ - _ - _   ← 5-11 km/h    │
│    * * * *    5 km           │     ‘ * ‘ *   10 km          │    /(___(__)  10 km          │  _ - _ - _ -  2 km           │
│   * * * *     0.1 mm | 100%  │    * ‘ * ‘    0.0 mm | 0%    │               0.0 mm | 0%    │               0.0 mm | 0%    │
└──────────────────────────────┴──────────────────────────────┴──────────────────────────────┴──────────────────────────────┘
Location: 10, Hottingerstrasse, Hottingen, Kreis 7, Zürich, Bezirk Zürich, Zürich, 8032, Schweiz/Suisse/Svizzera/Svizra [47.370096700000005,8.550088820923422]
[/cut]
" заменяется на кавычку, а последняя длинная строка обрезается до 125 символов, чтобы окно подсказки не разъехалось. На последнем этапе текст отображается функцией ShowHint с определенным стилем (темный фон и размер шрифта). Последнее, кстати, можно изменить по вкусу.
FallenAngel писал(а):Да, и меню должно быть на английском, по умолчанию. А то, часть на английском - часть на русском.
Тут уж всем не угодишь. В этом и заключается сложность универсализма.

Не стесняйтесь вносить изменения в скрипт. Некоторые вещи не требуют большого знания Autorun. Например, вы сами можете поменять поряд блоков в заголовоке. Просто закомментрируйте старую строку:

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

 gInfoHeader = gTitle & '        ' & (gWttr ? gWttrData & '    ' : '') & Date("d MMMM, ddd") & '.    CPU: ' & StrFormat("%02d", GetCPUUsage()) & '%    RAM: ' & GetMemStats() & '%    NET: ↓ ' & Round(nRecvSpeed / 1000, 1) & '  ↑ ' & Round(nSendSpeed / 1000, 1) & ' mb/s'
И скопируйте ее в новую. Дальше попробуйте поменять местами текст и переменные как вам больше понравится.

Это же касается и других частей интерфеса. Если вам не нравится сообщение "Booting...", к примеру, вы можете поменять его на любое другое, например на "Loading..." или "Starting..." или вообще его закомментировать, т.е. убрать.

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

Добавлено: Чт янв 15, 2026 19:24
Orion9
FallenAngel
Еще раз просмотрел и обновил InfoHeader.aucfg

Добавил некоторые проверки и изменил меню заголовка. Реорганизовать пункты можно по-разному, но пока остановился на таком варианте.

Loopback на заметку: запись ниже не выгружает иконку из-за расширения .cpl:

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

em_aucmd "timedate.cpl" ShellExec rundll32.exe
Можно такое учесть для апплетов?

FallenAngel
В меню теперь можно включить или отключить отображение погоды: так удобнее, чем вручную править файл конфигурации.

Файл пользователя можно создать автоматически, если он отсутствует. Для этого используется шаблон:

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

[Wttr]
Name=Погода:
Interval=1800
Url=https://wttr.in/47.37,8.55?format=%t
Hint=https://wttr.in/47.37,8.55?T

Но при желании его можно изменить в скрипте.

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

[cut="InfoHeader.aucfg"]

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

Sleep(200)

# наведение на заголовок
ControlSetHint /F /D:350 /H:2 0 "InfoHeaderHint"

# правый клик
ControlSetMouseAction /R /H:2 0 "InfoHeaderMenu"

# Shift + клик по заголовку
ControlSetMouseAction /L /K:S /H:2 0 "ToggleInfoHeader"

Global gTitle = WinGetText(), gInfoHeader = gTitle, gShowTitle = 1, gTitleThread = 0, gTitleRound = 2

Global gWttr = 1, gWttrUrl, gWttrHint, gWttrName, gWttrInterval = 3600
Global gWttrSec = 0, gWttrData, gWttrTip = "", gWttrFile = TEMP & "\wttr.txt", gWttrDataUpdate = 0

Global gWttrIni = COMMANDER_PATH & "\Ini\Header.ini"
Global gWttrIniApp = GetKnownFolderPath("LocalAppData") & "\GHISLER\Wttr.ini"

# запуск
LaunchInfoHeader()

# WM_WINDOWPOSCHANGING
# реагирование заголовка на изменение позиции окна
SetMessageAction 70 "SetInfoHeader"

Func ToggleInfoHeader()
    # переключение переменной
    gShowTitle = Not gShowTitle
    # запись нового значения в Header.ini
    IniWrite %gWttrIni% "Title" "ShowTitle" %"gShowTitle"    
    Sleep(100)
    LaunchInfoHeader()
EndFunc
 
Func SetInfoHeader()
    WinSetText(gShowTitle ? gInfoHeader : gTitle)
EndFunc

Func LaunchInfoHeader()
    # чтение ключа
    IniRead gShowTitle %gWttrIni% "Title" "ShowTitle" 1
    # запуск заголовка, если он еще не запущен
    If gShowTitle And Not gTitleThread Then
       gInfoHeader = gTitle & '        ' & "Booting..."
       WinSetText(gInfoHeader)
       # подготовка конфигурации
       InfoHeaderReadIni()       
       Sleep(200)
       RunThread InfoHeader
    EndIf   
EndFunc

Func InfoHeaderReadIni()
    Local sWttrIni = gWttrIni, sUrl
    # чтение из Header.ini
    IniRead gWttr         %gWttrIni%   "Title"  "Wttr" 1
    IniRead sUrl          %sWttrIni%   "Wttr"   "Url" ""    
    # чтение из AppData\Wttr.ini
    If FileExist(gWttrIniApp) Then
       sWttrIni = gWttrIniApp
    EndIf
    IniRead gWttrUrl      %sWttrIni%   "Wttr"  "Url" ""
    IniRead gWttrHint     %sWttrIni%   "Wttr"  "Hint" ""    
    IniRead gWttrName     %sWttrIni%   "Wttr"  "Name" ""
    IniRead gWttrInterval %sWttrIni%   "Wttr"  "Interval" 3600         
    # нет данных в AppData
    # откат к значению из Header.ini
    If gWttrUrl = "" Then gWttrUrl = sUrl
    # проверка корректности значения интервала
    If Not IsInt(gWttrInterval) Then gWttrInterval = 0    
    if gWttrInterval < 900 Then gWttrInterval = 900
    # по умолчанию интервал считается законченным
    gWttrSec = gWttrInterval
    # проверка существования локального файла
    If FileExist(gWttrFile) Then
       # количество секунд с последнего обновления
       gWttrSec = Round((Now() - FileGetTime(gWttrFile)) / 10000000)
       # прошло меньше секунд, чем отведенный интервал
       If gWttrSec < gWttrInterval Then gWttrData = StrTrim(FileRead(gWttrFile))
       If gWttrName <> "" Then gWttrData = gWttrName & " " & gWttrData
    EndIf       
EndFunc

Func InfoHeader
   Local nRecvSpeed, nSendSpeed, T1, lst = List(" | "," / ","—"," \ ")
   Static c = 0
   # поток запущен
   gTitleThread = 1
   # выполнять пока gShowTitle = true
   While gShowTitle
      T1 = GetUptime()
      # погода включена и требуется обновление
      If gWttr And gWttrSec >= gWttrInterval Then 
         # сброс счетчика
         gWttrSec = 0
         # запуск обновления в отдельном потоке 
         RunThread "GetWttrData"
      EndIf
      GetNetSpeed(nRecvSpeed, nSendSpeed)
      # идет процесс обновления
      If gWttrDataUpdate Then
         c += 1
         If c > lst.Count Then c = 1
         #gWttrData = Weather updating: " & lst[c-1]     
         gWttrData = "Weather updating: " & gWttrSec & ' sec.'
      EndIf
      gInfoHeader = gTitle & '        ' & (gWttr ? gWttrData & '    ' : '') & Date("d MMMM, ddd") & '.    CPU: ' & StrFormat("%02d", GetCPUUsage()) & '%    RAM: ' & GetMemStats() & '%    NET: ↓ ' & Round(nRecvSpeed / 1000, gTitleRound) & '  ↑ ' & Round(nSendSpeed / 1000, gTitleRound) & ' mb/s'
      WinSetText(gInfoHeader)
      # разбитие секунды на более мелкие интервалы
      # для лучшего отклика при изменении переменной
      While GetUptime() < T1 + 1000
          If Not gShowTitle Then Break
          Sleep(50)
      Wend
      # увеличение счетчика до следующего обновления погоды
      gWttrSec += 1 
   Wend
   Free(lst)
   # поток остановлен   
   gTitleThread = 0
   # обновление заголовка
   SetInfoHeader()
EndFunc

Func GetWttrData()
    Local msg, sData
    # флаг операции
    gWttrDataUpdate = true
    gWttrTip = "Соединение..."
    # загрузка файла в тихом режиме
    Local code = WinInetDownloadFile(gWttrUrl, gWttrFile, 1)
    # снятие флага операции для заголовка
    gWttrDataUpdate = false    
    If code = -1 Then
       msg = "WinInet Error: " & code
    ElseIf code > 0 Then
       msg = "WinInet Error: " & code
       gWttrTip = msg & auCRLF & GetWinInetError(code)
    EndIf
    If code = 0 Then
       gWttrTip = "Обновлено"
       msg = FileRead(gWttrFile)
       If ERROR = 1 Then msg = "FileRead Error: "
    EndIf
    sData = StrTrim(msg)
    gWttrData = sData
    # добавление имени местоположения если указано
    If code = 0 And gWttrName <> "" Then gWttrData = gWttrName & " " & sData
    If code = 0 Then
       gWttrSec = 0
    Else
       # повтор через 30 секунд
       gWttrSec = gWttrInterval - 30
    EndIf       
    # загрузка данных для подсказки в тихом режиме
    code = WinInetDownloadFile(gWttrHint, TEMP & "\wttr.html", 1)
    If code <> 0 Then
       msg = "WinInet Error: " & code
       gWttrTip &= auCRLF & "Хинт: " &  msg & auCRLF & GetWinInetError(code)     
    EndIf
EndFunc

Func InfoHeaderHint()
    Local txt, file = TEMP & "\wttr.html"
    If Not IsPressed (0x11) Then
       If Not gShowTitle Then Return "Заголовок выключен"
       txt &= "Работает" & auCRLF
       txt &= "Погода " & (gWttr? "включена" : "отключена") & auCRLF
       If gWttr Then
          txt &= "Интервал: " & gWttrInterval & " сек." & auCRLF                
          txt &= "Обновление через: " & gWttrInterval - gWttrSec & " сек." & auCRLF       
          If gWttrTip <> "" Then txt &= "Статус: " & gWttrTip & auCRLF
       EndIf 
       If IsPressed (0x12) Then
          txt &= "Файлы конфигурации:" & auCRLF
          txt &= gWttrIni & auCRLF
          txt &= gWttrIniApp & auCRLF
          txt &= "Временные файлы:" & auCRLF
          txt &= "Файл от " & Date('', gWttrFile) & ' ' & Time('', gWttrFile) & " "
          txt &= gWttrFile & auCRLF
          txt &= "Файл от " & Date('', file) & ' ' & Time('', file) & " "
          txt &= file & auCRLF
       EndIf   
       Return StrTrim(txt)
    Endif
    txt = FileRead(file)
    If ERROR = 1 Then Return "Ошибка чтения " & file
    # стиль подсказки
    SetHintParam("csh", "Font", 10, "Consolas")
    SetHintParam("csh", "BackColor", 0x001C1C1C)
    SetHintParam("csh", "Text", 0xF9F9F9)  
    # извлечение текста из html
    txt = StrReplace(txt, '"', '"')
    Static lst = List()
    lst.Count = 0
    lst.Text = RegExpGet(txt, '<pre>(.*?)</pre>', "$1")
    For i = 0 To lst.Count - 1
        If StrLen(lst[i]) > 125 Then lst[i] = StrLeft(lst[i], 122) & ".."
    Next
    # вернуть предыдущий стиль через 1 сек.        
    RunThread InfoHeaderHintReload 
    If lst.Count = 0 Then Return "Файл не содержит данных " & file
    Return lst.Text
EndFunc

Func InfoHeaderHintReload()
    Sleep(1000)
    SetHintParam("sh", "Reload")
    SetHintParam("csh", "Reload")    
EndFunc

Func InfoHeaderMenu()
     ShowPopupMenu /D /F "InfoHeaderCreateMenu"
EndFunc

Func InfoHeaderCreateMenu()
    Local txt 
    txt &= 'MENUITEM "Включить", em_aucmd "%COMMANDER_EXE%,37" InfoHeaderStart' & auCRLF
    txt &= 'MENUITEM "Выключить", em_aucmd "%COMMANDER_EXE%,36" InfoHeaderStop' & auCRLF
    txt &= 'MENUITEM "Обновить погоду", em_aucmd "shell32.dll,13" InfoHeaderUpdate' & auCRLF
    txt &= 'MENUITEM SEPARATOR' & auCRLF
    txt &= 'POPUP "Заголовок"' & auCRLF
    txt &= 'MENUITEM "Отображать погоду", em_aucmd -1 InfoHeaderWttr 1' & auCRLF
    txt &= 'MENUITEM "Не отображать погоду", em_aucmd -1 InfoHeaderWttr 0' & auCRLF    
    txt &= 'END_POPUP' & auCRLF         
    txt &= 'MENUITEM "Настройки...",  em_aucmd "imageres.dll,62" InfoHeaderEditIni' & auCRLF
    txt &= 'MENUITEM "Пользователь...",  em_aucmd -1 InfoHeaderEditIniApp' & auCRLF
    txt &= 'MENUITEM SEPARATOR' & auCRLF
    txt &= 'POPUP "Перейти"' & auCRLF
    txt &= 'MENUITEM "Перейти к Wttr.ini", em_aucmd "imageres.dll,62" InfoHeaderGotoIniApp' & auCRLF
    txt &= 'MENUITEM "Перейти к Wttr.txt", em_aucmd -1 InfoHeaderGotoTemp "Wttr.txt"' & auCRLF
    txt &= 'MENUITEM "Перейти к Wttr.html", em_aucmd -1 InfoHeaderGotoTemp "Wttr.html"' & auCRLF
    txt &= 'MENUITEM SEPARATOR' & auCRLF    
    txt &= 'MENUITEM "Перейти к Header.ini", em_aucmd "imageres.dll,62" InfoHeaderGotoIni' & auCRLF    
    txt &= 'END_POPUP' & auCRLF    
    txt &= 'MENUITEM "Даты и время",  em_aucmd "timedate.cpl" ShellExec rundll32.exe "shell32.dll,Control_RunDLL timedate.cpl"' & auCRLF       
    Return txt
EndFunc

Func InfoHeaderStart()
    # запись значения в Header.ini
    IniWrite %gWttrIni% "Title" "ShowTitle" 1   
    Sleep(100)
    LaunchInfoHeader()
EndFunc

Func InfoHeaderStop()
    IniWrite %gWttrIni% "Title" "ShowTitle" 0
    Sleep(100)
    LaunchInfoHeader()
EndFunc

Func InfoHeaderUpdate()
    If Not gWttr Then
       ShowHint("Погода выключена")
       Return
    EndIf
    # обновление через 1 сек.
    gWttrSec = gWttrInterval - 1
EndFunc

Func InfoHeaderWttr(Value)
    IniWrite %gWttrIni% "Title" "Wttr" %"Value"
    gWttr = Value
EndFunc

Func InfoHeaderEditIni()
    If FileExist(gWttrIni) Then
       ShellExec(gWttrIni)
    Else
       MsgBox("Файл не существует " & gWttrIni, "Autorun", 16)
    EndIf   
EndFunc

Func InfoHeaderGotoIni()
    CommandExec("~/CD:S", gWttrIni)
EndFunc

Func InfoHeaderEditIniApp()
    If FileExist(gWttrIniApp) Then
       ShellExec(gWttrIniApp)
    Else
       MsgBox("Файл пользователя не существует " & gWttrIniApp & auCRLF & auCRLF & _
              "Создать новый?", "Autorun", 3+0+32)
       If EXTENDED <> 6 Then Return
       Local txt = "[Wttr]" & auCRLF & _
                   "Name=Погода:" & auCRLF & _
                   "Interval=1800" & auCRLF & _
                   "Url=https://wttr.in/47.37,8.55?format=%t" & auCRLF & _
                   "Hint=https://wttr.in/47.37,8.55?T"
       FileWrite(gWttrIniApp, txt, "ANSI") 
       Sleep(100)
       If FileExist(gWttrIniApp) Then ShellExec(gWttrIniApp)
    EndIf
EndFunc

Func InfoHeaderGotoIniApp()
    CommandExec("~/CD:S", gWttrIniApp)
EndFunc

Func InfoHeaderGotoTemp(Filename)
    Local sFile = TEMP & "\" & Filename
    If FileExist(sFile) Then
       CommandExec("~/CD:S", sFile)
    Else
       MsgBox("Файл не существует " & sFile, "Autorun", 16)
    EndIf   
EndFunc

Func GetNetSpeed(ByRef nRecvSpeed, ByRef nSendSpeed)
    Static nIntfIdx = GetNetInterface()
    If nIntfIdx = -1 Then Return
 
    Static nPrevRecv = GetNetInterfaceInfo(nIntfIdx, 'Recv')
    Static nPrevSend = GetNetInterfaceInfo(nIntfIdx, 'Sent')
    Static nPrevTime = GetUptime()
 
    Local nRecv = GetNetInterfaceInfo(nIntfIdx, 'Recv')
    Local nSend = GetNetInterfaceInfo(nIntfIdx, 'Sent')
    Local nTime = GetUptime()
    Local nTimeDiff = nTime - nPrevTime
 
    nRecvSpeed = Round(Abs(nRecv - nPrevRecv) / nTimeDiff)
    nSendSpeed = Round(Abs(nSend - nPrevSend) / nTimeDiff)
 
    nPrevRecv = nRecv
    nPrevSend = nSend
    nPrevTime = nTime
EndFunc
[/cut]

Сервер работает плохо. Пару раз во временном каталоге сохранялся недокаченный на несколькой килобайт файл. Поскольку закрывающий тег </pre> в таком html обрезан, регулярное выражение не может вернуть текст. В таком случае вместо подсказки будет сообщение, что файл не содержит данных. Чтобы подсказка заработала необходимо еще раз обновить погоду и дождаться полной загрузки файла.

Добавлено: Пт янв 16, 2026 13:00
FallenAngel
Orion9 писал(а): Сервер работает плохо.
Я из-за этого пока оставил идею с погодой в заголовке. Но за обновление спасибо, потестирую.


А вообще, ждем возвращения Loopback - нужно как-то все таки добраться до заголовка листера.

Добавлено: Пт янв 16, 2026 19:46
Orion9
FallenAngel
Я там фигню сморозил. Вместо:

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

txt &= "Файл от " & Date('', gWttrFile) & ' ' & Time('', gWttrFile) & " "
txt &= gWttrFile & auCRLF
txt &= "Файл от " & Date('', file) & ' ' & Time('', file) & " "
txt &= file & auCRLF 
Должно быть:

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

txt &= "Временные файлы:" & auCRLF
txt &= "Файл от " & Date('', FileGetTime(gWttrFile)) & ' ' & Time('', FileGetTime(gWttrFile)) & " "
txt &= gWttrFile & auCRLF
txt &= "Файл от " & Date('', FileGetTime(file)) & ' ' & Time('', FileGetTime(file)) & " "
txt &= file & auCRLF
На автомате просто делал и не проверил толком, но теперь корректно показывает дату изменения файлов по Alt, так что сразу видно, когда было последнее обновление.

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

Сейчас лучше видно, что происходит в фоне. Информация в заголовке обновляется хоть и медленно, но стабильно (все-таки файл очень маленький приходит от сервера). А вот с Wttr.html могут быть проблемы. Мало того, что он скачивается медленно, так еще и не докачивается иногда, хотя у него размер всего 25-30 Кб. В общем, добавил некоторых проверок. Сейчас недокаченный файл будет отображаться в подсказке примерно так:
[cut]

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

Файл поврежден 
<pre>
Weather report: 47.37,8.55

      \   /     Sunny
       .-.      4 °C           
    ― (   ) ―   ↓ 4 km/h       
       `-’      10 km          
      /   \     0.0 mm         
                                                       ┌─────────────┐                                                       
┌──────────────────────────────┬───────────────────────┤  Fri 16 Jan ├───────────────────────┬──────────────────────────────┐
│            Morning           │             Noon      └──────┬──────┘     Evening           │             Night            │
├──────────────────────────────┼──────────────────────────────┼──────────────────────────────┼──────────────────────────────┤
│    \  /       Partly Cloudy  │     \   /     Sunny          │               Freezing fog   │               Mist           │
│  _ /"".-.     -2 °C          │      .-.      +4(3) °C       │  _ - _ - _ -  -2(-3) °C      │  _ - _ - _ -  -3(-4) °C      │
│    \_(   ).   ← 2-4 km/h     │   ― (   ) ―   ↓ 4-5 km/h     │   _ - _ - _   ← 4-8 km/h     │   _ - _ - _   ↖ 4-8 km/h     │
│    /(___(__)  10 km          │      `-’      10 km          │  _ - _ - _ -  0 km           │  _ - _ - _ -  2 km           │
│               0.0 mm | 0%    │     /   \     0.0 mm | 0%    │               0.0 mm | 0%    │               0.0 mm | 0%    │
└──────────────────────────────┴──────────────────────────────┴──────────────────────────────┴────────────────�[/
[/cut]
Это лучше, чем сообщение, что файл не содержит данных.

Еще изменил имена у некоторых функций для лучшей унификации имён в модуле.

Что касается Листера, если задача стоит добиться, чтобы его окно при открытии имело тот же размер и положение, что и окно тотала, то можно привязать функцию к сообщению об изменении позиции окна ТС, в модуле это выглядит так:

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

Func InfoHeaderSet()
    WinSetText(gShowTitle ? gInfoHeader : gTitle)
    SyncListerPosition()
EndFunc

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
Раскомментируйте ShowHint, чтобы увидеть количество записей в ini-файл, если интересно. Если фича не нужна, просто закомментируйте или уберите SyncListerPosition() из функции InfoHeaderSet().

Теоретически функции SyncListerPosition можно присвоить код через RegisterCommand, например 60050, а далее сделать em_команду с указанным кодом (об этом было 2-3 страницы назад). Полученную em_команду можно использовать в свзяке с командами cm_List*, чтобы перед запуском листера записывались параметры его окна в файл конфигурации.

Shift+клик, как вы хотите, самый сложный вариант. Нужно перехватывать клик, смотреть под каким окном находится указатель мыши, проверять клиентская ли это часть окна или не клиентская, и только потом запускать действие. Тут без Loopback, наверное, не обойтись.

Последний модуль:
[cut]

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

Sleep(200)

# наведение на заголовок
ControlSetHint /F /D:350 /H:2 0 "InfoHeaderHint"

# правый клик
ControlSetMouseAction /R /H:2 0 "InfoHeaderMenu"

# Shift + клик по заголовку
ControlSetMouseAction /L /K:S /H:2 0 "InfoHeaderSwitch"

Global gTitle = WinGetText(), gInfoHeader = gTitle, gShowTitle = 1, gTitleThread = 0, gTitleRound = 2

Global gWttr = 1, gWttrUrl, gWttrHint, gWttrName, gWttrInterval = 3600
Global gWttrSec = 0, gWttrData, gWttrTip = "", gWttrFile = TEMP & "\wttr.txt", gWttrDataUpdate = 0

Global gWttrIni = COMMANDER_PATH & "\Ini\Header.ini"
Global gWttrIniApp = GetKnownFolderPath("LocalAppData") & "\GHISLER\Wttr.ini"

# запуск
InfoHeaderLaunch()

# WM_WINDOWPOSCHANGING
# реагирование заголовка на изменение позиции окна
SetMessageAction 70 "InfoHeaderSet"

Func InfoHeaderSwitch()
    # переключение переменной
    gShowTitle = Not gShowTitle
    # запись нового значения в Header.ini
    IniWrite %gWttrIni% "Title" "ShowTitle" %"gShowTitle"    
    Sleep(100)
    InfoHeaderLaunch()
EndFunc
 
Func InfoHeaderSet()
    WinSetText(gShowTitle ? gInfoHeader : gTitle)
    SyncListerPosition()
EndFunc

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

Func InfoHeaderLaunch()
    # чтение ключа
    IniRead gShowTitle %gWttrIni% "Title" "ShowTitle" 1
    # запуск заголовка, если он еще не запущен
    If gShowTitle And Not gTitleThread Then
       gInfoHeader = gTitle & '        ' & "Booting..."
       WinSetText(gInfoHeader)
       # подготовка конфигурации
       InfoHeaderReadIni()       
       Sleep(200)
       RunThread InfoHeader
    EndIf   
EndFunc

Func InfoHeaderReadIni()
    Local sWttrIni = gWttrIni, sUrl
    # чтение из Header.ini
    IniRead gWttr         %gWttrIni%   "Title"  "Wttr" 1
    IniRead sUrl          %sWttrIni%   "Wttr"   "Url" ""    
    # чтение из AppData\Wttr.ini
    If FileExist(gWttrIniApp) Then
       sWttrIni = gWttrIniApp
    EndIf
    IniRead gWttrUrl      %sWttrIni%   "Wttr"  "Url" ""
    IniRead gWttrHint     %sWttrIni%   "Wttr"  "Hint" ""    
    IniRead gWttrName     %sWttrIni%   "Wttr"  "Name" ""
    IniRead gWttrInterval %sWttrIni%   "Wttr"  "Interval" 3600         
    # нет данных в AppData
    # откат к значению из Header.ini
    If gWttrUrl = "" Then gWttrUrl = sUrl
    # проверка корректности значения интервала
    If Not IsInt(gWttrInterval) Then gWttrInterval = 0    
    if gWttrInterval < 900 Then gWttrInterval = 900
    # по умолчанию интервал считается законченным
    gWttrSec = gWttrInterval
    # проверка существования локального файла
    If FileExist(gWttrFile) Then
       # количество секунд с последнего обновления
       gWttrSec = Round((Now() - FileGetTime(gWttrFile)) / 10000000)
       # прошло меньше секунд, чем отведенный интервал
       If gWttrSec < gWttrInterval Then gWttrData = StrTrim(FileRead(gWttrFile))
       If gWttrName <> "" Then gWttrData = gWttrName & " " & gWttrData
    EndIf       
EndFunc

Func InfoHeader
   Local nRecvSpeed, nSendSpeed, T1, lst = List(" | "," / ","—"," \ ")
   Static c = 0
   # поток запущен
   gTitleThread = 1
   # выполнять пока gShowTitle = true
   While gShowTitle
      T1 = GetUptime()
      # погода включена и требуется обновление
      If gWttr And gWttrSec >= gWttrInterval Then 
         # сброс счетчика
         gWttrSec = 0
         # запуск обновления в отдельном потоке 
         RunThread "GetWttrData"
      EndIf
      GetNetSpeed(nRecvSpeed, nSendSpeed)
      # идет процесс обновления
      If gWttrDataUpdate Then
         c += 1
         If c > lst.Count Then c = 1
         #gWttrData = Weather updating: " & lst[c-1]     
         gWttrData = "Weather updating: " & gWttrSec & ' sec.'
      EndIf
      gInfoHeader = gTitle & '        ' & (gWttr ? gWttrData & '    ' : '') & Date("d MMMM, ddd") & '.    CPU: ' & StrFormat("%02d", GetCPUUsage()) & '%    RAM: ' & GetMemStats() & '%    NET: ↓ ' & Round(nRecvSpeed / 1000, gTitleRound) & '  ↑ ' & Round(nSendSpeed / 1000, gTitleRound) & ' mb/s'
      WinSetText(gInfoHeader)
      # разбитие секунды на более мелкие интервалы
      # для лучшего отклика при изменении переменной
      While GetUptime() < T1 + 1000
          If Not gShowTitle Then Break
          Sleep(50)
      Wend
      # увеличение счетчика до следующего обновления погоды
      gWttrSec += 1 
   Wend
   Free(lst)
   # поток остановлен   
   gTitleThread = 0
   # обновление заголовка
   InfoHeaderSet()
EndFunc

Func GetWttrData()
    Local msg, sData, sTip
    # флаг операции
    gWttrDataUpdate = true
    gWttrTip = "Соединение..."
    # загрузка файла в тихом режиме
    Local code = WinInetDownloadFile(gWttrUrl, gWttrFile, 1)
    # снятие флага операции для заголовка
    gWttrDataUpdate = false    
    If code = -1 Then
       msg = "WinInet Error: " & code
    ElseIf code > 0 Then
       msg = "WinInet Error: " & code
       gWttrTip = msg & auCRLF & GetWinInetError(code)
    EndIf
    If code = 0 Then
       gWttrTip = "Обновлено в " & Time("", Now())
       msg = FileRead(gWttrFile)
       If ERROR = 1 Then msg = "FileRead Error: "
    EndIf
    sData = StrTrim(msg)
    gWttrData = sData
    # добавление имени местоположения если указано
    If code = 0 And gWttrName <> "" Then gWttrData = gWttrName & " " & sData
    If code = 0 Then
       gWttrSec = 0
    Else
       # повтор через 30 секунд
       gWttrSec = gWttrInterval - 30
    EndIf
    sTip = gWttrTip
    # загрузка данных для подсказки в тихом режиме
    gWttrTip = sTip & auCRLF & "Подсказка: Соединение..."
    code = WinInetDownloadFile(gWttrHint, TEMP & "\wttr.html", 1)
    If code <> 0 Then
       msg = "WinInet Error: " & code
       gWttrTip = sTip & auCRLF & "Подсказка: " &  msg & auCRLF
       If code = -1 Then
          gWttrTip &= "Download failed or ended with an error."
       Else
          gWttrTip &= GetWinInetError(code)
       EndIf
    Else
       gWttrTip = sTip & auCRLF & "Подсказка: Обновлено в " & Time("", Now())       
    EndIf
EndFunc

Func InfoHeaderHint()
    Local txt, file = TEMP & "\wttr.html"
    If Not IsPressed (0x11) Then
       If Not gShowTitle Then Return "Заголовок выключен"
       txt &= "Работает" & auCRLF
       txt &= "Погода " & (gWttr? "включена" : "отключена") & auCRLF
       If gWttr Then
          txt &= "Интервал: " & gWttrInterval & " сек." & auCRLF                
          txt &= "Обновление через: " & gWttrInterval - gWttrSec & " сек." & auCRLF       
          If gWttrTip <> "" Then txt &= "Статус: " & gWttrTip & auCRLF
       EndIf 
       If IsPressed (0x12) Then
          txt &= "Файлы конфигурации:" & auCRLF
          txt &= gWttrIni & auCRLF
          txt &= gWttrIniApp & auCRLF
          txt &= "Временные файлы:" & auCRLF
          txt &= "Файл от " & Date('', FileGetTime(gWttrFile)) & ' ' & Time('', FileGetTime(gWttrFile)) & " "
          txt &= gWttrFile & auCRLF
          txt &= "Файл от " & Date('', FileGetTime(file)) & ' ' & Time('', FileGetTime(file)) & " "
          txt &= file & auCRLF
       EndIf   
       Return StrTrim(txt)
    Endif
    txt = FileRead(file)
    If ERROR = 1 Then Return "Ошибка чтения " & file
    If txt = "" Then Return "Пустой файл " & file
    Local nPrePos = StrPos(txt, "<pre>")
    If nPrePos = 0 Then Return "Файл не содержит данных " & file    
    # стиль подсказки
    SetHintParam("csh", "Font", 10, "Consolas")
    SetHintParam("csh", "BackColor", 0x001C1C1C)
    SetHintParam("csh", "Text", 0xF9F9F9)  
    # извлечение текста из html
    txt = StrReplace(txt, '"', '"')
    Static lst = List()
    lst.Count = 0
    lst.Text = StrTrim(RegExpGet(txt, '<pre>(.*?)</pre>', "$1"))
    For i = 0 To lst.Count - 1
        If StrLen(lst[i]) > 125 Then lst[i] = StrLeft(lst[i], 122) & ".."
    Next
    # вернуть предыдущий стиль через 1 сек.        
    RunThread InfoHeaderHintReload 
    If lst.Count = 0 Then
       Return "Файл поврежден " & auCRLF & StrMid(txt, nPrePos)
    EndIf   
    Return lst.Text
EndFunc

Func InfoHeaderHintReload()
    Sleep(1000)
    SetHintParam("sh", "Reload")
    SetHintParam("csh", "Reload")    
EndFunc

Func InfoHeaderMenu()
     ShowPopupMenu /D /F "InfoHeaderCreateMenu"
EndFunc

Func InfoHeaderCreateMenu()
    Local txt 
    txt &= 'MENUITEM "Включить", em_aucmd "%COMMANDER_EXE%,37" InfoHeaderStart' & auCRLF
    txt &= 'MENUITEM "Выключить", em_aucmd "%COMMANDER_EXE%,36" InfoHeaderStop' & auCRLF
    txt &= 'MENUITEM "Обновить погоду", em_aucmd "shell32.dll,13" InfoHeaderUpdate' & auCRLF
    txt &= 'MENUITEM SEPARATOR' & auCRLF
    txt &= 'POPUP "Заголовок"' & auCRLF
    txt &= 'MENUITEM "Отображать погоду", em_aucmd -1 InfoHeaderWttr 1' & auCRLF
    txt &= 'MENUITEM "Не отображать погоду", em_aucmd -1 InfoHeaderWttr 0' & auCRLF    
    txt &= 'END_POPUP' & auCRLF         
    txt &= 'MENUITEM "Настройки...",  em_aucmd "imageres.dll,62" InfoHeaderEditIni' & auCRLF
    txt &= 'MENUITEM "Пользователь...",  em_aucmd -1 InfoHeaderEditIniApp' & auCRLF
    txt &= 'MENUITEM SEPARATOR' & auCRLF
    txt &= 'POPUP "Перейти"' & auCRLF
    txt &= 'MENUITEM "Перейти к Wttr.ini", em_aucmd "imageres.dll,62" InfoHeaderGotoIniApp' & auCRLF
    txt &= 'MENUITEM "Перейти к Wttr.txt", em_aucmd -1 InfoHeaderGotoTemp "Wttr.txt"' & auCRLF
    txt &= 'MENUITEM "Перейти к Wttr.html", em_aucmd -1 InfoHeaderGotoTemp "Wttr.html"' & auCRLF
    txt &= 'MENUITEM SEPARATOR' & auCRLF    
    txt &= 'MENUITEM "Перейти к Header.ini", em_aucmd "imageres.dll,62" InfoHeaderGotoIni' & auCRLF    
    txt &= 'END_POPUP' & auCRLF    
    txt &= 'MENUITEM "Даты и время",  em_aucmd "timedate.cpl" ShellExec rundll32.exe "shell32.dll,Control_RunDLL timedate.cpl"' & auCRLF       
    Return txt
EndFunc

Func InfoHeaderStart()
    # запись значения в Header.ini
    IniWrite %gWttrIni% "Title" "ShowTitle" 1   
    Sleep(100)
    InfoHeaderLaunch()
EndFunc

Func InfoHeaderStop()
    IniWrite %gWttrIni% "Title" "ShowTitle" 0
    Sleep(100)
    InfoHeaderLaunch()
EndFunc

Func InfoHeaderUpdate()
    If Not gWttr Then
       ShowHint("Погода выключена")
       Return
    EndIf
    # обновление через 1 сек.
    gWttrSec = gWttrInterval - 1
EndFunc

Func InfoHeaderWttr(Value)
    IniWrite %gWttrIni% "Title" "Wttr" %"Value"
    gWttr = Value
EndFunc

Func InfoHeaderEditIni()
    If FileExist(gWttrIni) Then
       ShellExec(gWttrIni)
    Else
       MsgBox("Файл не существует " & gWttrIni, "Autorun", 16)
    EndIf   
EndFunc

Func InfoHeaderGotoIni()
    CommandExec("~/CD:S", gWttrIni)
EndFunc

Func InfoHeaderEditIniApp()
    If FileExist(gWttrIniApp) Then
       ShellExec(gWttrIniApp)
    Else
       MsgBox("Файл пользователя не существует " & gWttrIniApp & auCRLF & auCRLF & _
              "Создать новый?", "Autorun", 3+0+32)
       If EXTENDED <> 6 Then Return
       Local txt = "[Wttr]" & auCRLF & _
                   "Name=Погода:" & auCRLF & _
                   "Interval=1800" & auCRLF & _
                   "Url=https://wttr.in/47.37,8.55?format=%t" & auCRLF & _
                   "Hint=https://wttr.in/47.37,8.55?T"
       FileWrite(gWttrIniApp, txt, "ANSI") 
       Sleep(100)
       If FileExist(gWttrIniApp) Then ShellExec(gWttrIniApp)
    EndIf
EndFunc

Func InfoHeaderGotoIniApp()
    CommandExec("~/CD:S", gWttrIniApp)
EndFunc

Func InfoHeaderGotoTemp(Filename)
    Local sFile = TEMP & "\" & Filename
    If FileExist(sFile) Then
       CommandExec("~/CD:S", sFile)
    Else
       MsgBox("Файл не существует " & sFile, "Autorun", 16)
    EndIf   
EndFunc

Func GetNetSpeed(ByRef nRecvSpeed, ByRef nSendSpeed)
    Static nIntfIdx = GetNetInterface()
    If nIntfIdx = -1 Then Return
 
    Static nPrevRecv = GetNetInterfaceInfo(nIntfIdx, 'Recv')
    Static nPrevSend = GetNetInterfaceInfo(nIntfIdx, 'Sent')
    Static nPrevTime = GetUptime()
 
    Local nRecv = GetNetInterfaceInfo(nIntfIdx, 'Recv')
    Local nSend = GetNetInterfaceInfo(nIntfIdx, 'Sent')
    Local nTime = GetUptime()
    Local nTimeDiff = nTime - nPrevTime
 
    nRecvSpeed = Round(Abs(nRecv - nPrevRecv) / nTimeDiff)
    nSendSpeed = Round(Abs(nSend - nPrevSend) / nTimeDiff)
 
    nPrevRecv = nRecv
    nPrevSend = nSend
    nPrevTime = nTime
EndFunc
[/cut]
P.s.
Обещал пересмотреть другие модули после праздников, но пока не получается. Теперь только в феврале, наверное, попробую еще раз взяться.

Добавлено: Ср фев 04, 2026 16:17
FallenAngel
Orion9
Команду, к примеру 64000 я не смог выполнить из меню Start. Не знает, так задумано?

Еще вопрос, мы можем выполнить команду для CompareTool= ? (секция [Configuration])

Добавлено: Чт фев 05, 2026 17:34
Orion9
FallenAngel
Не везде получится выполнить команду по коду. Если вызов не срабатывает, можно создать em_команду или отослать сообщение окну ТС.

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

[em_custom_name]
cmd=64000

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

%COMMANDER_PATH%\Utilites\TCFS2\TCFS2.exe /ef "tcm(64000,1)"
Второй вариант предподчительнее, так как не нужно вносить изменения в usercmd.ini.
мы можем выполнить команду для CompareTool= ?
А разве cm_CompareFilesByContent не подходит? Но если нужно выполнить эту команду из скрипта, тогда

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

SendCommand(2022)    # cm_CompareFilesByContent