windowswixwindows-installerinstallshieldinstallshield-2016

Installation progress status messages from InstallShield Suite Installer


I have my own Updater.exe that launches the InstallShield Suite Installer (which is embedded with two other InstallShield MSIs). I would like to get the installation progress status messages from Suite Installer and show in my Updater.exe UI.

Is it possible to poll/ping the installer to get the installation status message? (Like staging, installing msi1, installing msi2, configuring, install complete).

I am using .NET Framework 4.5+ in my installer as a prerequisite, Installshield version 2016, wiX toolset for custom actions.


Solution

  • InstallShield 2016 do not have any out of the box features to get the status messages while the installation is in progress.

    I end up writing my own tool to monitor installshield property values in debug log that generates with debuglog parameters.

    InstallShield Installer Debug Logs:

    syntax: Installer.exe /debuglog"logfilepath"

    example: installer.exe /debuglog"c:\users\test.user\desktop\debuglog.log"

    InstallShield sets three different status types viz; 'ISProgressSummary', 'ISInstallStatus' and 'ISParcelStatus' and logs into debug logs.

    Sample installshield debug log entries are:

    9-23-2019[10:40:56 AM]: Engine: property 'ISProgressSummary' value now 'The program features you selected are being installed.'

    9-23-2019[10:41:12 AM]: Engine: property 'ISInstallStatus' value now 'Installing package Microsoft Visual C++ 2012 Update 4 Redistributable Package (x86)'

    9-23-2019[10:41:17 AM]: Engine: property 'ISParcelStatus' value now 'Computing space requirements'

    Below C# code monitors debug log and invokes an event handler. You create an object of below class and send the 'statusType' you want to monitor. statusType can be ISProgressSummary, ISInstallStatus, ISParcelStatus

    public class LogFileMonitorLineEventArgs : EventArgs
    {
        public string Line { get; set; }
    }
    
    public class LogFileMonitor
    {
        public EventHandler<LogFileMonitorLineEventArgs> OnLine;
    
        readonly string _path;
        readonly string _delimiter;
        readonly string _statusType;
    
        Timer _timer;
    
        long _currentSize;
        bool _isCheckingLog;
    
        protected bool StartCheckingLog()
        {
            lock (_timer)
            {
                if (_isCheckingLog)
                    return true;
    
                _isCheckingLog = true;
                return false;
            }
        }
    
        protected void DoneCheckingLog()
        {
            lock (_timer)
                _isCheckingLog = false;
        }
    
        public LogFileMonitor(string path, string delimiter = "\n", string statusType = "")
        {
            _path = path;
            _delimiter = delimiter;
            _statusType = statusType;
        }
    
        public void Start()
        {
            _currentSize = new FileInfo(_path).Length;
    
            _timer = new Timer(1000);
            _timer.Elapsed += CheckLog;
            _timer.AutoReset = true;
            _timer.Start();
        }
    
        private void CheckLog(object s, ElapsedEventArgs e)
        {
            if (!StartCheckingLog()) return;
    
            try
            {
                var newSize = new FileInfo(_path).Length;
    
                if (_currentSize == newSize)
                    return;
    
                using (FileStream stream = File.Open(_path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
                using (StreamReader sr = new StreamReader(stream, Encoding.Unicode))
                {
                    sr.BaseStream.Seek(_currentSize, SeekOrigin.Begin);
    
                    var newData = sr.ReadToEnd();
                    if (!newData.EndsWith(_delimiter))
                    {
                        if (newData.IndexOf(_delimiter, StringComparison.Ordinal) == -1)
                        {
                            newData = string.Empty;
                        }
                        else
                        {
                            var pos = newData.LastIndexOf(_delimiter, StringComparison.Ordinal) + _delimiter.Length;
                            newData = newData.Substring(0, pos);
                        }
                    }
    
                    string[] lines = newData.Split(new[] { _delimiter }, StringSplitOptions.RemoveEmptyEntries);
    
                    string lastInstallStatus = string.Empty;
    
                    foreach (string line in lines)
                    {
                        if (line.Contains(_statusType))
                        {
                            lastInstallStatus = line;
                            OnLine?.Invoke(this, new LogFileMonitorLineEventArgs { Line = lastInstallStatus });
                        }
                    }
                }
    
                _currentSize = newSize;
            }
            catch (Exception)
            {
            }
    
            DoneCheckingLog();
        }
    
        public void Stop()
        {
            _timer?.Stop();
        }
    }