Тема: AutoHotkey: обновлялка для DrWeb
Мне в качестве антивируса нравится DrWeb. И, разумеется, он имеет свою собственную обновлялку. Но многих, я знаю, она не устаивает. Кого-то потому, что не любит когда неизвестно кто качает и передаёт неизвестно что. Кого-то потому, что после "официального" обновления слетает "неправедная" регистрация. А кто-то хочет сэкономить трафик.
Вот в сети и существует немалое количество альтернативных обновлялок. Несколькими я пробовал пользоваться. Но ни одна из тех, что я юзал, не работала так, как мне было нужно (а иные и вовсе безбожно глючили).
Руководствуясь известной сентенцией "если хочешь, чтобы всё было как надо - сделай это сам" я и накатал свою обновлялку. Было это года два назад. С тех пор я её постоянно корректировал в связи с:
- изменениями в DrWeb'е и форме получения обновлений;
- эксплуатацией обновлялки моими друзьями и знакомыми;
- усилением возможностей AutoHotkey.
Короче, скрипт протестирован "будь здоров". Учтено множество разных разностей. Этим объясняется, как может показаться, избыточность вводных данных (настроек пользователя) и прочих всяких проверок. Зато теперь, при необходимости или желании, легко изменять поведение скрипта. Для этих целей в скрипте и оставлены отладочные строки. Однако можно ничего не настраивать и скрипт будет работать правильно и сразу (хочется верить).
Скрипт заточен под компиляцию (хотя это и необязательно). Перед компиляцией, рядом со скриптом нужно положить unzip.exe (его легко можно найти обычным поиском по вашему компьютеру или в сети). Готовый экзешник нужно положить в папку DrWeb'а. Более детальные комментарии можно найти внутри скрипта.
;*************************************************************************************************************
; AutoHotkey Version: 1.0.46.15+
; Автор: Androgen Belkin
; Имя скрипта: DrwUpdater.ahk (v.2.3)
;*************************************************************************************************************
; Обновление баз антивируса DrWeb.
; Скрипт нужно поместить в папку с файлами антивирусных баз
; (обычно базы лежат в папке с самим DrWeb'ом).
; Обновления (после загрузки) распакуются в папку с этим скриптом.
; Если скрипт будет находиться в папке DrWeb, то помимо баз
; будет проверяться и обновляться версия движка (drweb32.dll) и другие библиотеки.
;
; Скрипт использует встроенную в винду утилиту ftp.exe.
; Также используется UnZip.exe, который нужно положить рядом со скриптом.
; Я использую UnZip потому, что после компиляции скрипта
; он уже не зависит от наличия установленных распаковщиков.
;
; Разумеется, можно использовать и другой подходящий распаковщик, например, WinRar.
; Но тогда:
; - нужно изменить командную строку распаковщика (она приведена и закомментирована).
; - команду на листинг архива
; - рассмотреть уместность команды FileInstall и пяти последующих строчек.
; Короче, использовать UnZip проще. И он значительно меньше размером :)
;
; Работа скрипта проверялась, в основном, на WinXP. Но будет работать и на Win9x.
; Правда тогда не будут показываться всплывающие сообщения, но это на работу скрипта не влияет.
;
; Для отладки скрипта, используются строки со словом "ОТЛАДКА!!!".
;*************************************************************************************************************
#NoEnv ; запрещаем имена переменных как у переменных окружения
; ОТЛАДКА!!! Назначаем рабочую папку скрипта
;~ SetWorkingDir, %A_ProgramFiles%\DrWeb ; чтобы скрипт можно было держать в любом месте
;=============================================================================================================
; НАСТРОЙКИ ПОЛЬЗОВАТЕЛЯ:
Downloads_Dir = %A_Temp%\!DrwUpd ; куда ВРЕМЕННО закачивать архивы антивирусных баз (здесь их можно хранить)
Log_File = %A_WorkingDir%\DrwUpdater.log ; имя лога обновлений (можно не указывать, тогда он и не пишется)
; Файлы, у которых нужно проверять версии:
; Версии уже имеющихся баз проверяются по соответствующему файлу описания базы (например, drwtoday.txt).
; Если файла с описанием не существует, то база будет загружена безоговорочно (без проверки версии).
Check_Vers_vdb =
( LTrim Join| Comments ; LTrim - удалять пробелы в начале, соединять строки через "|", разрешить комменты
drwtoday ; свежие обновления
dwntoday ; nasty
dwrtoday ; risky
drwnasty ; базовый nasty
drwrisky ; базовый risky
;~ drwebase.vdb ; не имеет файла описания, и его версия не проверяется т.к. он не изменяется
)
; Библиотеки, у которых нужно проверять версии (версия берётся из них самих)
; Поскольку количество библиотек периодически меняется, возможно понадобится корректировать этот список.
Check_Vers_dll =
( LTrim Join| Comments
drweb32 ; движок
vrcpp ; спамовый фильтр
)
; теоретически могущие измениться "константы"
FTP_DrWeb = ftp.drweb.com ; адрес FTP-сервера DrWeb
FTP_Dir = pub/drweb/bases ; каталог на сервере, откуда будем брать базы
Base_List = info.txt ; имя файла на сервере со списком доступных к загрузке баз
; временные служебные файлы
New_Base_List = %Downloads_Dir%\DrWeb_New_Base.htm ; имя, под которым сохранять закачанный список баз
FTPCommandFile = %Downloads_Dir%\FTPCommands.txt ; имя временного файла для команд FTP
FTPLogFile = %Downloads_Dir%\FTPLog.txt ; имя временного файла для лога команд FTP
ArcList = %Downloads_Dir%\ArcList.txt ; имя временного файла для листинга архива
; файлы, которые нужно удалить по окончании работы скрипта
Del_Base := False ; удалять ли скачанные архивы
Del_Temp_Files = ; список удаляемых временных файлов
( LTrim Join| Comments
%FTPCommandFile%
%FTPLogFile%
%ArcList%
%Downloads_Dir%\UnZip.exe
;~ %New_Base_List%
)
; Ниже можно указать путь к ftp.exe. А можно не указывать, скорее всего этот файл есть в системе.
; Тогда он будет найден. Но я всё же оставил возможность указать путь. Так, на всякий случай.
;~ FTP_Exe = %A_ScriptDir%\ftp.exe ; путь к ftp.exe
; КОНЕЦ НАСТРОЕК ПОЛЬЗОВАТЕЛЯ
;=============================================================================================================
;=============================================================================================================
; ПРОВЕРИТЬ ПРАВИЛЬНОСТЬ ПУТИ ЗАПУСКА ОБНОВЛЯЛКИ (НАЛИЧИЕ АНТИВИРУСНЫХ БАЗ В ТЕКУЩЕЙ ПАПКЕ ПО МАСКЕ "*.vdb")
IfNotExist, *.vdb ; если файлы не найдены
{
MsgBox,, Неверная папка запуска!, ; показать сообщение
( LTrim
Поместите файл "%A_ScriptName%" в папку,
в которой находятся антивирусные базы.
Обычно это: "C:\Program Files\DrWeb".
)
ExitApp ; конец скрипта
}
; ПРОВЕРИТЬ СУЩЕСТВОВАНИЕ ПАПКИ, В КОТОРУЮ ВРЕМЕННО ЗАКАЧИВАЮТСЯ АРХИВЫ
IfNotExist, %Downloads_Dir% ; если папка не существует
{
FileCreateDir, %Downloads_Dir% ; пробуем создать папку
If ErrorLevel ; если папка не создалась (например, в настройках пользователя указан неверный путь)
{
MsgBox,, Внимание!, ; показать сообщение
( LTrim
Не могу создать папку для временных файлов:
"%Downloads_Dir%"`n
Без этого обновление не может быть выполнено!
)
ExitApp ; конец скрипта
}
}
; ПРОВЕРИТЬ НАЛИЧИЕ FTP.EXE по указанному пути ИЛИ В СИСТЕМЕ
IfNotExist, %FTP_Exe% ; если ftp.exe нет по указанному пути
{
FTP_Exe := FileExistInShell("ftp.exe") ; ищем ftp.exe в системе
If NOT FTP_Exe
{
MsgBox,, Внимание!, ; показать сообщение
( LTrim
Не найден файл "ftp.exe".
Он необходим, для загрузки обновлений.`n
Без него обновление не может быть выполнено!
)
ExitApp ; конец скрипта
}
}
;=============================================================================================================
; ПОЛУЧИТЬ СПИСОК НОВЫХ БАЗ
;~ /* ; ОТЛАДКА!!! НЕ ИДЁМ В ИНТЕРНЕТ (для отладки РАСкомментировать строчку, и её пару)
TrayTipText = Загружаю список антивирусных баз ; текст сообщения в трее
SetTimer, RefreshTrayTip, 1000 ; обновлять сообщение каждую секунду
Gosub, RefreshTrayTip ; показать сообщение в трее прямо сейчас
FileDelete, %FTPCommandFile% ; удаляем (возможно) оставшийся от предыдущих операций временный файл
FileDelete, %FTPLogFile% ; удаляем (возможно) оставшийся от предыдущих операций временный файл
; ПОЛУЧИТЬ СПИСОК НОВЫХ БАЗ
FileAppend, ; пишем команды FTP в файл
(LTrim Comments
open %FTP_DrWeb% ; сервер DrWeb'а
anonymous ; входить анонимно
binary ; бинарный режим передачи
get %FTP_Dir%/%Base_List% "%New_Base_List%" ; что и куда качаем
quit ; выход из FTP-режима
), %FTPCommandFile%
RunWait, %comspec% /c "%FTP_Exe% -s:"%FTPCommandFile%" > "%FTPLogFile%"",, Hide
; ПРОВЕРИТЬ НАЛИЧИЕ ПОДКЛЮЧЕНИЯ К ИНТЕРНЕТ (анализируем лог)
FileReadLine, FirstLine, %FTPLogFile%, 1
If InStr(FirstLine, FTP_DrWeb) ; если была ошибка соединения
{
MsgBox,, Внимание!, ; показать сообщение
( LTrim
Не могу выполнить обновление!`n
Возможно, нет подключения к Интернет.
Попробуйте запустить обновление позже.
)
Goto, Del_Temp ; идём удалять временные файлы (конец скрипта)
}
;~ */ ; ОТЛАДКА!!! НЕ ИДЁМ В ИНТЕРНЕТ (для отладки РАСкомментировать строчку, и её пару)
;=============================================================================================================
; ИЩЕМ НОВЫЕ БАЗЫ, СРАВНИВАЕМ С ИМЕЮЩИМИСЯ
IfNotExist, %New_Base_List% ; если скачанный файл почему-то не существует
{
MsgBox,, Внимание!, ; показать сообщение
( LTrim
Не найден загруженный с сервера DrWeb список баз "%New_Base_List%".
Возможно, во время обновления вы очистили папку "%Downloads_Dir%".`n
Обновление не может быть завершено!
)
Goto, Del_Temp ; идём удалять временные файлы (конец скрипта)
}
Loop, read, %New_Base_List% ; лопатим загруженный список баз
{
IfNotInString, A_LoopReadLine, %FTP_Dir% ; если в строке нет ссылки на архив
Continue ; читаем следующую строку
Base_Found := True ; флаг, что архивы в списке были найдены (чтобы знать, если обновлять ничего не надо)
RegExMatch(A_LoopReadLine, FTP_Dir "/(\S+).zip", Arc_Name), Arc_Name := Arc_Name1 ; выкусываем имя архива
Loop, parse, Check_Vers_vdb, | ; парсим список баз, требующих проверки версии
{
If Arc_Name = %A_LoopField% ; если база из тех, что требуют проверки версии
{
If NOT FileExist(Arc_Name ".txt") ; если описания базы или её самой не существует
OR NOT FileExist(Arc_Name ".vdb")
Goto, Add_To_Downloads ; добавляем базу в список безоговорочно (без проверки версии)
; описание существует, сравниваем версии имеющейся и новой баз
FileReadLine, SecondLine, %Arc_Name%.txt, 2 ; читаем вторую строку файла с описанием базы
RegExMatch(SecondLine, "\d+", Exist_Vers) ; получаем число записей в текущей базе
RegExMatch(A_LoopReadLine, "(\d+)\)$", New_Vers_vdb) ; получаем число запсей в новой базе
New_Vers_vdb := New_Vers_vdb1 ; берём нужную группу из RegExp'а
If (Exist_Vers != New_Vers_vdb1) ; если версии не совпадают
Goto, Add_To_Downloads ; добавляем базу в список загрузки
}
}
Loop, parse, Check_Vers_dll, | ; парсим список dll, требующих проверки версии
{
If Arc_Name = %A_LoopField% ; если архив из тех, что требуют проверки версии
{
If NOT FileExist("drweb386.exe") ; (базы лежат отдельно от DrWeb)
Goto, ContinueRead ; продолжить читать строки из списка баз
; dll существует, сравниваем версии имеющейся и новой dll
FileGetVersion, Exist_Vers_dll, %A_LoopField%.dll
RegExMatch(A_LoopReadLine, "\((\S+)\)$", New_Vers_dll ) ; получаем версию новой dll
New_Vers_dll := New_Vers_dll1 ; берём нужную группу из RegExp'а
If (Exist_Vers_dll = New_Vers_dll) ; если версии совпадают
Goto, ContinueRead ; продолжить читать строки из списка баз
; Версии не совпадают, проверим их по частям (обходим некорректную инфу с сервера DrWeb'а)
StringSplit, Exist_VersArray, Exist_Vers_dll, . ; делим версию на части
StringSplit, New_VersArray, New_Vers_dll, .
VersNotEqual = ; сбрасываем флаг
Loop, %New_VersArray0%
{
If (Exist_VersArray%A_Index% != New_VersArray%A_Index%)
VersNotEqual := True ; ставим флаг, что версии не равны
}
If NOT VersNotEqual ; если после проверки СОВПАДАЮТ
Goto, ContinueRead ; продолжить читать строки из списка баз
}
}
; База не требует проверки версии
If FileExist(Arc_Name ".vdb") ; если база уже существует...
Continue ; продолжить читать строки из загруженной страницы
Add_To_Downloads: ; метка перехода для составления списка загрузки
If NOT Downloads_List ; если список загрузки ещё пуст
Downloads_List = %Arc_Name%.zip ; начинаем список
Else
Downloads_List = %Downloads_List%`n%Arc_Name%.zip ; добавляем архив в список, через перевод строки
ContinueRead: ; метка перехода для продолжения чтения списка загрузки
Continue ; продолжить читать строки из загруженной страницы
}
Sort, Downloads_List ; сортируем список, чтобы drwtoday шёл после остальных drw(N), и те его не перезаписывали
If NOT Base_Found ; если ни одной ссылки на базы не было найдено
{
MsgBox,, Внимание!, ; показать сообщение
( LTrim
В файле, полученном с FTP-сервера DrWeb:
"%New_Base_List%"
не удалось найти ссылки на антивирусные базы.
Вероятно, изменилась структура этого файла.`n
Обратитесь к разработчику скрипта :)
)
Goto, Del_Temp ; идём удалять временные файлы (конец скрипта)
}
If NOT Downloads_List ; если список обновлений пуст
{
MsgBox,, Обновление не требуется!, ; показать сообщение
( LTrim
Текущие версии локальных файлов - самые свежие :).
)
Goto, Del_Temp ; идём удалять временные файлы (конец скрипта)
}
/* ; ОТЛАДКА!!! СМОТРИМ СПИСОК ЗАГРУЗКИ (для отладки РАСкомментировать строчку, и её пару)
MsgBox, Будут загружены следующие архивы:`n%Downloads_List%
ExitApp ; конец скрипта
*/ ; ОТЛАДКА!!! СМОТРИМ СПИСОК ЗАГРУЗКИ (для отладки РАСкомментировать строчку, и её пару)
;=============================================================================================================
; СКОПИРОВАТЬ UNZIP.EXE ВО ВРЕМЕННУЮ ПАПКУ (КУДА БУДУТ ЗАГРУЖЕНЫ НОВЫЕ БАЗЫ)
FileInstall, UnZip.exe, %Downloads_Dir%\UnZip.exe, 1
If ErrorLevel
{
MsgBox,, Внимание!, Неверно указан распаковщик. ; показать сообщение
Goto, Del_Temp ; идём удалять временные файлы (конец скрипта)
}
;=============================================================================================================
; ОБРАБОТАТЬ СПИСОК ДЛЯ ЗАГРУЗКИ
Loop, Parse, Downloads_List, `n ; обработать составленный список для загрузки (разделитель - перевод строки)
{
TrayTipText = Загружаю обновление: %A_LoopField% ; текст сообщения в трее
SetTimer, RefreshTrayTip, 1000 ; обновлять сообщение каждую секунду
Gosub, RefreshTrayTip ; показать сообщение в трее прямо сейчас
;~ /* ; ОТЛАДКА!!! НЕ ИДЁМ В ИНТЕРНЕТ (для отладки РАСкомментировать строчку, и её пару)
; ЗАГРУЗИТЬ НОВУЮ БАЗУ с FTP
FileDelete, %FTPCommandFile% ; удалить предыдущий файл с командами для FTP
FileAppend, ; пишем команды FTP в файл
(LTrim Comments
open %FTP_DrWeb% ; сервер DrWeb'а
anonymous ; входить анонимно
binary ; бинарный режим передачи
get %FTP_Dir%/%A_LoopField% "%Downloads_Dir%\%A_LoopField%" ; что и куда качаем
quit ; выход из FTP-режима
), %FTPCommandFile%
RunWait, %comspec% /c "%FTP_Exe% -s:"%FTPCommandFile%" > "%FTPLogFile%"",, Hide
; АНАЛИЗ ЛОГА ЗАГРУЗКИ (УСПЕШНО ИЛИ НЕТ)
Download_OK = ; сбросить указатель успешности загрузки
Loop, read, %FTPLogFile% ; читаем лог загрузки
{
IfInString, A_LoopReadLine, File send OK ; если в строке указано, что "File send OK"
{
Download_OK := True ; флаг, что загрузка прошла успешно
Break ; перестаём читать лог
}
}
;~ */ ; ОТЛАДКА!!! НЕ ИДЁМ В ИНТЕРНЕТ (для отладки РАСкомментировать строчку, её пару и 4 строки ниже)
/* ; ОТЛАДКА!!! ИМИТАЦИЯ ЗАГРУЗКИ (для отладки РАСкомментировать строчку, и её пару)
Sleep, 1000 ; имитация загрузки :)
Download_OK := True ; ОТЛАДКА!!!
; (проверка ошибок загрузки)
If (A_LoopField = "dwntoday.zip") OR (A_LoopField = "drwtoday.zip")
Download_OK =
*/ ; ОТЛАДКА!!! ИМИТАЦИЯ ЗАГРУЗКИ (для отладки РАСкомментировать строчку, и её пару)
If NOT Download_OK ; если была ошибка загрузки
{
If NOT Log_Error ; если лог ошибок пуст
Log_Error = %A_LoopField%
Else
Log_Error = %Log_Error%`n%A_LoopField%
Continue ; пробуем скачать следующую базу
}
; РАСПАКОВАТЬ ЗАГРУЖЕННУЮ БАЗУ
;~ UnPacker = %A_ProgramFiles%\WinRAR\WinRAR.exe ; использовать этот распаковщик
;~ RunWait, %UnPacker% e -O+ "%Downloads_Dir%\%A_LoopField%" "%A_WorkingDir%",, Hide ; WinRar
RunWait, %Downloads_Dir%\UnZip.exe -o "%Downloads_Dir%\%A_LoopField%" -d "%A_WorkingDir%",, Hide
; параметры UnZip'а
; -o - перезаписывать имеющиеся файлы (параметр должен быть перед именем архива)
; имя распаковываемого архива
; -d - в какую папку распаковывать
; архив распакован, смотрим его содержимое чтобы записать в лог
RunWait, %comspec% /c "%Downloads_Dir%\UnZip.exe -l "%Downloads_Dir%\%A_LoopField%" > "%ArcList%"",, Hide
FileReadLine, ArcListLine, %ArcList%, 4 ; читаем 4-ю строку файла (первый файл в листинге)
RegExMatch(ArcListLine, "\w+$", Ext) ; получаем расширение файла
StringReplace, CurFile, A_LoopField, .zip, .%Ext% ; меняем расширение на соответствующее
If NOT Log_Success ; если лог успешных операций пуст
Log_Success = %CurFile%
Else
Log_Success = %Log_Success%`n%CurFile%
; УДАЛЯЕМ РАСПАКОВАННЫЙ АРХИВ
If Del_Base ; если указано удалять архивы
FileDelete, %A_LoopField% ; мочим архив
}
; Загрузки завершены, убираем сообщение в трее
SetTimer, RefreshTrayTip, Off
TrayTip ; удалить сообщение в трее
SoundPlay, %A_WinDir%\Media\ding.wav ; воспроизвести звук
;=============================================================================================================
; ПИШЕМ ЛОГ ОБНОВЛЕНИЙ
If Log_File ; если указано писать лог
{
; делаем так, чтобы последняя запись была наверху лога
Tmp_Log = %Log_File%.tmp ; сначала будем писать во временный лог
FileAppend,
(LTrim Comments
======================================
%A_DD%-%A_MMMM%-%A_YYYY%, %A_Hour%:%A_Min%:%A_Sec%
--------------------------------------
), %Tmp_Log%
If Log_Success ; если есть успешные операции
{
FileAppend,
(LTrim Comments
`nУстановлено обновление:
%Log_Success%
), %Tmp_Log%
}
If Log_Error ; если есть ошибки закачки
{
FileAppend,
(LTrim Comments
`n`n!!!--- ВНИМАНИЕ ---!!!
Эти файлы не удалось загрузить:
%Log_Error%
), %Tmp_Log%
}
FileAppend, `n======================================`n`n`n, %Tmp_Log%
FileRead, Last_Log, %Log_File% ; читаем настоящий лог
FileAppend, %Last_Log%, %Tmp_Log% ; добавляем его ко временному
FileMove, %Tmp_Log%, %Log_File%, 1 ; заменяем настоящий временным
}
If Log_Error ; если есть ошибки закачки
{
IfExist, %Log_File% ; если лог существует
{
MsgBox, 20, Внимание!, Обновление завершено с ошибками.`nПоказать лог обновления ?
IfMsgBox, Yes
{
Run, Notepad.exe "%Log_File%",,, Log_PID ; открываем лог в блокноте
WinWait, ahk_pid %Log_PID% ; подождать окно лога
WinActivate ; активизировать окно
}
}
Else ; если лог не пишется
MsgBox, 16, Внимание!, Обновление завершено с ошибками.
}
Else ; если ошибок не было
{
IfExist, %Log_File% ; если лог существует
{
MsgBox, 260, Поздравляю!, Обновление успешно завершено.`nПоказать лог обновления ?
IfMsgBox, Yes
{
Run, Notepad.exe "%Log_File%",,, Log_PID ; открываем лог в блокноте
WinWait, ahk_pid %Log_PID% ; подождать окно лога
WinActivate ; активизировать окно
}
}
Else ; если лог не пишется
MsgBox,, Поздравляю!, Обновление успешно завершено.
}
Del_Temp: ; удаление временных файлов
Loop, Parse, Del_Temp_Files, | ; парсим список временных файлов
FileDelete, %A_LoopField% ; удаляем временные файлы
ExitApp ; конец скрипта
;
; ============================================================================================================
RefreshTrayTip: ; подпрограмма обновления TrayTip'а
TrayTip, Обновление..., %TrayTipText%
Return
; ============================================================================================================
; ============================================================================================================
FileExistInShell(Target_File) ; функция проверки возможности запуска файла
{
; В качестве параметра функция принимает имя файла (с расширением!)
; Если файл найден, то функция возвращает путь к нему.
; Обычно имеет смысл проверять исполнимые файлы, но это не обязательно :)
; Сначала ищем в App Paths
Loop, HKEY_LOCAL_MACHINE, SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths, 2, 1 ; ищем путь в реестре
{
If (A_LoopRegName != Target_File) ; если текущий раздел не тот, что ищем
Continue
RegRead, Target_Path, %A_LoopRegKey%, %A_LoopRegSubKey%\%A_LoopRegName% ; читаем парам. "по умолчанию"
IfExist, %Target_Path% ; если файл, указанный в реестре существует
Return, %Target_Path% ; возвращаем путь к файлу
Else ; если вдруг файл по указанному пути не существует
Break ; больше в реестре не ищем
}
; В App Paths не нашли, ищем в системных папках
IfExist, %A_WinDir%\System32\%Target_File% ; если нашли файл
Return, A_WinDir "\System32\" Target_File ; возвращаем путь к файлу
IfExist, %A_WinDir%\%Target_File% ; если нашли файл
Return, A_WinDir "\" Target_File ; возвращаем путь к файлу
; Напоследок ищем в переменной окружения Path
EnvGet, EnvPath, Path ; получаем переменную окружения Path
StringSplit, EnvPathArray, EnvPath, `; ; разбиваем на части в массив
Loop, %EnvPathArray0% ; проверяем полученные пути
{
Target_Path := % EnvPathArray%A_Index% "\" Target_File ; составляем путь
IfExist, %Target_Path% ; если нашли файл
Return, %Target_Path% ; возвращаем путь к файлу
}
Return ; выходим из функции (не нашли файл)
} ; конец функции
; ============================================================================================================
Гы. Архив приложил, а сам код не запостил. Исправил.
Забрать скрипт со значком для компиляции.