Тема: AutoHotkey и Total Uninstall
Есть такая совершенно изумительная прожка - Total Uninstall. Одна из самых используемых мной. Отслеживает изменения в файловой системе и реестре. Предназначена, как явствует из названия, для полного и корректного удаления приложений, установка которых была отслежена с помощью этой прожки. В этом она похожа на всем, наверное, известную Ashampoo UnInstaller. Только работает намного быстрее и корректнее, а главное в разы более удобна и настраиваема (ИМХО, разумеется). На мой взгляд, такого же удобного и функционального инструмента для поиска изменений просто не существует. (А поскольку тема отслеживания изменений интересует меня уже несколько лет, то, я полагаю, что перепробовал уже, видимо, все прожки на эту тему).
Лично я почти не использую Total Uninstall по его прямому назначению. Гораздо, гораздо чаще я ищу этой прогой изменения, производимые какой-нибудь другой прогой (или мной лично). Например, так: запускаю TU, делаю снимок. Что-нибудь меняю (ну, скажем, некую настройку рабочего стола). Делаю второй снимок, получаю список изменений. Если изменения были, допустим, в реестре, то можно сохранить файл реестра с зафиксированными изменениями. Теперь я могу воткнуть эту настройку на других компах (ну или использовать как-нибудь ещё).
Одной фишки мне не хватало: нельзя так же легко, как изменения в реестре, сохранить добавленные-измененные файлы (для последующего использования). Вот и написал я скрипт (уже, наверное, с год назад). Хотел, прежде чем выложить его, доделать кое-что, но заленился. Выкладываю в таком недоделанном (но полностью работоспособном) виде.
Приведу ОДИН примерчик применения скрипта. Вот поставил я какую-нибудь игруху (я их ставлю на отдельный раздел). Разумеется, отмониторил инсталляцию. Вижу, что игруха (вот собака, поубивал бы) набросала dll-шек в system32. Запускаю скрипт, и он мне сохраняет все накиданные dll-шки отдельно. Бросаю эти dll-шки в папку с игрухой. Теперь, когда я переустановлю систему (или восстановлюсь из образа), игруха будет запускаться без всяких вопросов и возмущений, что, мол, ей чего-то не хватает.
Короче, прога мне нравится, но FAQ по её использованию писать не хочется. Сама прога живет здесь: http://www.martau.com/tu.php, всякое про неё можно почитать здесь: http://forum.ru-board.com/topic.cgi?for … opic=24554.
Вот скрипт. Будут замечания, пожелания - не стесняйтесь.
Скрипт сохраняет (для отслеженного TU приложения):
- созданные/изменённые файлы/папки
- файл реестра с созданными/изменёнными/удалёнными разделами и параметрами
Папка для сохранения задаётся в настройках скрипта, а для откомпилированного скрипта - в командной строке.
Приложение будет сохранено в указанную папку, в подпапку с именем сохраняемого приложения.
Вдобавок, при сохранении, можно создавать отдельные подпапки для созданных и для измененных файлов.
Работает скрипт так:
1. Запускаем Total Uninstall
2. Ставим курсор на имя сохраняемого приложения (слева в списке отслеженных приложений)
а) если желаем сохранять не целиком приложение, а только какие-то его части, то
ставим курсор (справа) на сохраняемые файл/папку или раздел/параметр реестра
3. Запускаем этот скрипт
4. Получаем желаемый результат
Скрипт тестировался на Total Uninstall v3.70 - v3.83.
;*****************************************************************************
; AutoHotkey Version: 1.0.46.15+
; Автор: Androgen Belkin
; Имя скрипта: SaveApp4TU.ahk (v.1.4)
;
; Тестировалось на Total Uninstall v3.70 - v3.83.
;*****************************************************************************
; Сохранить приложение, отслеженное Total Uninstall`ом.
; Скрипт сохраняет (для отслеженного приложения):
; - созданные/изменённые файлы/папки
; - файл реестра с созданными/изменёнными/удалёнными разделами и параметрами
;
; Папка для сохранения задаётся в настройках скрипта, а для откомпилированного скрипта - в командной строке.
; Приложение будет сохранено в указанную папку, в подпапку с именем сохраняемого приложения.
;
; Порядок использования скрипта:
; 1. Запускаем Total Uninstall
; 2. Ставим курсор на имя сохраняемого приложения (слева в списке отслеженных приложений)
; а) если желаем сохранять не целиком приложение, а только какие-то его части, то
; ставим курсор (справа) на сохраняемые файл/папку или раздел/параметр реестра
; 3. Запускаем этот скрипт
; 4. Получаем желаемый результат :)
;*****************************************************************************
;~ #NoTrayIcon ; не отображать значок скрипта в трее
#NoEnv ; запрещаем имена переменных как у переменных окружения
#SingleInstance force ; перезапускаем скрипт без вопросов
/*
Что сделать:
1) дополнить скриптом для копирования сохранённого в исходное расположение
2) изменить (расширить) варианты сохранения через переменные окружения в путях
(чтобы отвязать от профиля пользователя и т.д.)
*/
;-------------------------------------------------------------------------------
; НАСТРОЙКИ ПОЛЬЗОВАТЕЛЯ
KeepSetCompile := ;True ; сохранять заданные ниже настройки в скомпилированном скрипте,
; ...или использовать переданные в ком. строке (используется для компиляции скрипта с текущими настройками)
Mon_App_Path = D:\Рабочая папка\Сохраненное через Total Uninstall ; путь к папке, куда сохранять отслеженные приложения
Create_Sub_Dir := True ; создавать ли отдельные подпапки для созданных и для измененных файлов (True/False)
Del_Reg_Check := True ; ставить ли галочку на "удаленные ключи" при сохранении файла реестра (True/False)
Log_Name = SaveApp4TU.log ; имя файла лога операций скрипта
Separator = ==================================================================== ; разделитель для визуальной отбивки в логе
; КОНЕЦ НАСТРОЕК ПОЛЬЗОВАТЕЛЯ
;-------------------------------------------------------------------------------
;-------------------------------------------------------------------------------
; ЗАДАНИЕ ПЕРЕМЕННЫХ ПОД АНГЛИЙСКИЙ И РУССКИЙ ИНТЕРФЕЙС TU
; Внимание! Переменные под En и Ru интерфейсы могут измениться в будущих версиях TU. Возможно, их понадобится изменить.
; Кроме того, ниже знаками (!!!) отмечены могущие измениться места интерфейса.
MonApp_En = Monitored Applications ; текст контрола при английском интерфейсе
MonApp_Ru = Отслеженные приложения ; текст контрола при русском интерфейсе
; ПЕРЕМЕННЫЕ ПОД АНГЛИЙСКИЙ ИНТЕРФЕЙС TU (задаются по умолчанию)
wFolder = (FOLDER) ; какое слово ищем в файле изменений
wFile = (FILE) ; какое слово ищем в файле изменений
Allow_Details = All Details,Created Items,Deleted Items,Changed Items ; разрешенные названия подробностей
nDetails = All Details ; нужное название подробностей
wSelection = (Selection) ; слово в файле изменений (сохранены не все, а выделенные изменения)
wView = FILE SYSTEM DETAILS ; часть служебной строки, где указано, что сохранено все, или только выделенное
wReg = REGISTRY DETAILS ; часть служебной строки, откуда начинается лог изменений реестра
; ПЕРЕМЕННЫЕ ПОД РУССКИЙ ИНТЕРФЕЙС TU (задаются вызовом функции)
ChangeVars() ; функция изменения значений переменных под русский интерфейс
{
Global ; все переменные функции будут глобальными
wFolder = (ПАПКА) ; какое слово ищем в файле изменений
wFile = (ФАЙЛ) ; какое слово ищем в файле изменений
Allow_Details = Показать все,Созданные элементы,Удаленные элементы,Измененные элементы ; разрешенные названия подробностей
nDetails = Показать все ; нужное название подробностей
wSelection = (Выделение) ; слово в файле изменений (сохранены не все, а выделенные изменения)
wView = ПОДРОБНОСТИ О ФАЙЛОВОЙ ; часть служебной строки, где указано, что сохранено все, или только выделенное
wReg = ДЕТАЛИ РЕЕСТРА ; часть служебной строки, откуда начинается лог изменений реестра
Return ; выходим из функции
} ; конец функции
; КОНЕЦ ЗАДАНИЯ ПЕРЕМЕННЫХ
;-------------------------------------------------------------------------------
If A_IsCompiled ; если скрипт скомпилирован
{
If NOT KeepSetCompile ; если настройки, указанные в скрипте не используются
{
; ПРОВЕРКА НАЛИЧИЯ ПЕРЕДАННЫХ ПАРАМЕТРОВ
If 0 = 0 ; если скрипт запущен без параметров
{
MsgBox, 0, SaveApp4TU -- by Androgen Belkin©,
( LTrim
Скрипт следует запускать с параметром:
/dir=path где path - путь к папке, в которую будет сохраняться выбранное приложение.
%A_Space% (Если путь содержит пробелы, то его нужно заключить в кавычки).
Приложение будет сохранено в указанную папку, в подпапку с именем сохраняемого приложения.
Дополнительные параметры:
/sd создавать отдельные подпапки для созданных и для изменённых файлов
/rd писать в создаваемый файл реестра и удалённые разделы реестра
)
ExitApp ; конец скрипта
}
Mon_App_Path = ; сбрасываем путь к папке, куда сохранять отслеженные приложения
Create_Sub_Dir = ; сбрасываем "создавать отдельные подпапки для созданных и для измененных файлов"
Del_Reg_Check = ; сбрасываем "ставить ли галочку на "удаленные ключи" при сохранении файла реестра"
Loop, %0% ; лопатим полученную ком. строку
{
Param := %A_Index% ; берем каждый параметр (чтобы далее не париться с синтаксисом)
If ( Param = "/sd" )
Create_Sub_Dir := True ; создавать отдельные подпапки для созданных и для измененных файлов
If ( Param = "/rd" )
Del_Reg_Check := True ; ставить галочку на "удаленные ключи" при сохранении файла реестра
If ( InStr( Param, "dir=" ) = 1 ) OR ( InStr( Param, "dir=" ) = 2 ) ; если стоит в начале (или после "/")
{
StringTrimLeft, Mon_App_Path, Param, InStr( Param, "dir=" ) + 3 ; отрезаем идентификатор
StringRight, RightSymbol, Mon_App_Path, 1 ; берем последний символ
If RightSymbol = \ ; если последний символ в строке бэкслэш "\", то...
StringTrimRight, Mon_App_Path, Mon_App_Path, 1 ; удалить последний символ ("\")
}
}
}
}
Mon_App_Path := ExpandEnvVars( Mon_App_Path ) ; разворачиваем переменные окружения в пути
; ОПРЕДЕЛЯЕМ ЗАПУЩЕН ЛИ TU
Process, Exist, Tu.exe ; проверить существование процесса
TU_PID = %ErrorLevel% ; запоминаем PID (понадобится далее)
If TU_PID = 0 ; если процесс НЕ существует, то...
{
MsgBox, 0, SaveApp4TU -- by Androgen Belkin©,
( LTrim
Не запущена программа Total Uninstall.
Порядок использования скрипта:
1. Запускаем Total Uninstall
2. Ставим курсор на имя сохраняемого приложения (слева в списке отслеженных приложений)
%A_Tab%а) если желаем сохранять не целиком приложение, а только какие-то его части, то
%A_Tab%ставим курсор (справа) на сохраняемые файл/папку или раздел/параметр реестра
3. Запускаем этот скрипт
)
ExitApp ; конец скрипта
}
; ОПРЕДЕЛЯЕМ НЕ НАХОДИТСЯ ЛИ TU В РАБОТЕ (создает/сравнивает снимки)
IfWinExist, ahk_class TfrmInstall.UnicodeClass ; если существует окно снимков (Total Uninstall работает), то...
{
Text = Программа Total Uninstall находится в процессе создания снимков.`nСкрипт прерывает свою работу.
My_MsgBox( "SaveApp4TU -- by Androgen Belkin©", Text ) ; вызываем функцию отображения MsgBox'а с текстом по центру
ExitApp ; конец скрипта
}
; ОПРЕДЕЛЯЕМ СУЩЕСТВУЕТ ЛИ ГЛАВНОЕ ОКНО TU (на всякий случай)
DetectHiddenWindows, On ; искать в скрытых окнах...
IfWinNotExist, ahk_class TfrmTUMain.UnicodeClass ; если главное окно не существует, то...
{
Text = Не могу найти главное окно программы Total Uninstall.`nСкрипт прерывает свою работу.
My_MsgBox( "SaveApp4TU -- by Androgen Belkin©", Text ) ; вызываем функцию отображения MsgBox'а с текстом по центру
ExitApp ; конец скрипта
}
; ОПРЕДЕЛЯЕМ ЯЗЫК ИНТЕРФЕЙСА
; Получить текст контрола (Отслеженные приложения или Monitored Applications)
ControlGetText, Interface_Lang, TTntPanel.UnicodeClass3, ahk_class TfrmTUMain.UnicodeClass ; (!!!)
If ( Interface_Lang != MonApp_En AND Interface_Lang != MonApp_Ru ) ; если язык не тот, что нужно, то...
{
Text =
( LTrim
Интерфейс Total Uninstall НЕ АНГЛИЙСКИЙ и НЕ РУССКИЙ.
Скрипт рассчитан на работу только с этими языками интерфейса.
Скрипт прерывает свою работу.
)
My_MsgBox( "SaveApp4TU -- by Androgen Belkin©", Text ) ; вызываем функцию отображения MsgBox'а с текстом по центру
ExitApp ; конец скрипта
}
If Interface_Lang = %MonApp_Ru% ; если язык - русский, то...
ChangeVars() ; вызываем функцию изменения значений переменных под русский интерфейс
; ПОЛУЧАЕМ ИМЯ ПРИЛОЖЕНИЯ (и текущий вид подробностей изменений)
ControlGetText, App_Name_and_Details, TTitlePanel.UnicodeClass1, ahk_class TfrmTUMain.UnicodeClass ; (!!!)
; ИЗВЛЕКАЕМ ТЕКУЩИЙ ВИД ПОДРОБНОСТЕЙ ИЗМЕНЕНИЙ
StringGetPos, Pos_L_Bracket, App_Name_and_Details, [, R ; получаем позицию символа "[" в строке
StringGetPos, Pos_R_Bracket, App_Name_and_Details, ], R ; получаем позицию символа "]" в строке
StringMid, Cur_Details, App_Name_and_Details, % Pos_L_Bracket + 2, % Pos_R_Bracket - Pos_L_Bracket - 1 ; текущий вид подробностей
; ПРЕДУПРЕДИТЬ, ЕСЛИ ВИД ПОДРОБНОСТЕЙ ИЗМЕНЕНИЙ НЕ "ПОКАЗАТЬ ВСЕ"
;~ If Cur_Details contains %Allow_Details% ; если текущий вид подробностей из списка разрешенных названий подробностей, то...
;~ {
If ( Cur_Details != nDetails ) ; если вид не "Показать все", то...
{
Text =
( LTrim
Текущий вид подробностей изменений:
"%Cur_Details%".
БУДУТ СОХРАНЕНЫ НЕ ВСЕ ОТСЛЕЖЕННЫЕ ИЗМЕНЕНИЯ.
Продолжить?
)
Button := My_MsgBox( "SaveApp4TU -- by Androgen Belkin©", Text, 1 ) ; вызываем функцию отображения MsgBox'а с текстом по центру
If Button != OK ; если не ОК, то...
ExitApp ; конец скрипта
}
;~ }
;~ Else ; если текущий вид подробностей неверен (значит скобки "[]" в строке - часть имени (это косяк TU))
;~ Pos_L_Bracket := StrLen( App_Name_and_Details ) ; устанавливаем положение скобки на всю длину имени (чтобы ниже ничего не отрезалось)
; ПОЛУЧИТЬ ИМЯ ОТСЛЕЖЕННОГО ПРИЛОЖЕНИЯ
StringLeft, Mon_App_Name, App_Name_and_Details, Pos_L_Bracket ; получить имя отслеженного приложения
Mon_App_Name = %Mon_App_Name% ; избавляемся от начальных и/или конечных пробелов в имени
; ПРОВЕРИТЬ КОРРЕКТНОСТЬ ПОЛУЧЕННОГО ИМЕНИ
If Mon_App_Name = ; если имя пустое (состоит из пробелов)
{
Mon_App_Name_Temp = %A_DD%-%A_MMM%-%A_YYYY%_%A_Hour%.%A_Min%.%A_Sec% ; имя папки будет текущая дата-время
Text =
( LTrim
Не задано имя приложения (возможно, имя состоит из пробелов).
Приложение будет сохранено с именем "%Mon_App_Name_Temp%".
Продолжить?
)
Button := My_MsgBox( "SaveApp4TU -- by Androgen Belkin©", Text, 1 ) ; вызываем функцию отображения MsgBox'а с текстом по центру
If Button != OK ; если не ОК, то...
ExitApp ; конец скрипта
Mon_App_Name = %Mon_App_Name_Temp% ; фиксируем новое имя
}
If Mon_App_Name contains \,*,/,:,?,`",<,> ; если имя содержит недопустимые символы
{
Mon_App_Name_Temp = %A_DD%-%A_MMM%-%A_YYYY%_%A_Hour%.%A_Min%.%A_Sec% ; имя папки будет текущая дата-время
Text =
( LTrim
Имя: "%Mon_App_Name%" содержит недопустимые символы \*/?:"<>.
Приложение будет сохранено с именем "%Mon_App_Name_Temp%".
Продолжить?
)
Button := My_MsgBox( "SaveApp4TU -- by Androgen Belkin©", Text, 1 ) ; вызываем функцию отображения MsgBox'а с текстом по центру
If Button != OK ; если не ОК, то...
ExitApp ; конец скрипта
Mon_App_Name = %Mon_App_Name_Temp% ; фиксируем новое имя
}
If Mon_App_Name contains | ; если имя содержит "особо недопустимый" символ
{
Text =
( LTrim
Имя сохраняемого приложения: "%Mon_App_Name%".
Total Uninstall отказывается сохранять изменения для приложения,
в имени которого содержится символ "|".
Переименуйте приложение, и запустите скрипт снова.
Скрипт прерывает свою работу.
)
My_MsgBox( "SaveApp4TU -- by Androgen Belkin©", Text ) ; вызываем функцию отображения MsgBox'а с текстом по центру
ExitApp ; конец скрипта
}
; СОЗДАТЬ ПАПКУ ДЛЯ СОХРАНЕНИЯ ОТСЛЕЖЕННОГО ПРИЛОЖЕНИЯ
Mon_App_Path = %Mon_App_Path%\%Mon_App_Name% ; путь к папке, куда сохранять отслеженное приложение (начало - в настройках пользователя)
; Проверяем папку для сохранений на возможное существование
IfExist, %Mon_App_Path% ; если папка для сохранений УЖЕ существует, то...
{
Text = Уже существует папка:`n"%Mon_App_Path%".`n`nПерезаписать?
Button := My_MsgBox( "SaveApp4TU -- by Androgen Belkin©", Text, 1, 2 ) ; вызываем функцию отображения MsgBox'а с текстом по центру
If Button != OK ; если не ОК, то...
ExitApp ; конец скрипта
; если нажата "ОК"
FileRemoveDir, %Mon_App_Path%, 1 ; удалить папку со всем содержимым
If ErrorLevel ; если удалить папку почему-либо не удалось, то...
{
Text = НЕ УДАЕТСЯ УДАЛИТЬ ПАПКУ:`n"%Mon_App_Path%".`nСкрипт прерывает свою работу.
My_MsgBox( "SaveApp4TU -- by Androgen Belkin©", Text ) ; вызываем функцию отображения MsgBox'а с текстом по центру
ExitApp ; конец скрипта
}
Loop ; подождать завершения удаления
{
Sleep, 50 ; небольшая пауза
IfNotExist, %Mon_App_Path% ; если папка для сохранений УЖЕ НЕ существует, то...
Break ; выходим из цикла
}
}
; Создаем папку
FileCreateDir, %Mon_App_Path% ; создать папку для сохранений
If ErrorLevel ; если папка почему-либо не создалась (например, в настройках пользователя указан неверный путь), то...
{
Text = Не могу создать папку:`n"%Mon_App_Path%"`nСкрипт прерывает свою работу.
My_MsgBox( "SaveApp4TU -- by Androgen Belkin©", Text ) ; вызываем функцию отображения MsgBox'а с текстом по центру
ExitApp ; конец скрипта
}
; Показываем сообщение о работе скрипта
SplashImage,, FS10 B1 CT000080, Работаю...`nПожалуйста`, подождите.
; СОХРАНЯЕМ ЛОГ TU
; Выбрать пункт меню "Извлечь -> Изменения"
WinMenuSelectItem, ahk_class TfrmTUMain.UnicodeClass,, 1&, 8&, 1& ; (!!!) выбрать пункт меню Файл -> Извлечь -> Изменения
WinWait, ahk_class #32770 ahk_pid %TU_PID%,, 6 ; ждем окно "Сохранить как", назначаем его последним найденным (сейчас понадобится)
If ErrorLevel
{
Text = Не дождались нужного окна!`n`nСкрипт прерывает свою работу.
My_MsgBox( "SaveApp4TU -- by Androgen Belkin©", Text ) ; вызываем функцию отображения MsgBox'а с текстом по центру
ExitApp ; конец скрипта
}
; Сохранить файл изменений для отслеженного приложения
Changes_File_Path = %A_Temp%\%Mon_App_Name%.txt ; составить путь к сохраняемому файлу изменений
IfExist, %Changes_File_Path% ; если файл уже существует
FileDelete, %Changes_File_Path% ; удалить его (чтобы не подтверждать запрос на перезапись)
Loop ; убеждаемся, что путь к сохраняемому файлу вставлен правильно
{
ControlSetText, Edit1, %Changes_File_Path% ; вставить путь к сохраняемому файлу
Sleep, 10*A_index ; увеличивающаяся пауза, чтобы путь успевал вставиться
ControlGetText, Changes_File_Path_New, Edit1 ; получить имя сохраняемого файла изменений
If Changes_File_Path_New = %Changes_File_Path% ; если имя (и путь) вставились нормально, то...
Break ; конец цикла
}
Loop ; убеждаемся, что подтверждение принято
{
ControlSend, Button2, {Space} ; нажать ОК
Sleep, 10*A_index ; увеличивающаяся пауза, чтобы окно успевало среагировать
IfWinNotExist ; если окно сохранения файла уже НЕ существует, то...
Break ; конец цикла
}
Loop ; убеждаемся, что файл изменений создан
{
IfExist, %Changes_File_Path% ; если файл существует, то...
Break ; конец цикла
}
Loop ; убеждаемся, что сохранение файла изменений завершено
{
FileGetSize, File_Sise, %Changes_File_Path% ; получаем размер файла изменений
If File_Sise_Bak = %File_Sise% ; если текущий размер файла равен предыдущему размеру, то...
Break ; конец цикла
Sleep, 50 ; небольшая пауза
File_Sise_Bak = %File_Sise% ; запоминаем предыдущий размер файла изменений
}
; СОХРАНЯЕМ ОТСЛЕЖЕННЫЕ ИЗМЕНЕНИЯ В ФАЙЛОВОЙ СИСТЕМЕ
; --------------------------------------------------
Loop, Read, %Changes_File_Path% ; читаем файл изменений
{
Cur_String = %A_LoopReadLine% ; избавляемся от начальных и/или конечных пробелов в строке
; определяем сохраняется ли все или только выделенное
IfInString, Cur_String, %wView% ; находим нужную служебную строку
{
IfInString, Cur_String, %wSelection% ; если в ней указано, что сохранено только выделенное, то...
{
OnlySelection = True ; добавляем текст к финальному сообщению (в его начало)
Selection_Log = СОХРАНЯЛИСЬ ТОЛЬКО ВЫДЕЛЕННЫЕ ПУНКТЫ ; добавляем текст в лог
}
; Получить текущий вид подробностей изменений (данные берутся из файла изменений, т.к. иногда их нет в основном окне TU)
StringGetPos, Pos_L_Bracket, Cur_String, [, R ; получаем позицию символа "[" в строке
StringGetPos, Pos_R_Bracket, Cur_String, ], R ; получаем позицию символа "]" в строке
StringMid, Cur_Details, Cur_String, % Pos_L_Bracket + 2, % Pos_R_Bracket - Pos_L_Bracket - 1 ; текущий вид подробностей
}
; определяем нужно ли сохранять реестр
IfInString, Cur_String, %wReg% ; находим нужную служебную строку
wRegN = %A_index% ; запоминаем номер этой служебной строки
If ( A_index = wRegN + 2 AND Cur_String = "" ) ; если здесь уже должны быть разделы/параметры реестра а их нет, то...
Reg_Empty = 1 ; выставляем флаг, что отсутствуют изменения реестра (реестр сохранять не надо)
; определяем положение служебных символов
Pos_Deleted := InStr( Cur_String, "(-)" ) ; получаем в строке положение знака удаления (если он там есть)
Pos_Created := InStr( Cur_String, "(+)" ) ; получаем в строке положение знака создания (если он там есть)
Pos_Modified := InStr( Cur_String, "(*)" ) ; получаем в строке положение знака изменения (если он там есть)
; пропускаем удаленные файлы/папки
If Pos_Deleted = 1 ; если строка начинается со знака удаления файла/папки, то...
Continue ; перейти к следующей строке ( не обрабатывать эту строку)
; обрабатываем исходные папки
Pos_wFolder := InStr( Cur_String, wFolder ) ; получаем позицию слова "(ПАПКА)" в строке
If ( Pos_wFolder = 1 OR Pos_wFolder = 4 ) ; если строка "начинается" со слова "(ПАПКА)" (или (+)(ПАПКА)), то...
{
StringTrimLeft, Cur_Dir, Cur_String, Pos_wFolder + StrLen( wFolder ) ; отрезаем начальную служебную инфу, получаем имя исходной папки
Continue ; перейти к следующей строке (если в следующей строке - снова папка, то текущая папка пуста и копироваться не будет)
}
; обрабатываем исходные файлы
Pos_wFile := InStr( Cur_String, wFile ) ; получаем позицию слова "(ФАЙЛ)" в строке
If Pos_wFile = 4 ; если строка "начинается" со слова "(ФАЙЛ)" (со служебными знаками), то...
{
; получаем имя исходного файла
StringTrimLeft, Cur_File, Cur_String, Pos_wFile + StrLen( wFile ) ; отрезаем начальную служебную инфу
; если файл был изменен, то этого достаточно, т.к. останется только имя файла (служебная инфа - в следующей строке)
; если файл был добавлен, то после имени файла будет еще служебная инфа, поэтому ниже мы ее удаляем
If Pos_Created = 1 ; если файл был добавлен (гарантируем, что не испортим имя файла, имеющее знак " = "), то...
{
Pos_equal := InStr( Cur_File, " = ","", 0 ) ; получаем позицию последнего знака " = " в строке
StringLeft, Cur_File, Cur_File, Pos_equal - 1 ; выбираем из строки окончательное имя исходного файла (до служебной инфы)
}
; проверяем нет ли косяка TU (иногда, при экспорте изменений в файл, он не пишет исходную папку)
If Cur_Dir = ; если текущая папка не указана, то...
{
Text =
( LTrim
Программа Total Uninstall ОШИБЛАСЬ ПРИ ЭКСПОРТЕ ИЗМЕНЕНИЙ В ФАЙЛ.
В созданном TU логе изменений, не указана исходная папка, откуда должны копироваться файлы.
Попробуйте сохранить другое приложение, а затем опять попытаться сохранить "%Mon_App_Name%"
Скрипт прерывает свою работу.
)
My_MsgBox( "SaveApp4TU -- by Androgen Belkin©", Text ) ; вызываем функцию отображения MsgBox'а с текстом по центру
FileRemoveDir, %Mon_App_Path%, 1 ; удалить созданную папку для изменений со всем содержимым
ExitApp ; конец скрипта
}
; проверяем существование исходного файла
IfNotExist, %Cur_Dir%\%Cur_File% ; если не существует исходный файл
{
If Log_Error_Exists = ; если лог ошибок существования файлов пуст, то...
Log_Error_Exists = Не существуют следующие файлы:`n%Separator%`n
; вносим заголовок в переменную для лога ошибок существования файлов
Log_Error_Exists = %Log_Error_Exists%%Cur_Dir%\%Cur_File%`n
; добавляем отсутствующий файл в переменную для лога ошибок существования файлов
Continue ; продолжаем читать файл изменений
}
; создаем отдельные подпапки для созданных и для измененных файлов, если указано в настройках пользователя
If ( Create_Sub_Dir = True ) ; если указано создавать, то...
{
If Pos_Created = 1 ; если файл был добавлен, то...
Sub_Dir = \Created ; назначаем подпапку для целевого файла
If Pos_Modified = 1 ; если файл был изменен, то...
Sub_Dir = \Modified ; назначаем подпапку для целевого файла
}
; создаем дерево папок
StringReplace, Tree_Dir, Cur_Dir, : ; удаляем из строки ":" (подготавливаем строку к обработке)
Cur_Tree_Dir = ; очищаем переменную (от предыдущего цикла)
Loop, Parse, Tree_Dir,\ ; парсим строку
{
If A_Index = 1 ; если первая итерация цикла, то...
Cur_Tree_Dir = %A_LoopField% ; вносим текущую папку в дерево
Else ; если НЕ первая итерация цикла, то...
Cur_Tree_Dir = %Cur_Tree_Dir%\%A_LoopField% ; добавляем текущую папку в дерево
Target_Dir = %Mon_App_Path%%Sub_Dir%\%Cur_Tree_Dir% ; составляем имя целевой папки
}
IfNotExist, %Target_Dir% ; если целевая папка не существует, то...
FileCreateDir, %Target_Dir% ; создать целевую папку
; копируем исходные файлы в целевую папку
FileCopy, %Cur_Dir%\%Cur_File%, %Target_Dir% ; скопировать исходный файл в целевую папку
If ErrorLevel = 0 ; если файл скопирован успешно, то...
{
If Log_Success = ; если лог успешных операций пуст, то...
Log_Success = Сохранены следующие файлы:`n%Separator%`n
; вносим заголовок в переменную для лога успешных операций
Log_Success = %Log_Success%%Cur_Dir%\%Cur_File%`n
; добавляем файл в переменную для лога успешных операций
}
Else ; если есть ошибки копирования, то...
{
If Log_Error_Copy = ; если лог ошибок копирования пуст, то...
Log_Error_Copy = Следующие файлы существуют, но сохранить их не удалось:`n%Separator%`n
; вносим заголовок в переменную для лога ошибок копирования
Log_Error_Copy = %Log_Error_Copy%%Cur_Dir%\%Cur_File%`n
; добавляем файл в переменную для лога ошибок копирования
}
}
}
; Удаляем все пустые подпапки в созданной папке сохранений
; (пустые папки могут появиться, если к-либо файлы сохранить не удалось)
DelEmpty( Mon_App_Path ) ; вызываем функцию удаления пустых подпапок (см. строку ниже)
DelEmpty( dir ) ; функция удаления пустых подпапок
{
BatchLines_Before := A_BatchLines ; запоминаем текущие настройки скорости выполнения скрипта
SetBatchLines, -1 ; устанавливаем max скорость для скрипта
Global Mon_App_Path ; объявляем переменную глобальной (чтобы использовать внутри функции)
Loop %dir%\*.*, 2 ; обрабатываем текущую папку и ее подпапки
DelEmpty( A_LoopFileFullPath ) ; берем первую же папку и вызываем из функции саму себя, чтобы дойти до самой нижней подпапки
If dir != %Mon_App_Path% ; если текущая папка НЕ папка для сохранений, то...
FileRemoveDir, %dir% ; удаляем её, если она пуста
SetBatchLines, %BatchLines_Before% ; восстанавливаем скорость скрипта
}
; Удаляем сохранённый файл изменений
FileDelete, %Changes_File_Path%
; СОХРАНЯЕМ ОТСЛЕЖЕННЫЕ ИЗМЕНЕНИЯ В РЕЕСТРЕ
; -----------------------------------------
; Проверяем нужно ли сохранять реестр
If Reg_Empty = 1 ; если в логе отсутствуют изменения реестра (выяснили выше), то...
Goto Log_Write ; перейти сразу к записи лога
; Выбрать пункт меню "Извлечь -> Данные реестра"
WinMenuSelectItem, ahk_class TfrmTUMain.UnicodeClass,, 1&, 8&, 5& ; (!!!) выбрать пункт меню Файл -> Извлечь -> Данные реестра
DetectHiddenWindows, Off ; НЕ искать в скрытых окнах...
WinWait, ahk_class TdlgExportRegistry.UnicodeClass ; (!!!) подождать окно сохранения реестра
If ( Del_Reg_Check = True ) ; если в настройках пользователя указано ставить галочку на "удаленные ключи" при сохранении файла реестра
Control, Check,, TTntCheckBox.UnicodeClass3 ; (!!!) поставить галочку на "удаленные ключи"
Loop ; убеждаемся, что подтверждение принято
{
ControlSend, TTntButton.UnicodeClass2, {Space} ; (!!!) нажать ОК
Sleep, 10*A_index ; увеличивающаяся пауза, чтобы окно успевало среагировать
IfWinNotExist ; если окно сохранения реестра уже НЕ существует, то...
Break ; конец цикла
}
; Сохранить файл реестра для отслеженного приложения
; Сохраняем сначала во временную папку, затем копируем в целевую. Финт, чтобы TU не блокировал целевую папку от удаления.
WinWait, ahk_class #32770 ahk_pid %TU_PID% ; ждем окно "Сохранить как", назначаем его последним найденным (сейчас понадобится)
IfExist, %A_Temp%\%Mon_App_Name%.reg ; если файл реестра уже существует
FileDelete, %A_Temp%\%Mon_App_Name%.reg ; удалить его (чтобы не подтверждать запрос на перезапись)
Loop ; убеждаемся, что путь к сохраняемому файлу вставлен правильно
{
ControlSetText, Edit1, %A_Temp%\%Mon_App_Name%.reg ; вставить путь к сохраняемому файлу реестра
Sleep, 10*A_index ; увеличивающаяся пауза, чтобы путь успел вставиться
ControlGetText, Changes_Reg_Path, Edit1 ; получить имя (и путь) сохраняемого файла реестра
If Changes_Reg_Path = %A_Temp%\%Mon_App_Name%.reg ; если имя (и путь) вставились нормально, то...
Break ; конец цикла
}
Loop ; убеждаемся, что подтверждение принято
{
ControlSend, Button2, {Space} ; нажать ОК
Sleep, 10*A_index ; увеличивающаяся пауза, чтобы окно успевало среагировать
IfWinNotExist ; если окно сохранения файла реестра уже НЕ существует, то...
Break ; конец цикла
}
Loop ; убеждаемся, что файл реестра создан
{
IfExist, %Changes_Reg_Path% ; если файл существует, то...
Break ; конец цикла
}
Loop ; убеждаемся, что сохранение файла изменений завершено
{
FileGetSize, File_Sise, %Changes_Reg_Path% ; получаем размер файла реестра
If File_Sise_Bak = %File_Sise% ; если текущий размер файла равен предыдущему размеру, то...
Break ; конец цикла
Sleep, 50 ; небольшая пауза
File_Sise_Bak = %File_Sise% ; запоминаем предыдущий размер файла изменений
}
FileMove, %Changes_Reg_Path%, %Mon_App_Path%, 1 ; перемещаем файл реестра из временной папки в целевую
;~ FileCopy, %Changes_Reg_Path%, %Mon_App_Path%, 1 ; копируем файл реестра из временной папки в целевую
; Готовим строчку для записи в лог
Log_Reg = Сохранен файл реестра:`n%Separator%`n%Mon_App_Name%.reg ; записать его сохранение в лог
; ПИШЕМ ЛОГ ОПЕРАЦИЙ
Log_Write: ; сюда будет переход, если реестр сохранять не нужно
If ( Log_Сorrection != "" OR Log_Error_Copy != "" OR Log_Success != "" OR Log_Error_Exists != "" OR Log_Reg != "" ) ; если логи не пусты
{
FileAppend, Имя сохраняемого приложения: %Mon_App_Name%`n, %Mon_App_Path%\%Log_Name% ; заголовок в общем логе
FileAppend, %Cur_Details%`n, %Mon_App_Path%\%Log_Name% ; текущий вид подробностей - в общий лог
If Selection_Log ; если были сохранены только выделенные пункты (соответствующая строка НЕ пуста), то...
FileAppend, %Selection_Log%`n, %Mon_App_Path%\%Log_Name% ; записать это в общий лог
FileAppend, *******************************************`n`n, %Mon_App_Path%\%Log_Name% ; закрытие шапки в общем логе
}
If Log_Сorrection ; если лог исправления пути НЕ пуст, то...
FileAppend, %Log_Сorrection%`n, %Mon_App_Path%\%Log_Name% ; записать лог исправления пути в общий лог
If Log_Error_Copy ; если лог ошибок копирования НЕ пуст, то...
FileAppend, %Log_Error_Copy%`n, %Mon_App_Path%\%Log_Name% ; записать лог ошибок копирования в общий лог
If Log_Success ; если лог успешных операций НЕ пуст, то...
FileAppend, %Log_Success%`n, %Mon_App_Path%\%Log_Name% ; записать лог успешных операций в общий лог
If Log_Error_Exists ; если лог ошибок существования файлов НЕ пуст, то...
FileAppend, %Log_Error_Exists%`n, %Mon_App_Path%\%Log_Name% ; записать лог ошибок существования в общий лог
If Log_Reg ; если лог сохранения реестра НЕ пуст, то...
FileAppend, %Log_Reg%`n, %Mon_App_Path%\%Log_Name% ; записать лог сохранения реестра в общий лог
SplashImage, Off ; удаляем сплэш
; Готовим дополнительную инфу для сообщений
If OnlySelection ; если сохранялось только выделенное, то...
ExtInfo = Сохранялись только выделенные пункты ; дополнительная информация
StringMid, Cur_Details, Cur_Details, InStr( Cur_Details, ":" ) + 2 ; вырезаем текущий вид (без слова Вид/View)
If ( Cur_Details != nDetails ) ; если вид не "Показать все", то...
{
If ExtInfo ; если уже содержит текст
ExtInfo = %ExtInfo%`nи текущий вид подробностей изменений: "%Cur_Details%" ; добавить текст
Else
ExtInfo = Текущий вид подробностей изменений: "%Cur_Details%" ; назначить текст
}
IfNotExist, %Mon_App_Path%\%Log_Name% ; если лог НЕ существует (например, в логе TU были только удаленные файлы)
{
If ExtInfo ; если есть расширенная информация
Text =
( LTrim
Ничего не найдено для сохранения!
Приложение "%Mon_App_Name%" НЕ СОХРАНЕНО.
Возможно, это потому, что:
%ExtInfo%
)
Else ; если нет расширенной информации
Text =
( LTrim
Ничего не найдено для сохранения!
Приложение "%Mon_App_Name%" НЕ СОХРАНЕНО.
)
My_MsgBox( "SaveApp4TU -- by Androgen Belkin©", Text ) ; вызываем функцию отображения MsgBox'а с текстом по центру
FileRemoveDir, %Mon_App_Path%, 1 ; удалить созданную папку для изменений со всем содержимым
ExitApp ; конец скрипта
}
; Показываем финальное сообщение
If ExtInfo ; если есть расширенная информация
Text =
( LTrim
Сохранение успешно завершено!
Приложение сохранено в:
"%Mon_App_Path%"
%ExtInfo%
Показать лог операций?
)
Else ; если нет расширенной информации
Text =
( LTrim
Сохранение успешно завершено!
Приложение сохранено в:
"%Mon_App_Path%"
Показать лог операций?
)
Button := My_MsgBox( "SaveApp4TU -- by Androgen Belkin©", Text, 1 ) ; вызываем функцию отображения MsgBox'а с текстом по центру
If Button = OK ; если ОК, то...
Run, %Mon_App_Path%\%Log_Name% ; показать лог
ExitApp ; конец скрипта
;-------------------------------------------------------------------------------
My_MsgBox( Caption, Text, MsgBoxId = 0, Default = 1 ) ; функция отображения MsgBox'а с текстом по центру
{
; в качестве параметра функция принимает переменные, содержащие заголовок, текст, индекс MsgBox'а, номер кнопки по умолчанию
; функция возвращает текст нажатой кнопки (на случай, если это представляет интерес)
/*
Доступные Индексы MsgBox'а
OK 0
OK/Cancel 1
Yes/No/Cancel 3
Yes/No 4
*/
; Предварительные приготовления
BatchLines_Before := A_BatchLines ; запоминаем текущие настройки скорости выполнения скрипта
SetBatchLines, -1 ; указываем максимальную скорость для скрипта
B_Width = 75 ; ширина кнопок
B_Height = 23 ; высота кнопок
B_Space = 7 ; расстояние между кнопками
Text = %Text%`n ; добавляем к тексту перевод строки
; Создаем GUI
Gui, +LastFound -MinimizeBox ; делаем окно GUI последним найденным, удаляем дополнительные кнопки из заголовка окна
Gui, Margin, 11, 9 ; задаем отступы разметки для GUI
Gui, Font,, Tahoma ; задаем шрифт GUI
Gui, Add, Text, Center, %Text% ; добавляем текст (со свойством "по центру")
; Добавляем нужные кнопки в GUI
If MsgBoxId = 0
Gui, Add, Button, w%B_Width% h%B_Height% gButton, OK ; добавляем кнопку "ОК" с заданными размерами
If MsgBoxId = 1
{
Gui, Add, Button, w%B_Width% h%B_Height% gButton, OK ; добавляем кнопку "ОК" с заданными размерами
Gui, Add, Button, yp wp hp gButton, Отмена ; добавляем кнопку "Отмена" (y-координата, ширина и высота как у предыдущей)
}
If MsgBoxId = 3
{
Gui, Add, Button, w%B_Width% h%B_Height% gButton, Да ; добавляем кнопку "Да" с заданными размерами
Gui, Add, Button, yp wp hp gButton, Нет ; добавляем кнопку "Нет" (y-координата, ширина и высота как у предыдущей)
Gui, Add, Button, yp wp hp gButton, Отмена ; добавляем кнопку "Отмена" (y-координата, ширина и высота как у предыдущей)
}
If MsgBoxId = 4
{
Gui, Add, Button, w%B_Width% h%B_Height% gButton, Да ; добавляем кнопку "Да" с заданными размерами
Gui, Add, Button, yp wp hp gButton, Нет ; добавляем кнопку "Нет" (y-координата, ширина и высота как у предыдущей)
}
Gui, Show, Hide, %Caption% ; отобразить окно, но в скрытом виде (сейчас начнем его обрабатывать)
WinGetPos,,, Gui_Width ; получить ширину окна GUI
; Распределяем кнопки (и текст, если нужно) по ширине окна
If MsgBoxId = 0 ; если кнопка ОДНА
GuiControl, Move, Button1, % "x" (Gui_Width - B_Width)/2 - 2 ; поместить кнопку "ОК" по центру ширины
If ( MsgBoxId = 1 OR MsgBoxId = 4 ) ; если кнопок ДВЕ
{
If ( Gui_Width < B_Width*2 + B_Space ) ; если кнопки не помещаются в окно, то...
{
Gui_Width := B_Width*2 + B_Space + 28 ; задаем новую ширину GUI
WinMove,,,,, %Gui_Width% ; раздвигаем окно до нужной ширины
ControlGetPos,,, T_Width,, Static1 ; получаем ширину контрола текста
GuiControl, Move, Static1, % "x" (Gui_Width - T_Width)/2 ; поместить контрол текста по центру ширины
}
x1 := (Gui_Width - B_Space - B_Width*2)/2 - 3 ; вычисляем координату x для кнопки 1
x2 := x1 + B_Width + B_Space ; вычисляем координату x для кнопки 2
GuiControl, Move, Button1, x%x1% ; перемещаем кнопку "ОК" (или "Да")
GuiControl, Move, Button2, x%x2% ; перемещаем кнопку "Отмена" (или "Нет")
}
If MsgBoxId = 3 ; если кнопок ТРИ
{
If ( Gui_Width < B_Width*3 + B_Space*2 ) ; если кнопки не помещаются в окно, то...
{
Gui_Width := B_Width*3 + B_Space*2 + 28 ; задаем новую ширину GUI
WinMove,,,,, %Gui_Width% ; раздвигаем окно до нужной ширины
ControlGetPos,,, T_Width,, Static1 ; получаем ширину контрола текста
GuiControl, Move, Static1, % "x" (Gui_Width - T_Width)/2 ; поместить контрол текста по центру ширины
}
x1 := (Gui_Width - B_Space*2 - B_Width*3)/2 - 3 ; вычисляем координату x для кнопки 1
x2 := x1 + B_Width + B_Space ; вычисляем координату x для кнопки 2
x3 := x2 + B_Width + B_Space ; вычисляем координату x для кнопки 3
GuiControl, Move, Button1, x%x1% ; перемещаем кнопку "Да"
GuiControl, Move, Button2, x%x2% ; перемещаем кнопку "Нет"
GuiControl, Move, Button3, x%x3% ; перемещаем кнопку "Отмена"
}
; Назначаем кнопку по умолчанию
If Default = 1
GuiControl, +Default, Button1
If Default = 2
GuiControl, +Default, Button2
If Default = 3
GuiControl, +Default, Button3
; Показываем окно MsgBox'а
Gui, Show,, %Caption% ; показываем окно (ранее скрытое)
ControlFocus, Button%Default% ; ставим фокус на кнопку по умолчанию
ControlGetPos, bX, bY, bWidth, bHeight, Button%Default% ; получаем положение кнопки по умолчанию
MouseMove, bX+bWidth/2, bY+bHeight/2, 0 ; перемещаем мышь на кнопку по умолчанию
Pause, On ; ждем действий юзера
SetBatchLines, %BatchLines_Before% ; восстанавливаем скорость скрипта
Return, ButtonText ; возвращаем из функции текст нажатой кнопки
; подпрограммы Gui
GuiClose: ; при нажатии кнопки закрытия окна
GuiEscape: ; при нажатии кнопки ESC
Button: ; при нажатии любой кнопки
ButtonText = %A_GuiControl% ; запоминаем, текст нажатой кнопки
Gui Destroy ; убиваем GUI
Pause, Off ; снимаем с паузы
Return ; возвращаемся из подпрограммы (продолжаем с места паузы)
} ; конец функции
;-------------------------------------------------------------------------------
;-------------------------------------------------------------------------------
ExpandEnvVars( ppath ) ; функция по разворачиванию переменных окружения в их содержание
{
VarSetCapacity( Dest, 2000 ) ; обеспечиваем достаточную вместимость переменной
DllCall( "ExpandEnvironmentStrings", Str, ppath, Str, Dest, Int, 1998 ) ; получаем содержание п. окруж-я
Return, Dest ; возвращаем содержание переменной окружения
}
;-------------------------------------------------------------------------------
Этот скрипт можно забрать архивчиком.