1

Тема: WSH: Скрипт автообновления приложения

Есть приложение (бинарное), оно периодически обновляется, новая версия в Интернет. Приложение работает без участия пользователя и пользователь не должен принимать участие в его работе и, соответственно, работе обновления.

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

Алгоритм примерно следующий:

1. Проверили новую версию, если она есть, то приложение его загрузило и завершает свою работу.
2. Старт приложения реализуется через WSH-скрипт, который после выхода проверяет наличие загруженных обновлений и начинает обновлять файл с резервированием, проверками и т.п.
3. Приложение перезапускается.

Вроде бы несложно, но может быть кто-нибудь уже реализовывал подобный велосипед - укажите пжлст.

2

Re: WSH: Скрипт автообновления приложения

Написал сам, скрипт под мою действующую задачу как есть.

var CreateObject = function (ProgId) { return WScript.CreateObject(ProgId)};

var FSO = new ActiveXObject("Scripting.FileSystemObject");
var WSHShell = new ActiveXObject("Wscript.Shell");
var WshNetwork = new ActiveXObject("WScript.Network");

// ***** todo: путь брать из текущей папки
var strAppRootPath = WSHShell.CurrentDirectory+'\\vfs';
var strUpdatePath = strAppRootPath+"\\updates";
var strSourcePath = strUpdatePath+"\\source";
var strBackupPath = strUpdatePath+"\\backup";
var strLogPath = strAppRootPath+"\\log";

var strUpdateFileName = "vfs.rar";

var intCycles = 0;

var E;

var DebugMode = false;

var objVFS = 0;
var obj1 = 0;

var objVFSStatus = 0;
var obj1Status = 0;


// ***** functions begin

function Exec(strCommand, intMaxTimer, intTimerValue) {
    var    ExecObject;
    var intTimer = 0;

    if (!intTimerValue)    {
        intTimerValue = 1000;
    }

    if (!intMaxTimer) {
        intMaxTimer = 24*60*60;
    }

    try    {
        ExecObject = WSHShell.Exec(strCommand);

        while (ExecObject.status == 0) {
            
            WScript.Sleep(intTimerValue);

            intTimer++;

            if (intTimer >= intMaxTimer) {
                ExecObject.Terminate();
                return -2;
            }
        }

    }
    catch (E) {
        Log("Exec() ошибка выполнения "+E.code);

        ExecObject = null;

        return false;
    }

    try {
        return ExecObject.ExitCode;
    }
    finally {
        ExecObject = null;
    }
} // Exec()


function DeleteFiles(strPath) {
    try {
        with (FSO.GetFolder(strPath)) {
            var eFiles = new Enumerator(Files),
                eSubs = new Enumerator(Subfolders),
                itm;

            for (; !eFiles.atEnd(); eFiles.moveNext()) {
                itm = eFiles.item();

    //            WScript.echo(itm.Path + ".....удалено");
                itm.Attributes = 0;
                itm.Delete();
            }

            for (; !eSubs.atEnd(); eSubs.moveNext()) {
                itm = eSubs.item();

                DeleteFiles(itm.Path);

    //            WScript.echo(itm.Path + ".....удалено");

                if (itm.Size == 0) {
                    itm.Attributes = 0;
                    itm.Delete();
                }
            }
        }
    }
    catch (E) {
        return false;
    }

    return true;
} // DeleteFiles()


function UpdateFilesWithBackup(strSubPath) {
    var fBackup;

    if (!strSubPath) { strSubPath=""; }

    Log("UpdateFilesWithBackup="+strSourcePath+strSubPath);

    try {
        with (FSO.GetFolder(strSourcePath+strSubPath)) {
            var eFiles = new Enumerator(Files),
                eSubs = new Enumerator(Subfolders),
                itm;

            for (; !eFiles.atEnd(); eFiles.moveNext()) {
                itm = eFiles.item();

                try {
                    fBackup = FSO.GetFile(strAppRootPath+"\\"+strSubPath+"\\"+itm.Name);
                }
                catch (E) {}

                // делаем бакап
                if (fBackup) {
                    Log(strBackupPath+strSubPath+"\\"+itm.Name+" - backup");
                    fBackup.Copy(strBackupPath+strSubPath+"\\"+itm.Name, true);
                }

                // обновляем
                Log(strAppRootPath+"\\"+strSubPath+"\\"+itm.Name+" - обновляем");
                itm.Copy(strAppRootPath+"\\"+strSubPath+"\\"+itm.Name, true);
            }

            for (; !eSubs.atEnd(); eSubs.moveNext()) {
                itm = eSubs.item();

                try {
                    Log("CreateFolder in approot = "+strAppRootPath+"\\"+itm.Name);
                    FSO.CreateFolder(strAppRootPath+"\\"+strSubPath+"\\"+itm.Name);
                }
                catch (E) {
                    // папки не существует
                    Log(strAppRootPath+"\\"+strSubPath+"\\"+itm.Name+".....существует");
                }

                try {
                    Log("CreateFolder in backup = "+strBackupPath+strSubPath+"\\"+itm.Name);
                    FSO.CreateFolder(strBackupPath+strSubPath+"\\"+itm.Name);
                }
                catch (E) {
                    // папки не существует
                    Log(strBackupPath+strSubPath+"\\"+itm.Name+".....существует");
                }

                if (!UpdateFilesWithBackup(strSubPath+"\\"+itm.Name)) {
                    return false;
                }
            }
        }
    }
    catch (E) {
        return false;
    }

    return true;
} // UpdateFilesWithBackup()


