unit umainform; { LazAutoUpdater Tray Updater Copyright (C)2014 Gordon Bamber minesadorada@charcodelvalle.com An example of using LazAutoUpdate as a silent updater This source is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. A copy of the GNU General Public License is available on the World Wide Web at . You can also obtain it by writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. SUMMARY ======= TrayUpdater is a companion to LazAutoUpdate and UpdatePack It sits in the system tray, and automatically checks for updates and optionally auto-updates any apps for which it has a profile It uses an array of custom component LongTimers in order to update a number of different apps on different schedules It uses a config file LAUTray.ini to keep track. This file is automatically generated by UpdatePack and imported by this app when first run. VERSION HISTORY =============== 0.1.12: Updated to Laz 1.7 fpc 3.1.1 0.1.12: Updated lpr to correctly hide the mainform on startup 0.1.12: Commit to svn 0.1.13: Updated January 2017 Ini filename uses C_PFX 0.1.14: Memory leaks fixed 0.1.15: ?? } {$mode objfpc}{$H+} interface uses Classes, SysUtils, FileUtil, LazFileUtils, Forms, Dialogs, ExtCtrls, Menus, uLongTimer, ulazautoupdate, inifiles, eventlog, strUtils, uconfigform {$IFDEF WINDOWS}, registry{$ENDIF}; type { TAppRec - in-memory storage for all settings } TAppRec = record AppPrettyName: string; AppPath: string; INIPath: string; ZipPath: string; AppVersion: string; SFProjectName: string; SFUpdatesDirectory: string; Location: string; IntervalType: word; IntervalDay: word; IntervalDate: word; IntervalHour: word; Update: boolean; LastCheckDateTime: TDateTime; end; { Tmainform } Tmainform = class(TForm) Logger: TEventLog; idleReminder: TIdleTimer; LazAutoUpdate1: TLazAutoUpdate; mnu_About: TMenuItem; mnu_SelfCheck: TMenuItem; mnu_startAtOSStartUp: TMenuItem; mnu_configure: TMenuItem; mnu_fileExit: TMenuItem; PopupMenu1: TPopupMenu; TrayIcon1: TTrayIcon; procedure FormCloseQuery(Sender: TObject; var CanClose: boolean); procedure FormCreate(Sender: TObject); procedure FormWindowStateChange(Sender: TObject); procedure idleReminderTimer(Sender: TObject); procedure LazAutoUpdate1DebugEvent(Sender: TObject; WhereAt, Message: string); procedure mnu_AboutClick(Sender: TObject); procedure mnu_configureClick(Sender: TObject); procedure mnu_fileExitClick(Sender: TObject); procedure mnu_startAtOSStartUpClick(Sender: TObject); procedure mnu_SelfCheckClick(Sender: TObject); procedure TrayIcon1Click(Sender: TObject); function UT_DateTimeToStr(TheDate: TDateTime): string; function UT_StrToDate(str: string): TDateTime; private { private declarations } szImportINIPath: string; DebugMode: boolean; procedure DoAlert(aAppname, aVersion: string); // Transfers info from lauimport.ini to local ini function ImportINIFile: boolean; // When first run, Populates INI and AppRecArray with default entry procedure PopulateOwnProfile; // Transfers info from local INI to AppRecArray procedure DoReadINIIntoAppRecArray; // Re-initialises a LongTimer array from AppRecArray info procedure DoSetupLongTimerArray; // All LongTimers use this for their OnTimer event procedure DoLongTimerEvent(Sender: TObject); // Do a silent check/install for this AppRecArray member procedure CheckAndUpdate(ARecIndex: integer); public { public declarations } INI: TIniFile; AppRecArray: array of TAppRec; // Dynamic array LongTimerArray: array of TLongTimer; // Dynamic array iCurrentRecIndex: integer; // Transfers info from AppRecArray to local INI procedure DoWriteAppRecArrayIntoINI; end; var mainform: Tmainform; const {$IFDEF WINDOWS} C_OS = 'win'; {$ELSE} C_OS = 'linux'; {$ENDIF} {$IFDEF CPU32} C_BITNESS = '32'; {$ELSE} C_BITNESS = '64'; {$ENDIF} C_PFX = C_OS + C_BITNESS; C_LAUTRayINI = 'lauimport.ini'; C_INIFilename = 'trayupdater' + C_PFX + '.ini'; implementation resourcestring rs_balloontitle = '%s service'; rs_trayloaded = '%s Loaded'; rs_newversionavailable = 'A new version of %s (v%s) is available'; // TODO: Put all the other strings here {$R *.lfm} { Tmainform } procedure Tmainform.DoWriteAppRecArrayIntoINI; // AppRecArray is an in-memory copy of the INI file var i: integer; szSection: string; begin if High(AppRecArray) = 0 then Exit; for i := Low(AppRecArray) to High(AppRecArray) do begin szSection := AppRecArray[i].AppPrettyName; try INI.WriteString(szSection, 'AppPrettyName', AppRecArray[i].AppPrettyName); INI.WriteString(szSection, 'AppPath', AppRecArray[i].AppPath); INI.WriteString(szSection, 'INIPath', AppRecArray[i].INIPath); INI.WriteString(szSection, 'ZipPath', AppRecArray[i].ZipPath); INI.WriteString(szSection, 'AppVersion', AppRecArray[i].AppVersion); INI.WriteString(szSection, 'SFProjectName', AppRecArray[i].SFProjectName); INI.WriteString(szSection, 'SFUpdatesDirectory', AppRecArray[i].SFUpdatesDirectory); INI.WriteString(szSection, 'Location', AppRecArray[i].Location); INI.WriteInteger(szSection, 'IntervalType', AppRecArray[i].IntervalType); INI.WriteInteger(szSection, 'IntervalDate', AppRecArray[i].IntervalDate); INI.WriteInteger(szSection, 'IntervalDay', AppRecArray[i].IntervalDay); INI.WriteInteger(szSection, 'IntervalHour', AppRecArray[i].IntervalHour); INI.WriteInteger(szSection, 'IntervalHour', AppRecArray[i].IntervalHour); INI.WriteBool(szSection, 'Update', AppRecArray[i].Update); INI.WriteString(szSection, 'LastCheckDateTime', UT_DateTimeToStr(AppRecArray[i].LastCheckDateTime)); INI.UpdateFile; except if DebugMode then Logger.Error('Failed to write AppRecArray to %s', [INI.Filename]) end; end; end; procedure Tmainform.DoLongTimerEvent(Sender: TObject); // All LongTimerArray.OnTimer events go here // The Tag property is the same as the AppRecArray entry it relates to var iTag: integer; begin iTag := 0; // Assertion check if Sender is TLongTimer then begin iTag := TLongTimer(Sender).Tag; end; if DebugMode then Logger.Log('Timer for %s fired', [AppRecArray[iTag].AppPrettyName]); TrayIcon1.BalloonHint := Format('Checking %s for updates', [AppRecArray[iTag].AppPrettyName]); TrayIcon1.ShowBalloonHint; CheckAndUpdate(iTag); // AppRecArray[iTag].LastCheckDateTime := Now(); end; procedure Tmainform.DoSetupLongTimerArray; // This is called every time the AppRecArray is changed // Each profile has a LongTimer associated with it in LongTimerArray() // If the AppRecArray.Update=TRUE then the associated LongTimer is enabled // 1. Destroy and clear all the LongTimers aready allocated // 2. Iterate through the AppRecArray, setting up a new LongTimer for each element // 3. Enable the LongTimers for which AppArray.Update=TRUE var i, iArrayElement: integer; begin // Start with an empty array for i := Low(LongTimerArray) to High(LongTimerArray) do if LongTimerArray[i] <> nil then begin LongTimerArray[i].Enabled := False; FreeAndNil(LongTimerArray[i]); end; SetLength(LongTimerArray, 0); // Zeros the array iArrayElement := -1; for i := Low(AppRecArray) to High(AppRecArray) do begin Inc(iArrayElement); // initially to zero SetLength(LongTimerArray, iArrayElement + 1); LongTimerArray[iArrayElement] := TLongTimer.Create(nil); with AppRecArray[i] do begin case IntervalType of 0: LongTimerArray[iArrayElement].IntervalType := lt1Daily; 1: LongTimerArray[iArrayElement].IntervalType := lt2Weekly; 2: LongTimerArray[iArrayElement].IntervalType := lt3Monthly; end; case IntervalDay of 0: LongTimerArray[iArrayElement].WeeklyDay := lt1Monday; 1: LongTimerArray[iArrayElement].WeeklyDay := lt2Tuesday; 2: LongTimerArray[iArrayElement].WeeklyDay := lt3Wednesday; 3: LongTimerArray[iArrayElement].WeeklyDay := lt4Thursday; 4: LongTimerArray[iArrayElement].WeeklyDay := lt5Friday; 5: LongTimerArray[iArrayElement].WeeklyDay := lt6Saturday; 6: LongTimerArray[iArrayElement].WeeklyDay := lt7Sunday; end; LongTimerArray[iArrayElement].MonthlyDate := IntervalDate; LongTimerArray[iArrayElement].Daily24Hour := IntervalHour; LongTimerArray[iArrayElement].Tag := i; LongTimerArray[iArrayElement].OnTimer := @DoLongTimerEvent; // Make sure timer doesn't fire right after a self-update LongTimerArray[iArrayElement].SampleInterval := lt5Every45Minutes; LongTimerArray[iArrayElement].Enabled := Update; end; end; end; function Tmainform.UT_DateTimeToStr(TheDate: TDateTime): string; var frmstg: TFormatSettings; // Ensures consitent String dates across platforms begin //GetLocaleFormatSettings(0, frmstg); frmstg.DateSeparator := '-'; frmstg.ShortDateFormat := 'yyyy-mm-dd'; frmstg.TimeSeparator := '-'; frmstg.LongTimeFormat := 'hh-nn'; Result := DateTimeToStr(TheDate, frmstg); end; function Tmainform.UT_StrToDate(str: string): TDateTime; var frmstg: TFormatSettings; // Ensures consitent String dates across platforms begin //GetLocaleFormatSettings(0, frmstg); frmstg.DateSeparator := '-'; frmstg.ShortDateFormat := 'yyyy-mm-dd'; frmstg.TimeSeparator := '-'; frmstg.LongTimeFormat := 'hh-nn'; if not TryStrToDateTime(str, Result, frmstg) then Result := Now(); end; procedure Tmainform.DoReadINIIntoAppRecArray; // AppRecArray is an in-memory copy of the INI file var SectionStringList: TStringList; szSection: string; i, iArrayElement: integer; begin SetLength(AppRecArray, 0); // Zeros the array SectionStringList := TStringList.Create; try try // Fetch all the section names INI.ReadSections(SectionStringList); iArrayElement := -1; if SectionStringList.Count > 0 then for i := 0 to SectionStringList.Count - 1 do begin // Loop through all the INI file sections szSection := SectionStringList[i]; // Skip over the ProgramInfo section if (szSection <> 'ProgramInfo') then begin Inc(iArrayElement); // initially to zero SetLength(AppRecArray, iArrayElement + 1); AppRecArray[iArrayElement].AppPrettyName := INI.ReadString(szSection, 'AppPrettyName', ''); AppRecArray[iArrayElement].AppPath := INI.ReadString(szSection, 'AppPath', ''); AppRecArray[iArrayElement].INIPath := INI.ReadString(szSection, 'INIPath', ''); AppRecArray[iArrayElement].ZipPath := INI.ReadString(szSection, 'ZipPath', ''); AppRecArray[iArrayElement].AppVersion := INI.ReadString(szSection, 'AppVersion', ''); AppRecArray[iArrayElement].SFProjectName := INI.ReadString(szSection, 'SFProjectName', ''); AppRecArray[iArrayElement].SFUpdatesDirectory := INI.ReadString(szSection, 'SFUpdatesDirectory', ''); AppRecArray[iArrayElement].Location := INI.ReadString(szSection, 'Location', ''); AppRecArray[iArrayElement].IntervalType := INI.ReadInteger(szSection, 'IntervalType', 0); AppRecArray[iArrayElement].IntervalDate := INI.ReadInteger(szSection, 'IntervalDate', 1); AppRecArray[iArrayElement].IntervalDay := INI.ReadInteger(szSection, 'IntervalDay', 0); AppRecArray[iArrayElement].IntervalHour := INI.ReadInteger(szSection, 'IntervalHour', 9); AppRecArray[iArrayElement].Update := INI.ReadBool(szSection, 'Update', False); AppRecArray[iArrayElement].LastCheckDateTime := UT_StrToDate(INI.ReadString(szSection, 'LastCheckDateTime', UT_DateTimeToStr(Now))); end; end; except if DebugMode then Logger.Error('Failed to read AppRecArray from %s', [INI.Filename]) end; finally FreeAndNil(SectionStringList); end; // Low(AppRecArray) to High(AppRecArray) iterates the whole array end; procedure Tmainform.PopulateOwnProfile; // Only called when app is first run var szSection, szOSsuffix: string; begin szSection := Application.Title; szOSsuffix := LowerCase( {$I %FPCTARGETOS%} ); szSection += szOSsuffix; with INI do begin WriteString(szSection, 'AppPrettyName', szSection); WriteString(szSection, 'AppPath', ParamStr(0)); WriteString(szSection, 'Location', ProgramDirectory); WriteString(szSection, 'INIPath', 'lautraynotify' + szOSsuffix + '.ini'); WriteString(szSection, 'ZipPath', 'lautraynotify' + szOSsuffix + '.zip'); WriteString(szSection, 'AppVersion', LazAutoUpdate1.AppVersion); WriteString(szSection, 'SFProjectName', 'lazautoupdate'); WriteString(szSection, 'SFUpdatesDirectory', 'updates'); WriteInteger(szSection, 'IntervalType', 0); // lt1Daily WriteInteger(szSection, 'IntervalDay', 0); // lt1Monday (dummy) WriteInteger(szSection, 'IntervalDate', 1); // 1st Month (dummy) WriteInteger(szSection, 'IntervalHour', 9); // 9am WriteBool(szSection, 'Update', False); // Dont autoupdate the default profile! WriteString(szSection, 'LastCheckDateTime', UT_DateTimeToStr(Now)); WriteBool('ProgramInfo', 'IsVirgin', False); UpdateFile; end; with LazAutoUpdate1 do begin VersionsININame := 'lautraynotify' + szOSsuffix + '.ini'; ZipFileName := 'lautraynotify' + szOSsuffix + '.zip'; UpdatesFolder := 'updates'; SFProjectName := 'lazautoupdate'; end; DoReadINIIntoAppRecArray; if not FileExistsUTF8(C_LAUTRayINI) then LazAutoUpdate1.CreateLocalLauImportFile; LazAutoUpdate1.RelocateLauImportFile; end; function Tmainform.ImportINIFile: boolean; // Copies ini info from shared AppData directory // Appends Location info to the AppPath entry // Sets Update=TRUE // into C_INIFilename then deletes source ini var ImportINI: TIniFile; SectionStringList, SectionContents: TStringList; i: integer; szSection, szKey, szLocation, szAppPath: string; begin {TODO: Preserve user Logtimer settings?} Result := False; if not FileExists(szImportINIPath) then Exit; ImportINI := TIniFile.Create(szImportINIPath); SectionStringList := TStringList.Create; SectionContents := TStringList.Create; try ImportINI.ReadSections(SectionStringList); if SectionStringList.Count > 0 then begin szSection := SectionStringList[0]; ImportINI.ReadSection(szSection, SectionContents); for i := 0 to SectionContents.Count - 1 do begin szKey := SectionContents[i]; if szKey = 'Location' then szLocation := ImportINI.ReadString(szSection, szKey, ''); if szKey = 'AppPath' then szAppPath := ImportINI.ReadString(szSection, szKey, ''); INI.WriteString(szSection, szKey, ImportINI.ReadString( szSection, szKey, '')); end; // Append Location to AppPath if ((szAppPath <> '') and (szLocation <> '')) then INI.WriteString(szSection, 'AppPath', AppendPathDelim(szLocation) + ExtractFilename(szAppPath)); INI.WriteBool(szSection, 'Update', True); INI.WriteString(szSection, 'LastCheckDateTime', UT_DateTimeToStr(Now)); INI.UpdateFile; Result := True; end; finally FreeAndNil(SectionContents); FreeAndNil(SectionStringList); FreeAndNil(ImportINI); end; if (Result = True) then begin if DebugMode then Logger.Log('%s: Imported %s into %s', [Application.Title, szImportINIPath, INI.Filename]); DoReadINIIntoAppRecArray; end; end; procedure Tmainform.DoAlert(aAppname, aVersion: string); begin TrayIcon1.BalloonHint := Format(rs_newversionavailable, [aAppname, aVersion]); TrayIcon1.ShowBalloonHint; end; procedure Tmainform.FormWindowStateChange(Sender: TObject); begin if mainform.WindowState = wsMinimized then begin // 2 lines below are ineffective at hiding the main form // Replaced with Application.ShowMainForm := false; in the lpr // mainform.WindowState := wsNormal; // mainform.Hide; mainform.ShowInTaskBar := stNever; // Log a successful startup if DebugMode then Logger.Active := True; if DebugMode then Logger.Log('%s version %s started OK', [Application.Title, LazAutoUpdate1.AppVersion]); end; end; procedure Tmainform.idleReminderTimer(Sender: TObject); // IdleTimer checks for any new C_LAUTRayINI in the AppData folder // If found, it is imported then deleted. begin if LazAutoUpdate1.DownloadInprogress then begin TrayIcon1.BalloonHint := 'Download in progress'; TrayIcon1.ShowBalloonHint; Exit; end; if FileExists(szImportINIPath) then begin if DebugMode then Logger.Log('%s: Import file %s discovered', [Application.Title, szImportINIPath]); if ImportINIFile then begin if DebugMode then Logger.Log('%s: Import file %s successfully imported', [Application.Title, szImportINIPath]); DeleteFileUTF8(szImportINIPath); end else if DebugMode then Logger.Error('%s: Failed to import file %s', [Application.Title, szImportINIPath]); end; end; procedure Tmainform.LazAutoUpdate1DebugEvent(Sender: TObject; WhereAt, Message: string); begin if ((Logger.Active) and (DebugMode = True)) then Logger.Log('LazAutoUpdate:%s Message:%s', [WhereAt, Message]); end; procedure Tmainform.mnu_AboutClick(Sender: TObject); var sz: string; begin LazAutoUpdate1.ResetAppVersion; sz := Application.Title + ' system tray application' + LineEnding; sz += Format('Version %s', [LazAutoUpdate1.AppVersion]) + LineEnding; sz += 'License: LGPLv2' + LineEnding + LineEnding; sz += Format('Using LazAutoUpdate v%s', [LazAutoUpdate1.AutoUpdateVersion]) + LineEnding; sz += Format('%s %s', [LazAutoUpdate1.LCLVersion, LazAutoUpdate1.FPCVersion]) + LineEnding; sz += Format('Compiled %s', [LazAutoUpdate1.LastCompiled]) + LineEnding; MessageDlg('About ' + Application.Title, sz, mtInformation, [mbOK], 0); end; procedure Tmainform.mnu_configureClick(Sender: TObject); begin DoReadINIIntoAppRecArray; LazAutoUpdate1.ShowWhatsNewIfAvailable; configform.ShowModal; // may alter AppRecArray DoSetupLongTimerArray; DoWriteAppRecArrayIntoINI; end; procedure Tmainform.mnu_fileExitClick(Sender: TObject); begin DoWriteAppRecArrayIntoINI; Close; end; procedure Tmainform.mnu_startAtOSStartUpClick(Sender: TObject); {$IFDEF WINDOWS} var Reg: TRegistry; {$ENDIF} begin {$IFDEF WINDOWS} Reg := TRegistry.Create(KEY_ALL_ACCESS); try Reg.RootKey := HKEY_CURRENT_USER; Reg.OpenKey('\Software\Microsoft\Windows\CurrentVersion\Run', False); if mnu_startAtOSStartUp.Checked then begin Reg.WriteString(Application.Title, ParamStr(0)); INI.WriteBool('ProgramInfo', 'RunAtStartUp', True); if DebugMode then Logger.Log('Run at OS startup Activated'); MessageDlg(Application.Title, 'Run at OS startup activated OK', mtInformation, [mbOK], 0); end else begin Reg.DeleteValue(Application.Title); INI.WriteBool('ProgramInfo', 'RunAtStartUp', False); if DebugMode then Logger.Log('Run at OS startup Deactivated'); MessageDlg(Application.Title, 'Run at OS startup deactivated OK', mtInformation, [mbOK], 0); end; Reg.CloseKey; finally FreeAndNil(Reg); end; {$ELSE} if mnu_startAtOSStartUp.Checked then begin ShowMessage('Sorry - not implemented in Linux in this version'); mnu_startAtOSStartUp.Checked := False; end; {$ENDIF} end; procedure TmainForm.CheckAndUpdate(ARecIndex: integer); begin if LazAutoUpdate1.DownloadInprogress then begin if DebugMode then Logger.Error( 'Tried to update whilst download in progress. Exiting CheckAndUpdate routine'); Exit; end; with AppRecArray[ARecIndex] do begin LazAutoUpdate1.AppFileWithPath := AppPath; LazAutoUpdate1.AppVersion := AppVersion; LazAutoUpdate1.ZipfileName := ZipPath; LazAutoUpdate1.SFProjectName := SFProjectName; LazAutoUpdate1.UpdatesFolder := SFUpdatesDirectory; LazAutoUpdate1.VersionsININame := INIPath; LazAutoUpdate1.CopyTree := True; TrayIcon1.BalloonHint := Format('Checking %s for updates', [AppPrettyName]); TrayIcon1.ShowBalloonHint; // Debugging line LazAutoUpdate1.DebugMode := True; // Logs everyting if LazAutoUpdate1.NewVersionAvailable then begin TrayIcon1.BalloonHint := Format('Found newer version %s. Downloading...', [LazAutoUpdate1.GUIOnlineVersion]); TrayIcon1.ShowBalloonHint; if LazAutoUpdate1.DownloadNewVersion then if LazAutoUpdate1.SilentUpdate then begin TrayIcon1.BalloonHint := Format('Successfully updated %s to version %s', [AppPrettyName, LazAutoUpdate1.GUIOnlineVersion]); LastCheckDateTime := Now; end else TrayIcon1.BalloonHint := Format('Failed to update %s', [AppPrettyName]); end else begin TrayIcon1.BalloonHint := AppPrettyName + ' is up-to-date'; TrayIcon1.ShowBalloonHint; end; LazAutoUpdate1.ResetAppVersion; TrayIcon1.ShowBalloonHint; end; end; procedure Tmainform.mnu_SelfCheckClick(Sender: TObject); // iCurrentRecIndex is the selected profile begin // Production line //CheckAndUpdate(0); // Default profile (self) //Debugging line if DebugMode then Logger.Active := True; ShowMessageFmt('Update %s', [AppRecArray[iCurrentRecIndex].AppPrettyName]); CheckAndUpdate(iCurrentRecIndex); end; procedure Tmainform.TrayIcon1Click(Sender: TObject); begin if LazAutoUpdate1.DownloadInprogress then begin TrayIcon1.BalloonHint := 'Currently downloading...'; TrayIcon1.ShowBalloonHint; end; end; procedure Tmainform.FormCreate(Sender: TObject); begin if LowerCase(ParamStr(1)) = 'debug' then DebugMode := True else DebugMode := False; {$IFDEF DEBUGMODE} DebugMode := True; {$ENDIF} // Initialise the App's main INI file try INI := TIniFile.Create(GetAppConfigDirUTF8(False) + C_INIFilename); except sleep(4000); if (INI = nil) then INI := TIniFile.Create(GetAppConfigDirUTF8(False) + C_INIFilename); end; INI.CacheUpdates := False; INI.WriteString('ProgramInfo', 'AppName', Application.Title); INI.WriteString('ProgramInfo', 'Location', LazAutoUpdate1.AppFileWithPath); INI.WriteString('ProgramInfo', 'Version', LazAutoUpdate1.AppVersion); INI.WriteString('ProgramInfo', 'LastStarted', FormatDateTime(shortdateformat, Now)); mnu_startAtOSStartUp.Checked := INI.ReadBool('ProgramInfo', 'RunAtStartUp', False); // Is this a first-time run? if INI.ReadBool('ProgramInfo', 'IsVirgin', True) then PopulateOwnProfile; // Start up a system Event Log if DebugMode then Logger.LogType := ltFile; if DebugMode then Logger.Filename := ExtractFileNameWithoutExt(ParamStr(0)) + '.log'; // New logfile every time if DebugMode then if FileExistsUTF8(Logger.Filename) then DeleteFile(Logger.Filename); if DebugMode then Logger.AppendContent := True; if DebugMode then Logger.DefaultEventType := etInfo; if DebugMode then Logger.RaiseExceptionOnError := False; // Logger.Identification := Application.Title; // Fetch the location of any trayicon INI on the users system szImportINIPath := GetAppConfigDirUTF8(False, True); szImportINIPath := AnsiReplaceText(szImportINIPath, Application.Title, 'updatehm' + C_PFX); szImportINIPath := AppendPathDelim(szImportINIPath) + C_LAUTRayINI; if DebugMode then Logger.Debug('szImportINIPath=' + szImportINIPath); // Initialise the AppRecArray DoReadINIIntoAppRecArray; // Use last saved Profile at startup iCurrentRecIndex := mainform.INI.ReadInteger('ProgramInfo', 'CurrentProfileIndex', 0); DoSetupLongTimerArray; // Sort out the TrayIcon stuff TrayIcon1.Icon := Application.Icon; TrayIcon1.BalloonTitle := Format(rs_balloontitle, [Application.Title]); TrayIcon1.Hint := Application.Title; TrayIcon1.ShowIcon := True; TrayIcon1.Show; TrayIcon1.BalloonHint := Format(rs_trayloaded, [Application.Title]); TrayIcon1.ShowBalloonHint; LazAutoUpdate1.DebugMode := DebugMode; mnu_SelfCheck.Visible := DebugMode; end; procedure Tmainform.FormCloseQuery(Sender: TObject; var CanClose: boolean); var i: integer; begin if LazAutoUpdate1.DownloadInprogress then begin ShowMessage('Please wait. Update in progress'); CanClose := False; end else // Clean up memory No leaks (V0.1.14) begin if DebugMode then begin Logger.Log('User closed application'); Logger.Active := False; FreeAndNil(Logger); end; CanClose := True; end; if CanClose then begin // Clean up any LongTimers for i := Low(LongTimerArray) to High(LongTimerArray) do if LongTimerArray[i] <> nil then begin LongTimerArray[i].Enabled := False; FreeAndNil(LongTimerArray[i]); end; SetLength(AppRecArray, 0); FreeAndNil(AppRecArray); FreeAndNil(INI); end; end; end.