// ***** functions ends


function Update() {
    // ***** проверка файла обновления

    if (FSO.FileExists(strUpdatePath+"\\"+strUpdateFileName)) {
        if (Exec(WSHShell.CurrentDirectory+"\\"+"rar.exe t \""+strUpdatePath+"\\"+strUpdateFileName+"\"", 120) != 0) {
            Log("Ошибка выполнения rar test");

            // ***** todo: перезагрузить
        }
    }
    else {
        // по выходу запуститься приложение    
        return;
    }



    // ***** очистить папку от предыдущих файлов

    DeleteFiles(strSourcePath);


    // ***** распаковка в специальную папку

    if (Exec(WSHShell.CurrentDirectory+"\\"+"rar.exe x \""+strUpdatePath+"\\"+strUpdateFileName+"\" \""+strSourcePath+"\"", 120) != 0) {
        Log("Ошибка выполнения rar extract");

        // ***** todo: перезагрузить
    }



    // ****** очистить папку backup

    if (!DeleteFiles(strBackupPath)) {
        // ***** todo: надо перезагрузиться
    }


    // ***** при обновлении файлов нужно создавать их резервную копию
    // ***** если обновление неудачно, то нужно возвращать зарезервированные файлы назад и выходить - пустить в перезагрузку

    if (!UpdateFilesWithBackup()) {
        // ***** восстанавливаем бакап: копируем папку backup в approot

        try {
            FSO.CopyFolder(strBackupPath, strAppRootPath, true);
        }
        catch(E) {
            // ***** todo: В перезагрузку
        }
    }


    // ***** todo: запуск update.js из распакованных (если такой имеется) с контролем errorlevel, если он неуспешный, то надо откатывать обновление

    // update.js не будет пока


    // ***** todo: удалить update.rar

    FSO.DeleteFile(strUpdatePath+"\\"+strUpdateFileName, true);

} // Update()


function Setup() {
    try {
        if (!FSO.FileExists(WSHShell.CurrentDirectory+"\\1.exe")) {
            return false;
        }

        if (!FSO.FileExists(WSHShell.CurrentDirectory+"\\vfs.exe")) {
            return false;
        }

        if (!FSO.FileExists(WSHShell.CurrentDirectory+"\\rar.exe")) {
            return false;
        }

        if (!FSO.FolderExists(strLogPath)) {
            FSO.CreateFolder(strLogPath);
        }

        if (!FSO.FolderExists(strUpdatePath)) {
            FSO.CreateFolder(strUpdatePath);
        }

        if (!FSO.FolderExists(strSourcePath)) {
            FSO.CreateFolder(strSourcePath);
        }

        if (!FSO.FolderExists(strBackupPath)) {
            FSO.CreateFolder(strBackupPath);
        }
    }
    catch(E) {

        Log('Setup(): runtime error');

        return false;
    }

    return true;
} // Setup


function _GetLeadingZero(Value) {
    return (Value > 9) ? "" + Value : "0" + Value;
} // _GetLeadingZero()


function Log(strMessage, strURL, strLine) {
        try {
            var strAlertMessage = "";
            var LogFile; 
            var LogFileName = strLogPath+"\\";
            var DateObject = new Date();
            var strDateTime = _GetLeadingZero(DateObject.getDate())+"."+_GetLeadingZero(DateObject.getMonth()+1)+
             " "+_GetLeadingZero(DateObject.getHours())+":"+_GetLeadingZero(DateObject.getMinutes())+":"+
             _GetLeadingZero(DateObject.getSeconds());

            strLine = (strLine == undefined) ? "" : ": "+strLine;

            strAlertMessage += "ОШИБКА ИСПОЛНЕНИЯ\n\n";
//            strAlertMessage += "\nURL: "+strURL;
//            strAlertMessage += "\nСтрока"+strLine;
            strAlertMessage += "\n\n"+strMessage;

            LogFileName += "vp"+_GetLeadingZero(DateObject.getMonth()+1)+""+_GetLeadingZero(DateObject.getDate())+".txt";

            if (FSO.FileExists(LogFileName)) {
                LogFile = FSO.OpenTextFile(LogFileName, 8, true);
            }
            else {
                LogFile = FSO.CreateTextFile(LogFileName, false);
            }

            LogFile.WriteLine("! "+strDateTime+" ["+WshNetwork.ComputerName+":"+WshNetwork.UserName+"]"+
             " -"+
             " "+strURL+strLine+
             " - "+strMessage);

            LogFile.Close();

            if (DebugMode) {
                try {
                    WScript.Echo(strAlertMessage);
                }
                catch(e) {}
            }
    }
    catch(E) {
        if (DebugMode) {
            try {
                WScript.Echo("ошибка выполнения Log()."+
                  " Название ошибки: "+E.name+"."+
                  " Сообщение ошибки: "+E.message+"."+
                  " Описание ошибки: "+E.description+".");
            }
            catch(e) {}
        }
    }

    LogFile = null;
    DateObject = null;

    return true;
}



// ***** main


/*

todo:

- как сообщать на сервер ошибки обновления?

- надо считать кол-во привентивных перезагрузок и останавливать процесс, если они повторились Х раз

- освобождение памяти объектов

// ***** иначе нужно определить, может быть компьютер находится в стадии перезагрузки/завершения сеанса и тогда ничего делать не надо
// ***** если нет - запустить приложение заново, возможно это был несанкционированный выход (runtime error).
// ***** после выхода из приложения надо завершить 1.exe

*/

Log("Запуск");

if (!Setup()) {
    Log("Ошибка окружения");
    WScript.Quit();
}


// ****** todo: цикл на перезапуски приложений
while (true) {
    intCycles++;

    Update();

    Log('vfs start');
    try    {
        objVFS = WSHShell.Exec(WSHShell.CurrentDirectory+"\\vfs.exe");
    }
    catch (E) {
        Log('Ошибка запуска');
    }
    
    WScript.Sleep(5*1000);

    Log('1 start');
    try {
        obj1 = WSHShell.Exec(WSHShell.CurrentDirectory+"\\1.exe");
    }
    catch (E) {
        Log("Ошибка выполнения 1.exe"+E.code);
    }

    WScript.Sleep(5*1000);

    while (true) {
        // цикл ожидания выхода из приложений
        WScript.Sleep(500);

        objVFSStatus = objVFS.Status;
        obj1Status = obj1.Status;

        if (obj1Status != 0) {
            Log('obj1.ExitCode ='+obj1.ExitCode);

            try {
                objVFS.Terminate();
            }
            catch(E) {}

            Log('VFS terminate');

            // ***** todo: 1 перезапустился в другой ProcessID, надо найти его и далее использовать

            break;
        }

        if (objVFSStatus != 0) {
            Log('objVFS.ExitCode='+objVFS.ExitCode);

            try {
                obj1.Terminate();
            }
            catch(E) {}

            Log('1 terminate');

            if (objVFS.ExitCode == 99) {
                Log('Завершение работы вручную');
                WScript.Quit();
            }

            break;
        }
    } //status executed apps

} // main


/*

// ***** todo: принудительный перезапуск эмулятора и 1
// но надо отслеживать, что 1 сам может перезапуститься и тогда его объектом нельзя будет
// управлять, соответственно надо выходить из 1 другим способом

    if (Exec(strAppRootPath+"\\"+"Emulator.exe") == 100) {
        // ***** после завершения приложения по errorlevel=100, которое означает
        // надо обновиться запускается процесс обновления
        // Плюс, надо прервать выполнение 1.exe

        if (obj1 == 0)    {
            continue;
        }
        else {
            onj1.Terminate();
            WScript.Sleep(1000);

            continue;
        }
    }

*/
//}



// ***** todo: запустить приложение, запустить 1.exe

Log("Выход");

3

Re: WSH: Скрипт автообновления приложения

Была аналогичная задача, правда приложение было клиентское и файл(adp) в локалке.

Ярлык для запуска проекта вёл на скрипт. Скрипт проверял версию файла на сервере(текстовик с датой в формате yyyymmddvv, vv - версия в течении дня, на всякий случай). Если число было больше чем в локальном файле с версией, то загружался с перезаписью обновлённый adp-файл(плюс локальный файл с версией обновлялся), после чего он открывался в акцесе.

Ко всему выше перечисленному была простыня по работе с файлами, протоколированию в журнал, определению доступной версии акцесса, защите от дурака, созданию дерева каталогов(при первом запуске скрипта), правке настроек безопасности макросов и т.п. В общем, переписать бы всё заново - есть места в которых я сам уже не помню зачем там именно "это" делается...  С другой стороны, уже лет 5 работает с минимальными доработками, поэтому мотивации на переделку(подумываю о vb.net) мало - нужно усовершенствовать, а гениальных идей на этот счёт нет.

З.Ы. если нужно, могу выложить(хотя, наверно, краснеть придётся за код - тогда как раз осваивал vbs).