installer: uninstall the official Notepad2 installer when installing

TODO: simplify it

;* Notepad2-mod - Installer script
;*
;* Copyright (C) 2010-2011 XhmikosR
;*
;* This file is part of Notepad2-mod.
;*
;* See License.txt for details.

; Requirements:
; Inno Setup v5.4.2(+): http://www.jrsoftware.org/isdl.php

; $Id$

;#define ICL12
;#define VS2010
;#define WDK

; Preprocessor related stuff
#if VER < 0x05040200
  #error Update your Inno Setup version
#endif

#if !defined(ICL12) && !defined(VS2010) && !defined(WDK)
  #error You need to define ICL12 or VS2010 or WDK first
#endif

#if defined(ICL12) && (defined(VS2010) || defined(WDK))
  #error You can't define ICL12 and at the same time
#endif

#if defined(VS2010) && ( defined(ICL12) || defined(WDK))
  #error You can't define WDK and at the same time
#endif

#if defined(WDK) && (defined(ICL12) || defined(VS2010))
  #error You can't use two defines at the same time
#endif

#if defined(ICL12)
  #define compiler "ICL12"
  #define sse2_required
#elif defined(VS2010)
  #define compiler "VS2010"
#elif defined(WDK)
  #define compiler "WDK"
#endif

#define bindir       "..\bin\" + compiler

#ifnexist SourcePath + bindir + "\Release_x86\Notepad2.exe"
  #error Compile Notepad2 x86 first
#endif

#ifnexist SourcePath + bindir + "\Release_x64\Notepad2.exe"
  #error Compile Notepad2 x64 first
#endif

#define VerMajor
#define VerMinor
#define VerBuild
#define VerRevision

#expr ParseVersion(bindir + "\Release_x86\Notepad2.exe", VerMajor, VerMinor, VerBuild, VerRevision)
#define app_version  str(VerMajor) + "." + str(VerMinor) + "." + str(VerBuild) + "." + str(VerRevision)
#define app_name     "Notepad2-mod"
#define quick_launch "{userappdata}\Microsoft\Internet Explorer\Quick Launch"
#define IFEO         "SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\notepad.exe"

[Setup]
AppId={#app_name}
AppName={#app_name}
AppVersion={#app_version}
AppVerName={#app_name} {#app_version}
AppPublisher=XhmikosR
AppPublisherURL=http://code.google.com/p/notepad2-mod/
AppSupportURL=http://code.google.com/p/notepad2-mod/
AppUpdatesURL=http://code.google.com/p/notepad2-mod/
AppContact=http://code.google.com/p/notepad2-mod/
AppCopyright=Copyright  2004-2011, Florian Balmer et al.
VersionInfoCompany=XhmikosR
VersionInfoCopyright=Copyright  2004-2011, Florian Balmer et al.
VersionInfoDescription={#app_name} {#app_version} Setup
VersionInfoTextVersion={#app_version}
VersionInfoVersion={#app_version}
VersionInfoProductName={#app_name}
VersionInfoProductVersion={#app_version}
VersionInfoProductTextVersion={#app_version}
UninstallDisplayIcon={app}\Notepad2.exe
UninstallDisplayName={#app_name} {#app_version} ({#compiler})
DefaultDirName={pf}\Notepad2
LicenseFile=license.txt
OutputDir=.
OutputBaseFilename={#app_name}.{#app_version}_{#compiler}
SetupIconFile=Setup.ico
WizardImageFile=compiler:WizModernImage-IS.bmp
WizardSmallImageFile=WizardSmallImageFile.bmp
SolidCompression=yes
EnableDirDoesntExistWarning=no
AllowNoIcons=yes
ShowTasksTreeLines=yes
DisableDirPage=yes
DisableProgramGroupPage=yes
DisableReadyPage=yes
DisableWelcomePage=yes
AllowCancelDuringInstall=no
#if defined(WDK)
MinVersion=0,5.0
#else
MinVersion=0,5.1.2600sp3
#endif
ArchitecturesAllowed=x86 x64
ArchitecturesInstallIn64BitMode=x64

[Languages]
Name: en; MessagesFile: compiler:Default.isl

[Messages]
BeveledLabel     ={#app_name} {#app_version}  -  Compiled with {#compiler}
SetupAppTitle    =Setup - {#app_name}
SetupWindowTitle =Setup - {#app_name}

[CustomMessages]
en.msg_AppIsRunning          =Setup has detected that {#app_name} is currently running.%n%nPlease close all instances of it now, then click OK to continue, or Cancel to exit.
en.msg_AppIsRunningUninstall =Uninstall has detected that {#app_name} is currently running.%n%nPlease close all instances of it now, then click OK to continue, or Cancel to exit.
en.msg_DeleteSettings        =Do you also want to delete {#app_name}'s settings?%n%nIf you plan on installing {#app_name} again then you do not have to delete them.
en.msg_SetupIsRunningWarning ={#app_name} setup is already running!
#if defined(sse_required)
en.msg_simd_sse              =This build of {#app_name} requires a CPU with SSE extension support.%n%nYour CPU does not have those capabilities.
#elif defined(sse2_required)
en.msg_simd_sse2             =This build of {#app_name} requires a CPU with SSE2 extension support.%n%nYour CPU does not have those capabilities.
#endif
en.tsk_AllUsers              =For all users
en.tsk_CurrentUser           =For the current user only
en.tsk_Other                 =Other tasks:
en.tsk_ResetSettings         =Reset {#app_name}'s settings
en.tsk_RemoveDefault         =Restore Windows notepad
en.tsk_SetDefault            =Replace Windows notepad with {#app_name}

[Tasks]
Name: desktopicon;        Description: {cm:CreateDesktopIcon};     GroupDescription: {cm:AdditionalIcons}; Flags: unchecked
Name: desktopicon\user;   Description: {cm:tsk_CurrentUser};       GroupDescription: {cm:AdditionalIcons}; Flags: unchecked exclusive
Name: desktopicon\common; Description: {cm:tsk_AllUsers};          GroupDescription: {cm:AdditionalIcons}; Flags: unchecked exclusive
Name: quicklaunchicon;    Description: {cm:CreateQuickLaunchIcon}; GroupDescription: {cm:AdditionalIcons}; Flags: unchecked;             OnlyBelowVersion: 0,6.01
Name: reset_settings;     Description: {cm:tsk_ResetSettings};     GroupDescription: {cm:tsk_Other};       Flags: checkedonce unchecked; Check: SettingsExistCheck()
Name: set_default;        Description: {cm:tsk_SetDefault};        GroupDescription: {cm:tsk_Other};                                     Check: not DefaulNotepadCheck()
Name: remove_default;     Description: {cm:tsk_RemoveDefault};     GroupDescription: {cm:tsk_Other};       Flags: checkedonce unchecked; Check: DefaulNotepadCheck()

[Files]
Source: psvince.dll;                        DestDir: {app};                  Flags: ignoreversion
Source: ..\License.txt;                     DestDir: {app};                  Flags: ignoreversion
Source: {#bindir}\Release_x64\Notepad2.exe; DestDir: {app};                  Flags: ignoreversion;                         Check: Is64BitInstallMode()
Source: {#bindir}\Release_x86\Notepad2.exe; DestDir: {app};                  Flags: ignoreversion;                         Check: not Is64BitInstallMode()
Source: Notepad2.ini;                       DestDir: {userappdata}\Notepad2; Flags: onlyifdoesntexist uninsneveruninstall
Source: ..\Notepad2.txt;                    DestDir: {app};                  Flags: ignoreversion
Source: ..\Readme.txt;                      DestDir: {app};                  Flags: ignoreversion
Source: ..\Readme-mod.txt;                  DestDir: {app};                  Flags: ignoreversion

[Icons]
Name: {commondesktop}\{#app_name}; Filename: {app}\Notepad2.exe; Tasks: desktopicon\common; Comment: {#app_name} {#app_version}; WorkingDir: {app}; AppUserModelID: Notepad2; IconFilename: {app}\Notepad2.exe; IconIndex: 0
Name: {userdesktop}\{#app_name};   Filename: {app}\Notepad2.exe; Tasks: desktopicon\user;   Comment: {#app_name} {#app_version}; WorkingDir: {app}; AppUserModelID: Notepad2; IconFilename: {app}\Notepad2.exe; IconIndex: 0
Name: {#quick_launch}\{#app_name}; Filename: {app}\Notepad2.exe; Tasks: quicklaunchicon;    Comment: {#app_name} {#app_version}; WorkingDir: {app};                           IconFilename: {app}\Notepad2.exe; IconIndex: 0

[INI]
Filename: {app}\Notepad2.ini; Section: Notepad2; Key: Notepad2.ini; String: %APPDATA%\Notepad2\Notepad2.ini

[Run]
Filename: {app}\Notepad2.exe; Description: {cm:LaunchProgram,{#app_name}}; WorkingDir: {app}; Flags: nowait postinstall skipifsilent unchecked

[InstallDelete]
Type: files;      Name: {userdesktop}\{#app_name}.lnk;   Check: not IsTaskSelected('desktopicon\user')   and IsUpgrade()
Type: files;      Name: {commondesktop}\{#app_name}.lnk; Check: not IsTaskSelected('desktopicon\common') and IsUpgrade()
Type: files;      Name: {#quick_launch}\{#app_name}.lnk; Check: not IsTaskSelected('quicklaunchicon')    and IsUpgrade(); OnlyBelowVersion: 0,6.01
Type: files;      Name: {app}\Notepad2.ini;              Check: IsUpgrade()

[UninstallDelete]
Type: files;      Name: {app}\Notepad2.ini
Type: dirifempty; Name: {app}

[Code]
// Global variables/constants and general functions
const installer_mutex_name = '{#app_name}' + '_setup_mutex';

function IsModuleLoaded(modulename: AnsiString): Boolean;
external 'IsModuleLoaded2@files:psvince.dll stdcall setuponly';

function IsModuleLoadedU(modulename: AnsiString): Boolean;
external 'IsModuleLoaded2@{app}\psvince.dll stdcall uninstallonly';

#if defined(sse_required) || defined(sse2_required)
function IsProcessorFeaturePresent(Feature: Integer): Boolean;
external 'IsProcessorFeaturePresent@kernel32.dll stdcall';
#endif

////////////////////////////////////////
//  Custom functions and procedures   //
////////////////////////////////////////

// Check if Notepad2 has replaced Windows Notepad
function DefaulNotepadCheck(): Boolean;
var
  sDebugger: String;
begin
  if RegQueryStringValue(HKLM, '{#IFEO}', 'Debugger', sDebugger) then begin
    if sDebugger = (ExpandConstant('"{app}\Notepad2.exe" /z')) then begin
      Log('Custom Code: {#app_name} is set as the default notepad');
      Result := True;
    end
    else begin
      Log('Custom Code: {#app_name} is NOT set as the default notepad');
      Result := False;
    end;
  end;
end;

#if defined(sse_required)
function Is_SSE_Supported(): Boolean;
begin
  // PF_XMMI_INSTRUCTIONS_AVAILABLE
  Result := IsProcessorFeaturePresent(6);
end;

#elif defined(sse2_required)

function Is_SSE2_Supported(): Boolean;
begin
  // PF_XMMI64_INSTRUCTIONS_AVAILABLE
  Result := IsProcessorFeaturePresent(10);
end;

#endif

function IsOldBuildInstalled(): Boolean;
begin
  if RegKeyExists(HKLM, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Notepad2') and
  FileExists(ExpandConstant('{pf}\Notepad2\Uninstall.inf')) then begin
    Log('Custom Code: The old build is installed');
    Result := True;
  end
  else begin
    Log('Custom Code: The old build is NOT installed');
    Result := False;
  end;
end;

function IsOfficialBuildInstalled(): Boolean;
begin
  if RegKeyExists(HKLM, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Notepad2') and
  FileExists(ExpandConstant('{pf}\Notepad2\Notepad2.inf')) then begin
    Log('Custom Code: The official Notepad2 build is installed');
    Result := True;
  end
  else begin
    Log('Custom Code: The official Notepad2 build is NOT installed');
    Result := False;
  end;
end;

function IsUpgrade(): Boolean;
var
  sPrevPath: String;
begin
  sPrevPath := WizardForm.PrevAppDir;
  Result := (sPrevPath <> '');
end;

// Check if Notepad2's settings exist
function SettingsExistCheck(): Boolean;
begin
  if FileExists(ExpandConstant('{userappdata}\Notepad2\Notepad2.ini')) then begin
    Log('Custom Code: Settings are present');
    Result := True;
  end
  else begin
    Log('Custom Code: Settings are NOT present');
    Result := False;
  end;
end;

function UninstallOldVersion(): Integer;
var
  iResultCode: Integer;
begin
  // Return Values:
  // 0 - no idea
  // 1 - error executing the command
  // 2 - successfully executed the command

  // default return value
  Log('Custom Code: Will try to uninstall the old build');
  Result := 0;
    if Exec('rundll32.exe', ExpandConstant('advpack.dll,LaunchINFSectionEx "{pf}\Notepad2\Uninstall.inf",DefaultUninstall,,8,N'), '', SW_HIDE, ewWaitUntilTerminated, iResultCode) then begin
      Result := 2;
      Sleep(200);
      Log('Custom Code: The old build was successfully uninstalled');
    end
    else begin
      Result := 1;
      Log('Custom Code: Something went wrong when uninstalling the old build');
    end;
end;

function UninstallOfficialVersion(): Integer;
var
  iResultCode: Integer;
begin
  // Return Values:
  // 0 - no idea
  // 1 - error executing the command
  // 2 - successfully executed the command

  // default return value
  Log('Custom Code: Will try to uninstall the old build');
  Result := 0;
    if Exec('rundll32.exe', ExpandConstant('advpack.dll,LaunchINFSectionEx "{pf}\Notepad2\Notepad2.inf",DefaultUninstall,,8,N'), '', SW_HIDE, ewWaitUntilTerminated, iResultCode) then begin
      Result := 2;
      Sleep(200);
      Log('Custom Code: The official Notepad2 build was successfully uninstalled');
    end
    else begin
      Result := 1;
      Log('Custom Code: Something went wrong when uninstalling the official Notepad2 build');
    end;
end;

function ShouldSkipPage(PageID: Integer): Boolean;
begin
  // Hide the license page
  if IsUpgrade() and (PageID = wpLicense) then
    Result := True
  else
    Result := False;
end;

procedure AddReg();
begin
  RegWriteStringValue(HKCR, 'Applications\notepad2.exe', 'AppUserModelID', 'Notepad2');
  RegWriteStringValue(HKCR, 'Applications\notepad2.exe\shell\open\command', '', ExpandConstant('"{app}\Notepad2.exe" %1'));
  RegWriteStringValue(HKCR, '*\OpenWithList\notepad2.exe', '', '');
end;

procedure RemoveReg();
begin
  RegDeleteKeyIncludingSubkeys(HKCR, 'Applications\notepad2.exe');
  RegDeleteKeyIncludingSubkeys(HKCR, '*\OpenWithList\notepad2.exe');
end;

procedure CleanUpSettings();
begin
  DeleteFile(ExpandConstant('{userappdata}\Notepad2\Notepad2.ini'));
  RemoveDir(ExpandConstant('{userappdata}\Notepad2'));
end;

procedure CurPageChanged(CurPageID: Integer);
begin
  if CurPageID = wpSelectTasks then
    WizardForm.NextButton.Caption := SetupMessage(msgButtonInstall)
  else if CurPageID = wpFinished then
    WizardForm.NextButton.Caption := SetupMessage(msgButtonFinish);
end;

procedure CurStepChanged(CurStep: TSetupStep);
begin

  if CurStep = ssInstall then begin
    if IsOldBuildInstalled() then begin
      UninstallOldVersion();
      // This is the case where the old build is installed; the DefaulNotepadCheck() returns true
      // and the set_default task isn't selected
      if not IsTaskSelected('remove_default') then
        RegWriteStringValue(HKLM, '{#IFEO}', 'Debugger', ExpandConstant('"{app}\Notepad2.exe" /z'));
    end;
    if IsOfficialBuildInstalled() then begin
      UninstallOfficialVersion();
      // This is the case where the old build is installed; the DefaulNotepadCheck() returns true
      // and the set_default task isn't selected
      if not IsTaskSelected('remove_default') then
        RegWriteStringValue(HKLM, '{#IFEO}', 'Debugger', ExpandConstant('"{app}\Notepad2.exe" /z'));
    end;
  end;

  if CurStep = ssPostInstall then begin
    if IsTaskSelected('reset_settings') then
      CleanUpSettings();

    if IsTaskSelected('set_default') then
      RegWriteStringValue(HKLM, '{#IFEO}', 'Debugger', ExpandConstant('"{app}\Notepad2.exe" /z'));

    if IsTaskSelected('remove_default') then begin
      RegDeleteValue(HKLM, '{#IFEO}', 'Debugger');
      RegDeleteKeyIfEmpty(HKLM, '{#IFEO}');
    end;

    // Always add Notepad2's AppUserModelID and the rest registry values
    AddReg();
  end;

end;

procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
begin
  // When uninstalling, ask the user to delete Notepad2's settings
  if CurUninstallStep = usUninstall then begin
    if SettingsExistCheck() then begin
      if SuppressibleMsgBox(CustomMessage('msg_DeleteSettings'), mbConfirmation, MB_YESNO or MB_DEFBUTTON2, IDNO) = IDYES then begin
        CleanUpSettings();
      end;
    end;

    if DefaulNotepadCheck() then
      RegDeleteValue(HKLM, '{#IFEO}', 'Debugger');
    RegDeleteKeyIfEmpty(HKLM, '{#IFEO}');
    RemoveReg();

  end;
end;

procedure InitializeWizard();
begin
  WizardForm.SelectTasksLabel.Hide;
  WizardForm.TasksList.Top    := 0;
  WizardForm.TasksList.Height := PageFromID(wpSelectTasks).SurfaceHeight;
end;

function InitializeSetup(): Boolean;
var
  iMsgBoxResult: Integer;
begin
  // Create a mutex for the installer and if it's already running then show a message and stop installation
  if CheckForMutexes(installer_mutex_name) and not WizardSilent() then begin
    SuppressibleMsgBox(CustomMessage('msg_SetupIsRunningWarning'), mbError, MB_OK, MB_OK);
    Result := False;
  end
  else begin
    Result := True;
    CreateMutex(installer_mutex_name);

    while IsModuleLoaded('Notepad2.exe') and (iMsgBoxResult <> IDCANCEL) do
      iMsgBoxResult := SuppressibleMsgBox(CustomMessage('msg_AppIsRunning'), mbError, MB_OKCANCEL, IDCANCEL);

    if iMsgBoxResult = IDCANCEL then
      Result := False;

#if defined(sse2_required)
    if not Is_SSE2_Supported() then begin
      SuppressibleMsgBox(CustomMessage('msg_simd_sse2'), mbCriticalError, MB_OK, MB_OK);
      Result := False;
    end;
#elif defined(sse_required)
    if not Is_SSE_Supported() then begin
      SuppressibleMsgBox(CustomMessage('msg_simd_sse'), mbCriticalError, MB_OK, MB_OK);
      Result := False;
    end;
#endif

  end;
end;

function InitializeUninstall(): Boolean;
var
  iMsgBoxResult: Integer;
begin
  if CheckForMutexes(installer_mutex_name) then begin
    SuppressibleMsgBox(CustomMessage('msg_SetupIsRunningWarning'), mbError, MB_OK, MB_OK);
    Result := False;
  end
  else begin
    Result := True;
    CreateMutex(installer_mutex_name);

    // Check if app is running during uninstallation
    while IsModuleLoadedU('Notepad2.exe') and (iMsgBoxResult <> IDCANCEL) do
      iMsgBoxResult := SuppressibleMsgBox(CustomMessage('msg_AppIsRunningUninstall'), mbError, MB_OKCANCEL, IDCANCEL);

    if iMsgBoxResult = IDCANCEL then
      Result := False;

    // Unload psvince.dll in order to be uninstalled
    UnloadDLL(ExpandConstant('{app}\psvince.dll'));
  end;
end;

;* Notepad2-mod - Installer script
;*
;* Copyright (C) 2010-2011 XhmikosR
;*
;* This file is part of Notepad2-mod.
;*
;* See License.txt for details.

; Requirements:
; Inno Setup v5.4.2(+): http://www.jrsoftware.org/isdl.php

; $Id$

;#define ICL12
;#define VS2010
;#define WDK

; Preprocessor related stuff
#if VER < 0x05040200
  #error Update your Inno Setup version
#endif

#if !defined(ICL12) && !defined(VS2010) && !defined(WDK)
  #error You need to define ICL12 or VS2010 or WDK first
#endif

#if defined(ICL12) && (defined(VS2010) || defined(WDK))
  #error You can't define ICL12 and at the same time
#endif

#if defined(VS2010) && ( defined(ICL12) || defined(WDK))
  #error You can't define WDK and at the same time
#endif

#if defined(WDK) && (defined(ICL12) || defined(VS2010))
  #error You can't use two defines at the same time
#endif

#if defined(ICL12)
  #define compiler "ICL12"
  #define sse2_required
#elif defined(VS2010)
  #define compiler "VS2010"
#elif defined(WDK)
  #define compiler "WDK"
#endif

#define bindir       "..\bin\" + compiler

#ifnexist SourcePath + bindir + "\Release_x86\Notepad2.exe"
  #error Compile Notepad2 x86 first
#endif

#ifnexist SourcePath + bindir + "\Release_x64\Notepad2.exe"
  #error Compile Notepad2 x64 first
#endif

#define VerMajor
#define VerMinor
#define VerBuild
#define VerRevision

#expr ParseVersion(bindir + "\Release_x86\Notepad2.exe", VerMajor, VerMinor, VerBuild, VerRevision)
#define app_version  str(VerMajor) + "." + str(VerMinor) + "." + str(VerBuild) + "." + str(VerRevision)
#define app_name     "Notepad2-mod"
#define quick_launch "{userappdata}\Microsoft\Internet Explorer\Quick Launch"
#define IFEO         "SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\notepad.exe"

[Setup]
AppId={#app_name}
AppName={#app_name}
AppVersion={#app_version}
AppVerName={#app_name} {#app_version}
AppPublisher=XhmikosR
AppPublisherURL=http://code.google.com/p/notepad2-mod/
AppSupportURL=http://code.google.com/p/notepad2-mod/
AppUpdatesURL=http://code.google.com/p/notepad2-mod/
AppContact=http://code.google.com/p/notepad2-mod/
AppCopyright=Copyright  2004-2011, Florian Balmer et al.
VersionInfoCompany=XhmikosR
VersionInfoCopyright=Copyright  2004-2011, Florian Balmer et al.
VersionInfoDescription={#app_name} {#app_version} Setup
VersionInfoTextVersion={#app_version}
VersionInfoVersion={#app_version}
VersionInfoProductName={#app_name}
VersionInfoProductVersion={#app_version}
VersionInfoProductTextVersion={#app_version}
UninstallDisplayIcon={app}\Notepad2.exe
UninstallDisplayName={#app_name} {#app_version} ({#compiler})
DefaultDirName={pf}\Notepad2
LicenseFile=license.txt
OutputDir=.
OutputBaseFilename={#app_name}.{#app_version}_{#compiler}
SetupIconFile=Setup.ico
WizardImageFile=compiler:WizModernImage-IS.bmp
WizardSmallImageFile=WizardSmallImageFile.bmp
SolidCompression=yes
EnableDirDoesntExistWarning=no
AllowNoIcons=yes
ShowTasksTreeLines=yes
DisableDirPage=yes
DisableProgramGroupPage=yes
DisableReadyPage=yes
DisableWelcomePage=yes
AllowCancelDuringInstall=no
#if defined(WDK)
MinVersion=0,5.0
#else
MinVersion=0,5.1.2600sp3
#endif
ArchitecturesAllowed=x86 x64
ArchitecturesInstallIn64BitMode=x64

[Languages]
Name: en; MessagesFile: compiler:Default.isl

[Messages]
BeveledLabel     ={#app_name} {#app_version}  -  Compiled with {#compiler}
SetupAppTitle    =Setup - {#app_name}
SetupWindowTitle =Setup - {#app_name}

[CustomMessages]
en.msg_AppIsRunning          =Setup has detected that {#app_name} is currently running.%n%nPlease close all instances of it now, then click OK to continue, or Cancel to exit.
en.msg_AppIsRunningUninstall =Uninstall has detected that {#app_name} is currently running.%n%nPlease close all instances of it now, then click OK to continue, or Cancel to exit.
en.msg_DeleteSettings        =Do you also want to delete {#app_name}'s settings?%n%nIf you plan on installing {#app_name} again then you do not have to delete them.
en.msg_SetupIsRunningWarning ={#app_name} setup is already running!
#if defined(sse_required)
en.msg_simd_sse              =This build of {#app_name} requires a CPU with SSE extension support.%n%nYour CPU does not have those capabilities.
#elif defined(sse2_required)
en.msg_simd_sse2             =This build of {#app_name} requires a CPU with SSE2 extension support.%n%nYour CPU does not have those capabilities.
#endif
en.tsk_AllUsers              =For all users
en.tsk_CurrentUser           =For the current user only
en.tsk_Other                 =Other tasks:
en.tsk_ResetSettings         =Reset {#app_name}'s settings
en.tsk_RemoveDefault         =Restore Windows notepad
en.tsk_SetDefault            =Replace Windows notepad with {#app_name}

[Tasks]
Name: desktopicon;        Description: {cm:CreateDesktopIcon};     GroupDescription: {cm:AdditionalIcons}; Flags: unchecked
Name: desktopicon\user;   Description: {cm:tsk_CurrentUser};       GroupDescription: {cm:AdditionalIcons}; Flags: unchecked exclusive
Name: desktopicon\common; Description: {cm:tsk_AllUsers};          GroupDescription: {cm:AdditionalIcons}; Flags: unchecked exclusive
Name: quicklaunchicon;    Description: {cm:CreateQuickLaunchIcon}; GroupDescription: {cm:AdditionalIcons}; Flags: unchecked;             OnlyBelowVersion: 0,6.01
Name: reset_settings;     Description: {cm:tsk_ResetSettings};     GroupDescription: {cm:tsk_Other};       Flags: checkedonce unchecked; Check: SettingsExistCheck()
Name: set_default;        Description: {cm:tsk_SetDefault};        GroupDescription: {cm:tsk_Other};                                     Check: not DefaulNotepadCheck()
Name: remove_default;     Description: {cm:tsk_RemoveDefault};     GroupDescription: {cm:tsk_Other};       Flags: checkedonce unchecked; Check: DefaulNotepadCheck()

[Files]
Source: psvince.dll;                        DestDir: {app};                  Flags: ignoreversion
Source: ..\License.txt;                     DestDir: {app};                  Flags: ignoreversion
Source: {#bindir}\Release_x64\Notepad2.exe; DestDir: {app};                  Flags: ignoreversion;                         Check: Is64BitInstallMode()
Source: {#bindir}\Release_x86\Notepad2.exe; DestDir: {app};                  Flags: ignoreversion;                         Check: not Is64BitInstallMode()
Source: Notepad2.ini;                       DestDir: {userappdata}\Notepad2; Flags: onlyifdoesntexist uninsneveruninstall
Source: ..\Notepad2.txt;                    DestDir: {app};                  Flags: ignoreversion
Source: ..\Readme.txt;                      DestDir: {app};                  Flags: ignoreversion
Source: ..\Readme-mod.txt;                  DestDir: {app};                  Flags: ignoreversion

[Icons]
Name: {commondesktop}\{#app_name}; Filename: {app}\Notepad2.exe; Tasks: desktopicon\common; Comment: {#app_name} {#app_version}; WorkingDir: {app}; AppUserModelID: Notepad2; IconFilename: {app}\Notepad2.exe; IconIndex: 0
Name: {userdesktop}\{#app_name};   Filename: {app}\Notepad2.exe; Tasks: desktopicon\user;   Comment: {#app_name} {#app_version}; WorkingDir: {app}; AppUserModelID: Notepad2; IconFilename: {app}\Notepad2.exe; IconIndex: 0
Name: {#quick_launch}\{#app_name}; Filename: {app}\Notepad2.exe; Tasks: quicklaunchicon;    Comment: {#app_name} {#app_version}; WorkingDir: {app};                           IconFilename: {app}\Notepad2.exe; IconIndex: 0

[INI]
Filename: {app}\Notepad2.ini; Section: Notepad2; Key: Notepad2.ini; String: %APPDATA%\Notepad2\Notepad2.ini

[Run]
Filename: {app}\Notepad2.exe; Description: {cm:LaunchProgram,{#app_name}}; WorkingDir: {app}; Flags: nowait postinstall skipifsilent unchecked

[InstallDelete]
Type: files;      Name: {userdesktop}\{#app_name}.lnk;   Check: not IsTaskSelected('desktopicon\user')   and IsUpgrade()
Type: files;      Name: {commondesktop}\{#app_name}.lnk; Check: not IsTaskSelected('desktopicon\common') and IsUpgrade()
Type: files;      Name: {#quick_launch}\{#app_name}.lnk; Check: not IsTaskSelected('quicklaunchicon')    and IsUpgrade(); OnlyBelowVersion: 0,6.01
Type: files;      Name: {app}\Notepad2.ini;              Check: IsUpgrade()

[UninstallDelete]
Type: files;      Name: {app}\Notepad2.ini
Type: dirifempty; Name: {app}

[Code]
// Global variables/constants and general functions
const installer_mutex_name = '{#app_name}' + '_setup_mutex';

function IsModuleLoaded(modulename: AnsiString): Boolean;
external 'IsModuleLoaded2@files:psvince.dll stdcall setuponly';

function IsModuleLoadedU(modulename: AnsiString): Boolean;
external 'IsModuleLoaded2@{app}\psvince.dll stdcall uninstallonly';

#if defined(sse_required) || defined(sse2_required)
function IsProcessorFeaturePresent(Feature: Integer): Boolean;
external 'IsProcessorFeaturePresent@kernel32.dll stdcall';
#endif

////////////////////////////////////////
//  Custom functions and procedures   //
////////////////////////////////////////

// Check if Notepad2 has replaced Windows Notepad
function DefaulNotepadCheck(): Boolean;
var
  sDebugger: String;
begin
  if RegQueryStringValue(HKLM, '{#IFEO}', 'Debugger', sDebugger) then begin
    if sDebugger = (ExpandConstant('"{app}\Notepad2.exe" /z')) then begin
      Log('Custom Code: {#app_name} is set as the default notepad');
      Result := True;
    end
    else begin
      Log('Custom Code: {#app_name} is NOT set as the default notepad');
      Result := False;
    end;
  end;
end;

#if defined(sse_required)
function Is_SSE_Supported(): Boolean;
begin
  // PF_XMMI_INSTRUCTIONS_AVAILABLE
  Result := IsProcessorFeaturePresent(6);
end;

#elif defined(sse2_required)

function Is_SSE2_Supported(): Boolean;
begin
  // PF_XMMI64_INSTRUCTIONS_AVAILABLE
  Result := IsProcessorFeaturePresent(10);
end;

#endif

function IsOldBuildInstalled(): Boolean;
begin
  if RegKeyExists(HKLM, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Notepad2') and
  FileExists(ExpandConstant('{pf}\Notepad2\Uninstall.inf')) then begin
    Log('Custom Code: The old build is installed');
    Result := True;
  end
  else begin
    Log('Custom Code: The old build is NOT installed');
    Result := False;
  end;
end;

function IsUpgrade(): Boolean;
var
  sPrevPath: String;
begin
  sPrevPath := WizardForm.PrevAppDir;
  Result := (sPrevPath <> '');
end;

// Check if Notepad2's settings exist
function SettingsExistCheck(): Boolean;
begin
  if FileExists(ExpandConstant('{userappdata}\Notepad2\Notepad2.ini')) then begin
    Log('Custom Code: Settings are present');
    Result := True;
  end
  else begin
    Log('Custom Code: Settings are NOT present');
    Result := False;
  end;
end;

function UninstallOldVersion(): Integer;
var
  iResultCode: Integer;
begin
  // Return Values:
  // 0 - no idea
  // 1 - error executing the command
  // 2 - successfully executed the command

  // default return value
  Log('Custom Code: Will try to uninstall the old build');
  Result := 0;
    if Exec('rundll32.exe', ExpandConstant('advpack.dll,LaunchINFSectionEx "{pf}\Notepad2\Uninstall.inf",DefaultUninstall,,8,N'), '', SW_HIDE, ewWaitUntilTerminated, iResultCode) then begin
      Result := 2;
      Sleep(200);
      Log('Custom Code: The old build was successfully uninstalled');
    end
    else begin
      Result := 1;
      Log('Custom Code: Something went wrong when uninstalling the old build');
    end;
end;

function ShouldSkipPage(PageID: Integer): Boolean;
begin
  // Hide the license page
  if IsUpgrade() and (PageID = wpLicense) then
    Result := True
  else
    Result := False;
end;

procedure AddReg();
begin
  RegWriteStringValue(HKCR, 'Applications\notepad2.exe', 'AppUserModelID', 'Notepad2');
  RegWriteStringValue(HKCR, 'Applications\notepad2.exe\shell\open\command', '', ExpandConstant('"{app}\Notepad2.exe" %1'));
  RegWriteStringValue(HKCR, '*\OpenWithList\notepad2.exe', '', '');
end;

procedure RemoveReg();
begin
  RegDeleteKeyIncludingSubkeys(HKCR, 'Applications\notepad2.exe');
  RegDeleteKeyIncludingSubkeys(HKCR, '*\OpenWithList\notepad2.exe');
end;

procedure CleanUpSettings();
begin
  DeleteFile(ExpandConstant('{userappdata}\Notepad2\Notepad2.ini'));
  RemoveDir(ExpandConstant('{userappdata}\Notepad2'));
end;

procedure CurPageChanged(CurPageID: Integer);
begin
  if CurPageID = wpSelectTasks then
    WizardForm.NextButton.Caption := SetupMessage(msgButtonInstall)
  else if CurPageID = wpFinished then
    WizardForm.NextButton.Caption := SetupMessage(msgButtonFinish);
end;

procedure CurStepChanged(CurStep: TSetupStep);
begin

  if CurStep = ssInstall then begin
    if IsOldBuildInstalled() then begin
      UninstallOldVersion();
      // This is the case where the old build is installed; the DefaulNotepadCheck() returns true
      // and the set_default task isn't selected
      if not IsTaskSelected('remove_default') then
        RegWriteStringValue(HKLM, '{#IFEO}', 'Debugger', ExpandConstant('"{app}\Notepad2.exe" /z'));
    end;
  end;

  if CurStep = ssPostInstall then begin
    if IsTaskSelected('reset_settings') then
      CleanUpSettings();

    if IsTaskSelected('set_default') then
      RegWriteStringValue(HKLM, '{#IFEO}', 'Debugger', ExpandConstant('"{app}\Notepad2.exe" /z'));

    if IsTaskSelected('remove_default') then begin
      RegDeleteValue(HKLM, '{#IFEO}', 'Debugger');
      RegDeleteKeyIfEmpty(HKLM, '{#IFEO}');
    end;

    // Always add Notepad2's AppUserModelID and the rest registry values
    AddReg();
  end;

end;

procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
begin
  // When uninstalling, ask the user to delete Notepad2's settings
  if CurUninstallStep = usUninstall then begin
    if SettingsExistCheck() then begin
      if SuppressibleMsgBox(CustomMessage('msg_DeleteSettings'), mbConfirmation, MB_YESNO or MB_DEFBUTTON2, IDNO) = IDYES then begin
        CleanUpSettings();
      end;
    end;

    if DefaulNotepadCheck() then
      RegDeleteValue(HKLM, '{#IFEO}', 'Debugger');
    RegDeleteKeyIfEmpty(HKLM, '{#IFEO}');
    RemoveReg();

  end;
end;

procedure InitializeWizard();
begin
  WizardForm.SelectTasksLabel.Hide;
  WizardForm.TasksList.Top    := 0;
  WizardForm.TasksList.Height := PageFromID(wpSelectTasks).SurfaceHeight;
end;

function InitializeSetup(): Boolean;
var
  iMsgBoxResult: Integer;
begin
  // Create a mutex for the installer and if it's already running then show a message and stop installation
  if CheckForMutexes(installer_mutex_name) and not WizardSilent() then begin
    SuppressibleMsgBox(CustomMessage('msg_SetupIsRunningWarning'), mbError, MB_OK, MB_OK);
    Result := False;
  end
  else begin
    Result := True;
    CreateMutex(installer_mutex_name);

    while IsModuleLoaded('Notepad2.exe') and (iMsgBoxResult <> IDCANCEL) do
      iMsgBoxResult := SuppressibleMsgBox(CustomMessage('msg_AppIsRunning'), mbError, MB_OKCANCEL, IDCANCEL);

    if iMsgBoxResult = IDCANCEL then
      Result := False;

#if defined(sse2_required)
    if not Is_SSE2_Supported() then begin
      SuppressibleMsgBox(CustomMessage('msg_simd_sse2'), mbCriticalError, MB_OK, MB_OK);
      Result := False;
    end;
#elif defined(sse_required)
    if not Is_SSE_Supported() then begin
      SuppressibleMsgBox(CustomMessage('msg_simd_sse'), mbCriticalError, MB_OK, MB_OK);
      Result := False;
    end;
#endif

  end;
end;

function InitializeUninstall(): Boolean;
var
  iMsgBoxResult: Integer;
begin
  if CheckForMutexes(installer_mutex_name) then begin
    SuppressibleMsgBox(CustomMessage('msg_SetupIsRunningWarning'), mbError, MB_OK, MB_OK);
    Result := False;
  end
  else begin
    Result := True;
    CreateMutex(installer_mutex_name);

    // Check if app is running during uninstallation
    while IsModuleLoadedU('Notepad2.exe') and (iMsgBoxResult <> IDCANCEL) do
      iMsgBoxResult := SuppressibleMsgBox(CustomMessage('msg_AppIsRunningUninstall'), mbError, MB_OKCANCEL, IDCANCEL);

    if iMsgBoxResult = IDCANCEL then
      Result := False;

    // Unload psvince.dll in order to be uninstalled
    UnloadDLL(ExpandConstant('{app}\psvince.dll'));
  end;
end;


Only send survey if they actually input anything

#define AppExeName "SyncTrayzor.exe"
#define AppRoot "..\.."
#define AppSrc AppRoot + "\src\SyncTrayzor"
#define AppBin AppRoot +"\bin\" + Arch + "\Release"
#define AppExe AppBin + "\SyncTrayzor.exe"
#define AppName GetStringFileInfo(AppExe, "ProductName")
#define AppVersion GetFileVersion(AppExe)
#define AppPublisher "SyncTrayzor"
#define AppURL "https://github.com/canton7/SyncTrayzor"
#define AppDataFolder "SyncTrayzor"
#define RunRegKey "Software\Microsoft\Windows\CurrentVersion\Run"
#define DotNetInstallerExe "dotNet451Setup.exe"
#define DonateUrl "https://synctrayzor.antonymale.co.uk/donate"
#define SurveyUrl "https://synctrayzor.antonymale.co.uk/survey.php"

[Setup]
AppId={{#AppId}
AppName={#AppName} ({#Arch})
AppVersion={#AppVersion}
VersionInfoVersion={#AppVersion}
;AppVerName={#AppName} {#AppVersion}
AppPublisher={#AppPublisher}
AppPublisherURL={#AppURL}
AppSupportURL={#AppURL}
AppUpdatesURL={#AppURL}
DefaultDirName={pf}\{#AppName}
DefaultGroupName={#AppName}
AllowNoIcons=yes
LicenseFile={#AppRoot}\LICENSE.txt
OutputDir="."
OutputBaseFilename={#AppName}Setup-{#Arch}
SetupIconFile={#AppSrc}\Icons\default.ico
WizardSmallImageFile=..\icon.bmp
Compression=lzma2/max
;Compression=None
SolidCompression=yes
PrivilegesRequired=admin
CloseApplications=yes
RestartApplications=no
; If we try and close CefSharp.BrowserSubprocess.exe we'll fail - it doesn't respond well
; However if we close *just* SyncTrayzor, that will take care of shutting down CefSharp and syncthing
CloseApplicationsFilter=SyncTrayzor.exe
TouchDate=current
#if "x64" == Arch
ArchitecturesInstallIn64BitMode=x64
ArchitecturesAllowed=x64
#endif

[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"

[CustomMessages]
InstallingDotNetFramework=Installing .NET Framework. This might take a few minutes...
DotNetFrameworkFailedToLaunch=Failed to launch .NET Framework Installer with error "%1". Please fix the error then run this installer again.
DotNetFrameworkFailed1602=.NET Framework installation was cancelled. This installation can continue, but be aware that this application may not run unless the .NET Framework installation is completed successfully.
DotNetFrameworkFailed1603=A fatal error occurred while installing the .NET Framework. Please fix the error, then run the installer again.
DotNetFrameworkFailed5100=Your computer does not meet the requirements of the .NET Framework. Please consult the documentation.
DotNetFrameworkFailedOther=The .NET Framework installer exited with an unexpected status code "%1". Please review any other messages shown by the installer to determine whether the installation completed successfully, and abort this installation and fix the problem if it did not.

[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked

[Dirs]
Name: "{userappdata}\{#AppDataFolder}"

[Files]
; Near the beginning, as it's extracted first and this makes it cheaper
Source: "..\{#DotNetInstallerExe}"; DestDir: {tmp}; Flags: dontcopy nocompression noencryption

Source: "{#AppBin}\*"; DestDir: "{app}"; Excludes: "*.xml,*.vshost.*,*.config,*.log,FluentValidation.resources.dll,System.Windows.Interactivity.resources.dll,syncthing.exe,data,logs,ffmpegsumo.dll,d3dcompiler_43.dll,d3dcompiler_47.dll,libEGL.dll,libGLESv2.dll,pdf.dll"; Flags: ignoreversion recursesubdirs
Source: "{#AppBin}\SyncTrayzor.exe.Installer.config"; DestDir: "{app}"; DestName: "SyncTrayzor.exe.config"; Flags: ignoreversion
Source: "{#AppSrc}\Icons\default.ico"; DestDir: "{app}"; Flags: ignoreversion
Source: "{#AppRoot}\*.md"; DestDir: "{app}"; Flags: ignoreversion
Source: "{#AppRoot}\*.txt"; DestDir: "{app}"; Flags: ignoreversion
Source: "*.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "syncthing.exe"; DestDir: "{app}"; DestName: "syncthing.exe"; Flags: ignoreversion

[Icons]
Name: "{group}\{#AppName}"; Filename: "{app}\{#AppExeName}"
Name: "{group}\{cm:UninstallProgram,{#AppName}}"; Filename: "{uninstallexe}"
Name: "{commondesktop}\{#AppName}"; Filename: "{app}\{#AppExeName}"; Tasks: desktopicon

[Run]
Filename: "{app}\{#AppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(AppName, '&', '&&')}}"; Flags: nowait postinstall; Parameters: {code:SyncTrayzorStartFlags}; Check: ShouldStartSyncTrayzor

[Code]
var
  GlobalRestartRequired: boolean;
  UninstallPollPage: TNewNotebookPage;
  UninstallNextButton: TNewButton;

function DotNetIsMissing(): Boolean;
var
  Exists: Boolean;
  Release: Cardinal;
begin
  Exists := RegQueryDWordValue(HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full', 'Release', Release);
  Result := not Exists or (Release < 378758);
end;

// Adapted from https://blogs.msdn.microsoft.com/davidrickard/2015/07/17/installing-net-framework-4-5-automatically-with-inno-setup/
function InstallDotNet(): String;
var
  StatusText: string;
  ResultCode: Integer;
begin
  StatusText := WizardForm.StatusLabel.Caption;
  WizardForm.StatusLabel.Caption := CustomMessage('InstallingDotNetFramework');
  WizardForm.ProgressGauge.Style := npbstMarquee;
  try
    ExtractTemporaryFile('{#DotNetInstallerExe}');
    if not Exec(ExpandConstant('{tmp}\{#DotNetInstallerExe}'), '/passive /norestart /showrmui /showfinalerror', '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then
    begin
      Result := FmtMessage(CustomMessage('DotNetFrameworkFailedToLaunch'), [SysErrorMessage(ResultCode)]);
    end
    else
    begin
      // See https://msdn.microsoft.com/en-us/library/ee942965(v=vs.110).aspx#return_codes
      case resultCode of
        0: begin
          // Successful
        end;
        1602 : begin
          MsgBox(CustomMessage('DotNetFrameworkFailed1602'), mbInformation, MB_OK);
        end;
        1603: begin
          Result := CustomMessage('DotNetFrameworkFailed1603');
        end;
        1641: begin
          GlobalRestartRequired := True;
        end;
        3010: begin
          GlobalRestartRequired := True;
        end;
        5100: begin
          Result := CustomMessage('DotNetFrameworkFailed5100');
        end;
        else begin
          MsgBox(FmtMessage(CustomMessage('DotNetFrameworkFailedOther'), [IntToStr(ResultCode)]), mbError, MB_OK);
        end;
      end;
    end;
  finally
    WizardForm.StatusLabel.Caption := StatusText;
    WizardForm.ProgressGauge.Style := npbstNormal;
  end;
end;

procedure BumpInstallCount;
var
  FileContents: AnsiString;
  InstallCount: integer;
begin
  { Increment the install count in InstallCount.txt if it exists, or create it with the contents '1' if it doesn't }
  if LoadStringFromFile(ExpandConstant('{app}\InstallCount.txt'), FileContents) then
  begin
    InstallCount := StrTointDef(Trim(string(FileContents)), 0) + 1;
  end
  else
  begin
    InstallCount := 1;
  end;

  SaveStringToFile(ExpandConstant('{app}\InstallCount.txt'), IntToStr(InstallCount), False);
end;

procedure URLLabelOnClick(Sender: TObject);
var
	ErrorCode: Integer;
begin
	ShellExec('open', '{#DonateUrl}', '', '', SW_SHOWNORMAL, ewNoWait, ErrorCode);
end;

procedure InitializeWizard;
var
  URLLabel: TNewStaticText;
begin
  URLLabel := TNewStaticText.Create(WizardForm);
  URLLabel.Caption := 'Donate';
  URLLabel.Cursor := crHand;
  URLLabel.Parent := WizardForm;
  URLLabel.Font.Style := URLLabel.Font.Style + [fsUnderline];
	URLLabel.Font.Color := clBlue;
	URLLabel.Top := WizardForm.ClientHeight - URLLabel.Height - 15;
  URLLabel.Left := ScaleX(10)
  URLLabel.OnClick := @URLLabelOnClick;
end;

procedure CurStepChanged(CurStep: TSetupStep);
var
  FindRec: TFindRec;
  FolderPath: String;
  FilePath: String;
  ExeConfig: String;
begin
  if CurStep = ssInstall then
  begin
    BumpInstallCount();

    { We might be being run from ProcessRunner.exe, *and* we might be trying to update it. Funsies. Let's rename it (which Windows lets us do) }
    DeleteFile(ExpandConstant('{app}\ProcessRunner.exe.old'));
    RenameFile(ExpandConstant('{app}\ProcessRunner.exe'), ExpandConstant('{app}\ProcessRunner.exe.old'));

    Log(ExpandConstant('Looking for resource files in {app}\*'));
    { Remove resource files. This means that out-of-date languages will be removed, which (as a last-ditch resore) will alert maintainers that something's wrong }
    if FindFirst(ExpandConstant('{app}\*'), FindRec) then
    begin
      try
        repeat
          if (FindRec.Attributes and FILE_ATTRIBUTE_DIRECTORY <> 0) and (FindRec.Name <> '.') and (FindRec.Name <> '..') then
          begin
            FolderPath :=  ExpandConstant('{app}\') + FindRec.Name;
            FilePath := FolderPath + '\SyncTrayzor.resources.dll';
            if DeleteFile(FilePath) then
            begin
              Log('Deleted ' + FilePath);
              if DelTree(FolderPath, True, False, False) then
                Log('Deleted ' + FolderPath);
            end;
          end;
        until not FindNext(FindRec);
      finally
        FindClose(FindRec);
      end;
    end;
  end
  else if CurStep = ssPostInstall then
  begin
    ExeConfig := ExpandConstant('{param:SyncTrayzorExeConfig}');
    if ExeConfig <> '' then
    begin
      if FileExists(ExeConfig) then
      begin
        FileCopy(ExeConfig, ExpandConstant('{app}\SyncTrayzor.exe.config'), false);
      end
      else
      begin
        MsgBox('Could not find SyncTrayzorExeConfig file: ' + ExeConfig + '. Using default.', mbError, MB_OK);
      end
    end
  end
end;

procedure CurPageChanged(CurPageID: Integer);
begin

end;

function PrepareToInstall(var NeedsRestart: Boolean): String;
begin
  // 'NeedsRestart' only has an effect if we return a non-empty string, thus aborting the installation.
  // If the installers indicate that they want a restart, this should be done at the end of installation.
  // Therefore we set the global 'restartRequired' if a restart is needed, and return this from NeedRestart()

  if DotNetIsMissing() then
  begin
    Result := InstallDotNet();
  end;
end;

function NeedRestart(): Boolean;
begin
  Result := GlobalRestartRequired;
end;

function ShouldStartSyncTrayzor(): Boolean;
var
  flagPassed: Boolean;
  i: Integer;
begin
  // Can't use {param}, as it doesn't match flags with no value
  flagPassed := False;
  for i := 0 to ParamCount do begin
    if ParamStr(i) = '/StartSyncTrayzor' then begin
      flagPassed := True;
      break;
    end;
  end;
  Result := (not WizardSilent()) or flagPassed;
end;

function SyncTrayzorStartFlags(param: String): String;
begin
   if WizardSilent() then begin
      Result := '-minimized'
   end else begin
      Result := ''
   end;
end;

// See https://stackoverflow.com/a/42550055/1086121

procedure UpdateUninstallWizard;
begin
  if UninstallProgressForm.InnerNotebook.ActivePage = UninstallPollPage then
  begin
    UninstallProgressForm.PageNameLabel.Caption := 'Please Tell Us Why You''re Leaving';
    UninstallProgressForm.PageDescriptionLabel.Caption := '';
  end;

  UninstallNextButton.Caption := 'Uninstall';
  // Make the "Uninstall" button break the ShowModal loop
  UninstallNextButton.ModalResult := mrOK;
end;

function SerializeBool(value: Boolean): String;
begin
  if value then
  begin
    Result := 'true';
  end
  else
  begin
    Result := 'false';
  end
end;

function EscapeJsonString(value: String): String;
var
  c: Char;
  i: Integer;
begin
  // http://www.jrsoftware.org/ishelp/index.php?topic=isxfunc_charlength
  i := 1;
  while i <= Length(value) do
  begin
    c := value[i];
    if c = #10 then begin
      Result := Result + '\n';
    end else if c = #13 then begin
      Result := Result + '\r';
    end else if c = #9 then begin
      Result := Result + '\t';
    end else if Ord(c) < 32 then begin
      Result := Result + Format('\x%.4x', [Ord(c)]);
    end else if c = '"' then begin
      Result := Result + '\"';
    end else if c = '\' then begin
      Result := Result + '\\'
    end else begin
      Result := Result + c;
    end;
    i := i + CharLength(value, i);
  end;
end;

procedure InitializeUninstallProgressForm();
var
  PageText: TNewStaticText;
  PageNameLabel: string;
  PageDescriptionLabel: string;
  CancelButtonEnabled: Boolean;
  CancelButtonModalResult: Integer;
  Checklist: TNewCheckListBox;
  CommentsText: TNewStaticText;
  CommentsBox: TNewMemo;
  WinHttpReq: Variant;
begin
  if not UninstallSilent then
  begin
    // Create the poll page and make it active
    UninstallPollPage := TNewNotebookPage.Create(UninstallProgressForm);
    UninstallPollPage.Notebook := UninstallProgressForm.InnerNotebook;
    UninstallPollPage.Parent := UninstallProgressForm.InnerNotebook;
    UninstallPollPage.Align := alClient;

    PageText := TNewStaticText.Create(UninstallProgressForm);
    PageText.Parent := UninstallPollPage;
    PageText.AutoSize := True;
    PageText.WordWrap := True;
    PageText.SetBounds(UninstallProgressForm.StatusLabel.Left, UninstallProgressForm.StatusLabel.Top, UninstallProgressForm.StatusLabel.Width, UninstallProgressForm.StatusLabel.Height);
    PageText.ShowAccelChar := False;
    PageText.Caption := 'Please tell us why you don''t like Syncthing / SyncTrayzor so we can improve things.' + #13#10 +
    'No personal data will be sent. You can skip this step if you want.';

    Checklist := TNewCheckListBox.Create(UninstallProgressForm);
    Checklist.Parent := UninstallPollPage;
    Checklist.SetBounds(PageText.Left, PageText.Top + PageText.Height + ScaleY(10), PageText.Width, ScaleY(20) * 5);
    Checklist.BorderStyle := bsNone;
    Checklist.Color := clBtnFace;
    Checklist.WantTabs := True;
    Checklist.MinItemHeight := ScaleY(20);

    Checklist.AddCheckBox('I couldn''t get Syncthing to work', '', 0, False, True, False, False, nil);
    Checklist.AddCheckBox('Syncthing doesn''t do what I need', '', 0, False, True, False, False, nil);
    Checklist.AddCheckBox('I prefer Resilio Sync', '', 0, False, True, False, False, nil);
    Checklist.AddCheckBox('I don''t like SyncTrayzor - going to use another wrapper', '', 0, False, True, False, False, nil);
    Checklist.AddCheckBox('Other (please expand below)', '', 0, False, True, False, False, nil);

    CommentsText := TNewStaticText.Create(UninstallProgressForm);
    CommentsText.Parent := UninstallPollPage;
    CommentsText.AutoSize := True;
    CommentsText.WordWrap := True;
    CommentsText.SetBounds(PageText.Left, Checklist.Top + Checklist.Height + ScaleY(10), PageText.Width, ScaleY(15));
    CommentsText.AutoSize := False;
    CommentsText.ShowAccelChar := False;
    CommentsText.Caption := 'Anything else?';

    CommentsBox := TNewMemo.Create(UninstallProgressForm);
    CommentsBox.Parent := UninstallPollPage;
    CommentsBox.SetBounds(PageText.Left, CommentsText.Top + CommentsText.Height + ScaleY(5), PageText.Width, ScaleY(60));
    CommentsBox.ScrollBars := ssVertical;

    UninstallProgressForm.InnerNotebook.ActivePage := UninstallPollPage;

    PageNameLabel := UninstallProgressForm.PageNameLabel.Caption;
    PageDescriptionLabel := UninstallProgressForm.PageDescriptionLabel.Caption;

    UninstallNextButton := TNewButton.Create(UninstallProgressForm);
    UninstallNextButton.Parent := UninstallProgressForm;
    UninstallNextButton.Left := UninstallProgressForm.CancelButton.Left - UninstallProgressForm.CancelButton.Width - ScaleX(10);
    UninstallNextButton.Top := UninstallProgressForm.CancelButton.Top;
    UninstallNextButton.Width := UninstallProgressForm.CancelButton.Width;
    UninstallNextButton.Height := UninstallProgressForm.CancelButton.Height;

    UninstallProgressForm.CancelButton.TabOrder := UninstallNextButton.TabOrder + 1;

    // Run our wizard pages
    UpdateUninstallWizard;
    CancelButtonEnabled := UninstallProgressForm.CancelButton.Enabled
    UninstallProgressForm.CancelButton.Enabled := True;
    CancelButtonModalResult := UninstallProgressForm.CancelButton.ModalResult;
    UninstallProgressForm.CancelButton.ModalResult := mrCancel;

    if UninstallProgressForm.ShowModal = mrCancel then Abort;

    if Checklist.Checked[0] or Checklist.Checked[1] or Checklist.Checked[2] or Checklist.Checked[3] or Checklist.Checked[4] or (CommentsBox.Text <> '') then begin
      WinHttpReq := CreateOleObject('WinHttp.WinHttpRequest.5.1');
      WinHttpReq.Open('POST', '{#SurveyUrl}', false);
      WinHttpReq.Send('{' +
        ' "version": "{#AppVersion}", "comment": "' + EscapeJsonString(CommentsBox.Text) + '"' +
        ', "checklist": {' +
          ' "wontWork": '+ SerializeBool(Checklist.Checked[0]) +
          ', "notWhatINeed": '+ SerializeBool(Checklist.Checked[1]) +
          ', "preferResilio": '+ SerializeBool(Checklist.Checked[2]) +
          ', "dontLikeSyncTrayzor": '+ SerializeBool(Checklist.Checked[3]) +
          ', "other": '+ SerializeBool(Checklist.Checked[4]) +
        ' }' +
        ' }');
    end;

    // Restore the standard page payout
    UninstallProgressForm.CancelButton.Enabled := CancelButtonEnabled;
    UninstallProgressForm.CancelButton.ModalResult := CancelButtonModalResult;

    UninstallProgressForm.PageNameLabel.Caption := PageNameLabel;
    UninstallProgressForm.PageDescriptionLabel.Caption := PageDescriptionLabel;

    UninstallProgressForm.InnerNotebook.ActivePage := UninstallProgressForm.InstallingPage;
  end;
end;

[UninstallDelete]
Type: files; Name: "{app}\ProcessRunner.exe.old"
Type: files; Name: "{app}\InstallCount.txt"
Type: filesandordirs; Name: "{userappdata}\{#AppDataFolder}"
Type: filesandordirs; Name: "{localappdata}\{#AppDataFolder}"

#define AppExeName "SyncTrayzor.exe"
#define AppRoot "..\.."
#define AppSrc AppRoot + "\src\SyncTrayzor"
#define AppBin AppRoot +"\bin\" + Arch + "\Release"
#define AppExe AppBin + "\SyncTrayzor.exe"
#define AppName GetStringFileInfo(AppExe, "ProductName")
#define AppVersion GetFileVersion(AppExe)
#define AppPublisher "SyncTrayzor"
#define AppURL "https://github.com/canton7/SyncTrayzor"
#define AppDataFolder "SyncTrayzor"
#define RunRegKey "Software\Microsoft\Windows\CurrentVersion\Run"
#define DotNetInstallerExe "dotNet451Setup.exe"
#define DonateUrl "https://synctrayzor.antonymale.co.uk/donate"
#define SurveyUrl "https://synctrayzor.antonymale.co.uk/survey.php"

[Setup]
AppId={{#AppId}
AppName={#AppName} ({#Arch})
AppVersion={#AppVersion}
VersionInfoVersion={#AppVersion}
;AppVerName={#AppName} {#AppVersion}
AppPublisher={#AppPublisher}
AppPublisherURL={#AppURL}
AppSupportURL={#AppURL}
AppUpdatesURL={#AppURL}
DefaultDirName={pf}\{#AppName}
DefaultGroupName={#AppName}
AllowNoIcons=yes
LicenseFile={#AppRoot}\LICENSE.txt
OutputDir="."
OutputBaseFilename={#AppName}Setup-{#Arch}
SetupIconFile={#AppSrc}\Icons\default.ico
WizardSmallImageFile=..\icon.bmp
Compression=lzma2/max
;Compression=None
SolidCompression=yes
PrivilegesRequired=admin
CloseApplications=yes
RestartApplications=no
; If we try and close CefSharp.BrowserSubprocess.exe we'll fail - it doesn't respond well
; However if we close *just* SyncTrayzor, that will take care of shutting down CefSharp and syncthing
CloseApplicationsFilter=SyncTrayzor.exe
TouchDate=current
#if "x64" == Arch
ArchitecturesInstallIn64BitMode=x64
ArchitecturesAllowed=x64
#endif

[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"

[CustomMessages]
InstallingDotNetFramework=Installing .NET Framework. This might take a few minutes...
DotNetFrameworkFailedToLaunch=Failed to launch .NET Framework Installer with error "%1". Please fix the error then run this installer again.
DotNetFrameworkFailed1602=.NET Framework installation was cancelled. This installation can continue, but be aware that this application may not run unless the .NET Framework installation is completed successfully.
DotNetFrameworkFailed1603=A fatal error occurred while installing the .NET Framework. Please fix the error, then run the installer again.
DotNetFrameworkFailed5100=Your computer does not meet the requirements of the .NET Framework. Please consult the documentation.
DotNetFrameworkFailedOther=The .NET Framework installer exited with an unexpected status code "%1". Please review any other messages shown by the installer to determine whether the installation completed successfully, and abort this installation and fix the problem if it did not.

[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked

[Dirs]
Name: "{userappdata}\{#AppDataFolder}"

[Files]
; Near the beginning, as it's extracted first and this makes it cheaper
Source: "..\{#DotNetInstallerExe}"; DestDir: {tmp}; Flags: dontcopy nocompression noencryption

Source: "{#AppBin}\*"; DestDir: "{app}"; Excludes: "*.xml,*.vshost.*,*.config,*.log,FluentValidation.resources.dll,System.Windows.Interactivity.resources.dll,syncthing.exe,data,logs,ffmpegsumo.dll,d3dcompiler_43.dll,d3dcompiler_47.dll,libEGL.dll,libGLESv2.dll,pdf.dll"; Flags: ignoreversion recursesubdirs
Source: "{#AppBin}\SyncTrayzor.exe.Installer.config"; DestDir: "{app}"; DestName: "SyncTrayzor.exe.config"; Flags: ignoreversion
Source: "{#AppSrc}\Icons\default.ico"; DestDir: "{app}"; Flags: ignoreversion
Source: "{#AppRoot}\*.md"; DestDir: "{app}"; Flags: ignoreversion
Source: "{#AppRoot}\*.txt"; DestDir: "{app}"; Flags: ignoreversion
Source: "*.dll"; DestDir: "{app}"; Flags: ignoreversion
Source: "syncthing.exe"; DestDir: "{app}"; DestName: "syncthing.exe"; Flags: ignoreversion

[Icons]
Name: "{group}\{#AppName}"; Filename: "{app}\{#AppExeName}"
Name: "{group}\{cm:UninstallProgram,{#AppName}}"; Filename: "{uninstallexe}"
Name: "{commondesktop}\{#AppName}"; Filename: "{app}\{#AppExeName}"; Tasks: desktopicon

[Run]
Filename: "{app}\{#AppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(AppName, '&', '&&')}}"; Flags: nowait postinstall; Parameters: {code:SyncTrayzorStartFlags}; Check: ShouldStartSyncTrayzor

[Code]
var
  GlobalRestartRequired: boolean;
  UninstallPollPage: TNewNotebookPage;
  UninstallNextButton: TNewButton;

function DotNetIsMissing(): Boolean;
var
  Exists: Boolean;
  Release: Cardinal;
begin
  Exists := RegQueryDWordValue(HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full', 'Release', Release);
  Result := not Exists or (Release < 378758);
end;

// Adapted from https://blogs.msdn.microsoft.com/davidrickard/2015/07/17/installing-net-framework-4-5-automatically-with-inno-setup/
function InstallDotNet(): String;
var
  StatusText: string;
  ResultCode: Integer;
begin
  StatusText := WizardForm.StatusLabel.Caption;
  WizardForm.StatusLabel.Caption := CustomMessage('InstallingDotNetFramework');
  WizardForm.ProgressGauge.Style := npbstMarquee;
  try
    ExtractTemporaryFile('{#DotNetInstallerExe}');
    if not Exec(ExpandConstant('{tmp}\{#DotNetInstallerExe}'), '/passive /norestart /showrmui /showfinalerror', '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then
    begin
      Result := FmtMessage(CustomMessage('DotNetFrameworkFailedToLaunch'), [SysErrorMessage(ResultCode)]);
    end
    else
    begin
      // See https://msdn.microsoft.com/en-us/library/ee942965(v=vs.110).aspx#return_codes
      case resultCode of
        0: begin
          // Successful
        end;
        1602 : begin
          MsgBox(CustomMessage('DotNetFrameworkFailed1602'), mbInformation, MB_OK);
        end;
        1603: begin
          Result := CustomMessage('DotNetFrameworkFailed1603');
        end;
        1641: begin
          GlobalRestartRequired := True;
        end;
        3010: begin
          GlobalRestartRequired := True;
        end;
        5100: begin
          Result := CustomMessage('DotNetFrameworkFailed5100');
        end;
        else begin
          MsgBox(FmtMessage(CustomMessage('DotNetFrameworkFailedOther'), [IntToStr(ResultCode)]), mbError, MB_OK);
        end;
      end;
    end;
  finally
    WizardForm.StatusLabel.Caption := StatusText;
    WizardForm.ProgressGauge.Style := npbstNormal;
  end;
end;

procedure BumpInstallCount;
var
  FileContents: AnsiString;
  InstallCount: integer;
begin
  { Increment the install count in InstallCount.txt if it exists, or create it with the contents '1' if it doesn't }
  if LoadStringFromFile(ExpandConstant('{app}\InstallCount.txt'), FileContents) then
  begin
    InstallCount := StrTointDef(Trim(string(FileContents)), 0) + 1;
  end
  else
  begin
    InstallCount := 1;
  end;

  SaveStringToFile(ExpandConstant('{app}\InstallCount.txt'), IntToStr(InstallCount), False);
end;

procedure URLLabelOnClick(Sender: TObject);
var
	ErrorCode: Integer;
begin
	ShellExec('open', '{#DonateUrl}', '', '', SW_SHOWNORMAL, ewNoWait, ErrorCode);
end;

procedure InitializeWizard;
var
  URLLabel: TNewStaticText;
begin
  URLLabel := TNewStaticText.Create(WizardForm);
  URLLabel.Caption := 'Donate';
  URLLabel.Cursor := crHand;
  URLLabel.Parent := WizardForm;
  URLLabel.Font.Style := URLLabel.Font.Style + [fsUnderline];
	URLLabel.Font.Color := clBlue;
	URLLabel.Top := WizardForm.ClientHeight - URLLabel.Height - 15;
  URLLabel.Left := ScaleX(10)
  URLLabel.OnClick := @URLLabelOnClick;
end;

procedure CurStepChanged(CurStep: TSetupStep);
var
  FindRec: TFindRec;
  FolderPath: String;
  FilePath: String;
  ExeConfig: String;
begin
  if CurStep = ssInstall then
  begin
    BumpInstallCount();

    { We might be being run from ProcessRunner.exe, *and* we might be trying to update it. Funsies. Let's rename it (which Windows lets us do) }
    DeleteFile(ExpandConstant('{app}\ProcessRunner.exe.old'));
    RenameFile(ExpandConstant('{app}\ProcessRunner.exe'), ExpandConstant('{app}\ProcessRunner.exe.old'));

    Log(ExpandConstant('Looking for resource files in {app}\*'));
    { Remove resource files. This means that out-of-date languages will be removed, which (as a last-ditch resore) will alert maintainers that something's wrong }
    if FindFirst(ExpandConstant('{app}\*'), FindRec) then
    begin
      try
        repeat
          if (FindRec.Attributes and FILE_ATTRIBUTE_DIRECTORY <> 0) and (FindRec.Name <> '.') and (FindRec.Name <> '..') then
          begin
            FolderPath :=  ExpandConstant('{app}\') + FindRec.Name;
            FilePath := FolderPath + '\SyncTrayzor.resources.dll';
            if DeleteFile(FilePath) then
            begin
              Log('Deleted ' + FilePath);
              if DelTree(FolderPath, True, False, False) then
                Log('Deleted ' + FolderPath);
            end;
          end;
        until not FindNext(FindRec);
      finally
        FindClose(FindRec);
      end;
    end;
  end
  else if CurStep = ssPostInstall then
  begin
    ExeConfig := ExpandConstant('{param:SyncTrayzorExeConfig}');
    if ExeConfig <> '' then
    begin
      if FileExists(ExeConfig) then
      begin
        FileCopy(ExeConfig, ExpandConstant('{app}\SyncTrayzor.exe.config'), false);
      end
      else
      begin
        MsgBox('Could not find SyncTrayzorExeConfig file: ' + ExeConfig + '. Using default.', mbError, MB_OK);
      end
    end
  end
end;

procedure CurPageChanged(CurPageID: Integer);
begin

end;

function PrepareToInstall(var NeedsRestart: Boolean): String;
begin
  // 'NeedsRestart' only has an effect if we return a non-empty string, thus aborting the installation.
  // If the installers indicate that they want a restart, this should be done at the end of installation.
  // Therefore we set the global 'restartRequired' if a restart is needed, and return this from NeedRestart()

  if DotNetIsMissing() then
  begin
    Result := InstallDotNet();
  end;
end;

function NeedRestart(): Boolean;
begin
  Result := GlobalRestartRequired;
end;

function ShouldStartSyncTrayzor(): Boolean;
var
  flagPassed: Boolean;
  i: Integer;
begin
  // Can't use {param}, as it doesn't match flags with no value
  flagPassed := False;
  for i := 0 to ParamCount do begin
    if ParamStr(i) = '/StartSyncTrayzor' then begin
      flagPassed := True;
      break;
    end;
  end;
  Result := (not WizardSilent()) or flagPassed;
end;

function SyncTrayzorStartFlags(param: String): String;
begin
   if WizardSilent() then begin
      Result := '-minimized'
   end else begin
      Result := ''
   end;
end;

// See https://stackoverflow.com/a/42550055/1086121

procedure UpdateUninstallWizard;
begin
  if UninstallProgressForm.InnerNotebook.ActivePage = UninstallPollPage then
  begin
    UninstallProgressForm.PageNameLabel.Caption := 'Please Tell Us Why You''re Leaving';
    UninstallProgressForm.PageDescriptionLabel.Caption := '';
  end;

  UninstallNextButton.Caption := 'Uninstall';
  // Make the "Uninstall" button break the ShowModal loop
  UninstallNextButton.ModalResult := mrOK;
end;

function SerializeBool(value: Boolean): String;
begin
  if value then
  begin
    Result := 'true';
  end
  else
  begin
    Result := 'false';
  end
end;

function EscapeJsonString(value: String): String;
var
  c: Char;
  i: Integer;
begin
  // http://www.jrsoftware.org/ishelp/index.php?topic=isxfunc_charlength
  i := 1;
  while i <= Length(value) do
  begin
    c := value[i];
    if c = #10 then begin
      Result := Result + '\n';
    end else if c = #13 then begin
      Result := Result + '\r';
    end else if c = #9 then begin
      Result := Result + '\t';
    end else if Ord(c) < 32 then begin
      Result := Result + Format('\x%.4x', [Ord(c)]);
    end else if c = '"' then begin
      Result := Result + '\"';
    end else if c = '\' then begin
      Result := Result + '\\'
    end else begin
      Result := Result + c;
    end;
    i := i + CharLength(value, i);
  end;
end;

procedure InitializeUninstallProgressForm();
var
  PageText: TNewStaticText;
  PageNameLabel: string;
  PageDescriptionLabel: string;
  CancelButtonEnabled: Boolean;
  CancelButtonModalResult: Integer;
  Checklist: TNewCheckListBox;
  CommentsText: TNewStaticText;
  CommentsBox: TNewMemo;
  WinHttpReq: Variant;
begin
  if not UninstallSilent then
  begin
    // Create the poll page and make it active
    UninstallPollPage := TNewNotebookPage.Create(UninstallProgressForm);
    UninstallPollPage.Notebook := UninstallProgressForm.InnerNotebook;
    UninstallPollPage.Parent := UninstallProgressForm.InnerNotebook;
    UninstallPollPage.Align := alClient;

    PageText := TNewStaticText.Create(UninstallProgressForm);
    PageText.Parent := UninstallPollPage;
    PageText.AutoSize := True;
    PageText.WordWrap := True;
    PageText.SetBounds(UninstallProgressForm.StatusLabel.Left, UninstallProgressForm.StatusLabel.Top, UninstallProgressForm.StatusLabel.Width, UninstallProgressForm.StatusLabel.Height);
    PageText.ShowAccelChar := False;
    PageText.Caption := 'Please tell us why you don''t like Syncthing / SyncTrayzor so we can improve things.' + #13#10 +
    'No personal data will be sent. You can skip this step if you want.';

    Checklist := TNewCheckListBox.Create(UninstallProgressForm);
    Checklist.Parent := UninstallPollPage;
    Checklist.SetBounds(PageText.Left, PageText.Top + PageText.Height + ScaleY(10), PageText.Width, ScaleY(20) * 5);
    Checklist.BorderStyle := bsNone;
    Checklist.Color := clBtnFace;
    Checklist.WantTabs := True;
    Checklist.MinItemHeight := ScaleY(20);

    Checklist.AddCheckBox('I couldn''t get Syncthing to work', '', 0, False, True, False, False, nil);
    Checklist.AddCheckBox('Syncthing doesn''t do what I need', '', 0, False, True, False, False, nil);
    Checklist.AddCheckBox('I prefer Resilio Sync', '', 0, False, True, False, False, nil);
    Checklist.AddCheckBox('I don''t like SyncTrayzor - going to use another wrapper', '', 0, False, True, False, False, nil);
    Checklist.AddCheckBox('Other (please expand below)', '', 0, False, True, False, False, nil);

    CommentsText := TNewStaticText.Create(UninstallProgressForm);
    CommentsText.Parent := UninstallPollPage;
    CommentsText.AutoSize := True;
    CommentsText.WordWrap := True;
    CommentsText.SetBounds(PageText.Left, Checklist.Top + Checklist.Height + ScaleY(10), PageText.Width, ScaleY(15));
    CommentsText.AutoSize := False;
    CommentsText.ShowAccelChar := False;
    CommentsText.Caption := 'Anything else?';

    CommentsBox := TNewMemo.Create(UninstallProgressForm);
    CommentsBox.Parent := UninstallPollPage;
    CommentsBox.SetBounds(PageText.Left, CommentsText.Top + CommentsText.Height + ScaleY(5), PageText.Width, ScaleY(60));
    CommentsBox.ScrollBars := ssVertical;

    UninstallProgressForm.InnerNotebook.ActivePage := UninstallPollPage;

    PageNameLabel := UninstallProgressForm.PageNameLabel.Caption;
    PageDescriptionLabel := UninstallProgressForm.PageDescriptionLabel.Caption;

    UninstallNextButton := TNewButton.Create(UninstallProgressForm);
    UninstallNextButton.Parent := UninstallProgressForm;
    UninstallNextButton.Left := UninstallProgressForm.CancelButton.Left - UninstallProgressForm.CancelButton.Width - ScaleX(10);
    UninstallNextButton.Top := UninstallProgressForm.CancelButton.Top;
    UninstallNextButton.Width := UninstallProgressForm.CancelButton.Width;
    UninstallNextButton.Height := UninstallProgressForm.CancelButton.Height;

    UninstallProgressForm.CancelButton.TabOrder := UninstallNextButton.TabOrder + 1;

    // Run our wizard pages
    UpdateUninstallWizard;
    CancelButtonEnabled := UninstallProgressForm.CancelButton.Enabled
    UninstallProgressForm.CancelButton.Enabled := True;
    CancelButtonModalResult := UninstallProgressForm.CancelButton.ModalResult;
    UninstallProgressForm.CancelButton.ModalResult := mrCancel;

    if UninstallProgressForm.ShowModal = mrCancel then Abort;

    WinHttpReq := CreateOleObject('WinHttp.WinHttpRequest.5.1');
    WinHttpReq.Open('POST', '{#SurveyUrl}', false);
    WinHttpReq.Send('{' +
      ' "version": "{#AppVersion}", "comment": "' + EscapeJsonString(CommentsBox.Text) + '"' +
      ', "checklist": {' +
        ' "wontWork": '+ SerializeBool(Checklist.Checked[0]) +
        ', "notWhatINeed": '+ SerializeBool(Checklist.Checked[1]) +
        ', "preferResilio": '+ SerializeBool(Checklist.Checked[2]) +
        ', "dontLikeSyncTrayzor": '+ SerializeBool(Checklist.Checked[3]) +
        ', "other": '+ SerializeBool(Checklist.Checked[4]) +
      ' }' +
      ' }');

    // Restore the standard page payout
    UninstallProgressForm.CancelButton.Enabled := CancelButtonEnabled;
    UninstallProgressForm.CancelButton.ModalResult := CancelButtonModalResult;

    UninstallProgressForm.PageNameLabel.Caption := PageNameLabel;
    UninstallProgressForm.PageDescriptionLabel.Caption := PageDescriptionLabel;

    UninstallProgressForm.InnerNotebook.ActivePage := UninstallProgressForm.InstallingPage;
  end;
end;

[UninstallDelete]
Type: files; Name: "{app}\ProcessRunner.exe.old"
Type: files; Name: "{app}\InstallCount.txt"
Type: filesandordirs; Name: "{userappdata}\{#AppDataFolder}"
Type: filesandordirs; Name: "{localappdata}\{#AppDataFolder}"


Fix exec not working properly

Fix exec not working properly

; Script generated by the Inno Script Studio Wizard.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!

#define MyAppName "Imperial Splendour"
#define MyAppVersion "2.0"
#define MyAppPublisher "Imperial Splendour"
#define MyAppURL "https://imperialsplendour.com/"
#define MyAppExeName "ImperialSplendour.exe"
#define TmpFolder "ImperialSplendour"
#define UninstallDir "IS_Uninstall"
#define UninstallHelperExe "deactivator.exe"
#define SetupName "ImperialSplendourSetup"
#define ETWDefaultPath "steam\steamapp\common\Empire Total War"
#define CampaignPath "data\campaigns\imperial_splendour"

#define MyAppId "{{DC820B8B-AE7B-4555-AB8A-C1399266A06F}"

[Setup]
; NOTE: The value of AppId uniquely identifies this application.
; Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
AppId={#MyAppId}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
;AppVerName={#MyAppName} {#MyAppVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
DisableDirPage=yes
DefaultDirName={commonpf}
AlwaysShowDirOnReadyPage=yes
DefaultGroupName={#MyAppName}
OutputBaseFilename={#SetupName}
Compression=lzma
SolidCompression=yes
; 10GB = 10,737,418,240
ExtraDiskSpaceRequired=10737418240
DirExistsWarning=yes
SetupIconFile=appicon.ico
UninstallDisplayIcon={app}\{#MyAppExeName}
UninstallFilesDir={app}\{#UninstallDir}

[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"

[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked

[Files]
Source: "{#TmpFolder}\*.*"; DestDir: "{app}"; Flags: recursesubdirs

[Dirs]
Name: "{app}\{#CampaignPath}"

[Icons]
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}"
Name: "{commondesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon

[Run]
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent

[UninstallRun]
Filename: "{app}\{#UninstallDir}\{#UninstallHelperExe}"; WorkingDir: "{app}"

[UninstallDelete]
Type: filesandordirs; Name: "{app}\{#UninstallDir}";

[CustomMessages]
NewerVersionExists=A newer version of {#MyAppName} is already installed.%n%nInstaller version: {#MyAppVersion}%nCurrent version:
ETWNotFound=Couldn''t find your Empire Total War installation. Please make sure you have it installed correctly and manually select the install folder.
DeactivationError=There was an error preparing the upgrade. Please delete any remaining Imperial Splendour files manually and try again.

[Code]

var ExpectedPath: String;
var HasInstallation: Boolean;
var OldInstallLocation: string;
var
  StartupInfoPage: TInputQueryWizardPage;
  InputDirPage:    TInputDirWizardPage;

function InitializeSetup: Boolean;
var Version: String;
var RegistryPath: string;
var RegistryPathX32: string;
var RegistryPathX64: string;
var IsX64: string;
begin
  RegistryPathX32 := ExpandConstant('Software\Microsoft\Windows\CurrentVersion\Uninstall\{#MyAppId}_is1');
  RegistryPathX64 := ExpandConstant('Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{#MyAppId}_is1');

  HasInstallation := RegValueExists(HKEY_LOCAL_MACHINE, RegistryPathX32, 'DisplayVersion');
  if HasInstallation then
    RegistryPath := RegistryPathX32
  else begin
    HasInstallation := RegValueExists(HKEY_LOCAL_MACHINE, RegistryPathX64, 'DisplayVersion')
    if HasInstallation then
      RegistryPath := RegistryPathX64
  end;

  if not HasInstallation then
  begin
    Result := True;
    Exit;
  end;

  RegQueryStringValue(HKEY_LOCAL_MACHINE, RegistryPath, 'DisplayVersion', Version);
  if Version < '{#MyAppVersion}' then begin
    MsgBox(ExpandConstant('{cm:NewerVersionExists} '+Version), mbInformation, MB_OK);
    Result := False;

  end else begin
    RegQueryStringValue(HKEY_LOCAL_MACHINE, RegistryPath, 'InstallLocation', OldInstallLocation);
    Result := True;
  end;
end;

procedure InitializeWizard;
begin
  ExpectedPath := ExpandConstant('{commonpf}\{#ETWDefaultPath}');

  StartupInfoPage:= CreateInputQueryPage(wpWelcome,
    'Welcome',
    'Please read the following important information before continuing',
    'Requirements:'#13#10 +
    '- an Empire: Total War installation through Steam'#13#10 +
    '- at least 10GB of hard drive space'#13#10 +
    ''#13#10 +
    'NOTE: Having a different mod installed alongside Imperial Splendour can lead to issues when using the launcher. In this case, you can still use the installer but we recommend to switch between mods manually.');

  InputDirPage := CreateInputDirPage(StartupInfoPage.ID,
    'Select existing Empire: Total War Installation Location',
    '',
    'Imperial Splendour will be installed inside the following Empire: Total War folder.'#13#10 +
    ''#13#10 +
    'To continue, click Next. If you would like to select a different folder, click Browse.',
    False, '');
  InputDirPage.Add('');
  if HasInstallation then
     InputDirPage.Values[0] := OldInstallLocation
  else if DirExists(ExpectedPath) then
    InputDirPage.Values[0] := ExpectedPath
  else
    InputDirPage.Values[0] := '';

end;

function NextButtonClick(CurPageID: Integer): Boolean;
begin
  if CurPageID = StartupInfoPage.ID then
    if InputDirPage.Values[0] = '' then
      MsgBox(ExpandConstant('{cm:ETWNotFound}'), mbError, MB_OK);

  if CurPageID = InputDirPage.ID then
    WizardForm.DirEdit.Text := InputDirPage.Values[0];

  Result := true;
end;

function ShouldSkipPage(PageID: Integer): Boolean;
begin
  if PageID = InputDirPage.ID then begin
    if HasInstallation then
      Result := True;
      Exit;
  end;

  Result := False;
end;

function PrepareToInstall(var NeedsRestart: Boolean): String;
var ResultCode: Integer;
begin
  if not HasInstallation then begin
    Result := '';
    Exit;
  end;

  if not Exec(ExpandConstant(WizardForm.DirEdit.Text + '\{#UninstallDir}\{#UninstallHelperExe}'), '-strict', '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then
    Result := ExpandConstant('{cm:DeactivationError}')
  else
    Result := '';
end;

; Script generated by the Inno Script Studio Wizard.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!

#define MyAppName "Imperial Splendour"
#define MyAppVersion "2.0"
#define MyAppPublisher "Imperial Splendour"
#define MyAppURL "https://imperialsplendour.com/"
#define MyAppExeName "ImperialSplendour.exe"
#define TmpFolder "ImperialSplendour"
#define UninstallDir "IS_Uninstall"
#define UninstallHelperExe "deactivator.exe"
#define SetupName "ImperialSplendourSetup"
#define ETWDefaultPath "steam\steamapp\common\Empire Total War"
#define CampaignPath "data\campaigns\imperial_splendour"

#define MyAppId "{{DC820B8B-AE7B-4555-AB8A-C1399266A06F}"

[Setup]
; NOTE: The value of AppId uniquely identifies this application.
; Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
AppId={#MyAppId}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
;AppVerName={#MyAppName} {#MyAppVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
DisableDirPage=yes
DefaultDirName={commonpf}
AlwaysShowDirOnReadyPage=yes
DefaultGroupName={#MyAppName}
OutputBaseFilename={#SetupName}
Compression=lzma
SolidCompression=yes
; 10GB = 10,737,418,240
ExtraDiskSpaceRequired=10737418240
DirExistsWarning=yes
SetupIconFile=appicon.ico
UninstallDisplayIcon={app}\{#MyAppExeName}
UninstallFilesDir={app}\{#UninstallDir}

[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"

[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked

[Files]
Source: "{#TmpFolder}\*.*"; DestDir: "{app}"; Flags: recursesubdirs

[Dirs]
Name: "{app}\{#CampaignPath}"

[Icons]
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}"
Name: "{commondesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon

[Run]
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent

[UninstallRun]
Filename: "{app}\{#UninstallDir}\{#UninstallHelperExe}"; WorkingDir: "{app}"

[UninstallDelete]
Type: filesandordirs; Name: "{app}\{#UninstallDir}";

[CustomMessages]
NewerVersionExists=A newer version of {#MyAppName} is already installed.%n%nInstaller version: {#MyAppVersion}%nCurrent version:
ETWNotFound=Couldn''t find your Empire Total War installation. Please make sure you have it installed correctly and manually select the install folder.
DeactivationError=There was an error preparing the upgrade. Please delete any remaining Imperial Splendour files manually and try again.

[Code]

var ExpectedPath: String;
var HasInstallation: Boolean;
var OldInstallLocation: string;
var
  StartupInfoPage: TInputQueryWizardPage;
  InputDirPage:    TInputDirWizardPage;

function InitializeSetup: Boolean;
var Version: String;
var RegistryPath: string;
var RegistryPathX32: string;
var RegistryPathX64: string;
var IsX64: string;
begin
  RegistryPathX32 := ExpandConstant('Software\Microsoft\Windows\CurrentVersion\Uninstall\{#MyAppId}_is1');
  RegistryPathX64 := ExpandConstant('Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{#MyAppId}_is1');

  HasInstallation := RegValueExists(HKEY_LOCAL_MACHINE, RegistryPathX32, 'DisplayVersion');
  if HasInstallation then
    RegistryPath := RegistryPathX32
  else begin
    HasInstallation := RegValueExists(HKEY_LOCAL_MACHINE, RegistryPathX64, 'DisplayVersion')
    if HasInstallation then
      RegistryPath := RegistryPathX64
  end;

  if not HasInstallation then
  begin
    Result := True;
    Exit;
  end;

  RegQueryStringValue(HKEY_LOCAL_MACHINE, RegistryPath, 'DisplayVersion', Version);
  if Version < '{#MyAppVersion}' then begin
    MsgBox(ExpandConstant('{cm:NewerVersionExists} '+Version), mbInformation, MB_OK);
    Result := False;

  end else begin
    RegQueryStringValue(HKEY_LOCAL_MACHINE, RegistryPath, 'InstallLocation', OldInstallLocation);
    Result := True;
  end;
end;

procedure InitializeWizard;
begin
  ExpectedPath := ExpandConstant('{commonpf}\{#ETWDefaultPath}');

  StartupInfoPage:= CreateInputQueryPage(wpWelcome,
    'Welcome',
    'Please read the following important information before continuing',
    'Requirements:'#13#10 +
    '- an Empire: Total War installation through Steam'#13#10 +
    '- at least 10GB of hard drive space'#13#10 +
    ''#13#10 +
    'NOTE: Having a different mod installed alongside Imperial Splendour can lead to issues when using the launcher. In this case, you can still use the installer but we recommend to switch between mods manually.');

  InputDirPage := CreateInputDirPage(StartupInfoPage.ID,
    'Select existing Empire: Total War Installation Location',
    '',
    'Imperial Splendour will be installed inside the following Empire: Total War folder.'#13#10 +
    ''#13#10 +
    'To continue, click Next. If you would like to select a different folder, click Browse.',
    False, '');
  InputDirPage.Add('');
  if HasInstallation then
     InputDirPage.Values[0] := OldInstallLocation
  else if DirExists(ExpectedPath) then
    InputDirPage.Values[0] := ExpectedPath
  else
    InputDirPage.Values[0] := '';

end;

function NextButtonClick(CurPageID: Integer): Boolean;
begin
  if CurPageID = StartupInfoPage.ID then
    if InputDirPage.Values[0] = '' then
      MsgBox(ExpandConstant('{cm:ETWNotFound}'), mbError, MB_OK);

  if CurPageID = InputDirPage.ID then
    WizardForm.DirEdit.Text := InputDirPage.Values[0];

  Result := true;
end;

function ShouldSkipPage(PageID: Integer): Boolean;
begin
  if PageID = InputDirPage.ID then begin
    if HasInstallation then
      Result := True;
      Exit;
  end;

  Result := False;
end;

function PrepareToInstall(var NeedsRestart: Boolean): String;
var ResultCode: Integer;
begin
  if not HasInstallation then begin
    Result := '';
    Exit;
  end;

  if not Exec(ExpandConstant('{#UninstallDir}\{#UninstallHelperExe}'), '-strict', '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then
    Result := ExpandConstant('{cm:DeactivationError}')
  else
    Result := '';
end;


Fix flag in Inno Setup script

Fix flag in Inno Setup script

The proper way to have a checked option box is apparently to not set
a flag at all.

Oddly, the previous version must have worked for the last release,
but it doesn't work now.

; Script generated by the Inno Setup Script Wizard.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!

[Setup]
; NOTE: The value of AppId uniquely identifies this application.
; Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
AppId={{0467A404-8C83-42A2-8C25-0903FBD14D9C}
AppName=EcoData Retriever
AppVersion=1.6
;AppVerName=EcoData Retriever 1.6
AppPublisher=Weecology
AppPublisherURL=http://ecodataretriever.org
AppSupportURL=http://ecodataretriever.org
AppUpdatesURL=http://ecodataretriever.org
DefaultDirName={pf}\EcoDataRetriever
DefaultGroupName=EcoData Retriever
OutputBaseFilename=RetrieverSetup
SetupIconFile=icon.ico
Compression=lzma
SolidCompression=yes
ChangesEnvironment=true

[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"

[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
Name: modifypath; Description: Add application directory to your environmental path

[Files]
Source: "dist\retriever.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "dist\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
; NOTE: Don't use "Flags: ignoreversion" on any shared system files

[Icons]
Name: "{group}\EcoData Retriever"; Filename: "{app}\retriever.exe"
Name: "{group}\{cm:UninstallProgram,EcoData Retriever}"; Filename: "{uninstallexe}"
Name: "{commondesktop}\EcoData Retriever"; Filename: "{app}\retriever.exe"; Tasks: desktopicon

[Code]
const
    ModPathName = 'modifypath';
    ModPathType = 'user';

function ModPathDir(): TArrayOfString;
begin
    setArrayLength(Result, 1)
    Result[0] := ExpandConstant('{app}');
end;
#include "modpath.iss"

[Run]
Filename: "{app}\retriever.exe"; Description: "{cm:LaunchProgram,EcoData Retriever}"; Flags: nowait postinstall skipifsilent

; Script generated by the Inno Setup Script Wizard.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!

[Setup]
; NOTE: The value of AppId uniquely identifies this application.
; Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
AppId={{0467A404-8C83-42A2-8C25-0903FBD14D9C}
AppName=EcoData Retriever
AppVersion=1.6
;AppVerName=EcoData Retriever 1.6
AppPublisher=Weecology
AppPublisherURL=http://ecodataretriever.org
AppSupportURL=http://ecodataretriever.org
AppUpdatesURL=http://ecodataretriever.org
DefaultDirName={pf}\EcoDataRetriever
DefaultGroupName=EcoData Retriever
OutputBaseFilename=RetrieverSetup
SetupIconFile=icon.ico
Compression=lzma
SolidCompression=yes
ChangesEnvironment=true

[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"

[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
Name: modifypath; Description: Add application directory to your environmental path; Flags: checked

[Files]
Source: "dist\retriever.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "dist\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
; NOTE: Don't use "Flags: ignoreversion" on any shared system files

[Icons]
Name: "{group}\EcoData Retriever"; Filename: "{app}\retriever.exe"
Name: "{group}\{cm:UninstallProgram,EcoData Retriever}"; Filename: "{uninstallexe}"
Name: "{commondesktop}\EcoData Retriever"; Filename: "{app}\retriever.exe"; Tasks: desktopicon

[Code]
const
    ModPathName = 'modifypath';
    ModPathType = 'user';

function ModPathDir(): TArrayOfString;
begin
    setArrayLength(Result, 1)
    Result[0] := ExpandConstant('{app}');
end;
#include "modpath.iss"

[Run]
Filename: "{app}\retriever.exe"; Description: "{cm:LaunchProgram,EcoData Retriever}"; Flags: nowait postinstall skipifsilent


build(installer): fixed name for StartMenuEntry in innosetup script

; Script generated by the Inno Setup Script Wizard.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!

#define MyAppName "ActivityWatch"
#define MyAppVersion GetEnv('AW_VERSION')
#define MyAppPublisher "ActivityWatch Contributors"
#define MyAppURL "https://activitywatch.net/"
#define MyAppExeName "aw-qt.exe"
#define RootDir "..\.."
#define DistDir "..\..\dist"

#pragma verboselevel 9

[Setup]
; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
; TODO: Should probably remove the extra leading {, but don't want to risk changing the AppId...
AppId={{F226B8F4-3244-46E6-901D-0CE8035423E4}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
;AppVerName={#MyAppName} {#MyAppVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL="https://github.com/ActivityWatch/activitywatch/issues"
AppUpdatesURL="https://github.com/ActivityWatch/activitywatch/releases"
DefaultDirName={autopf}\{#MyAppName}
DisableProgramGroupPage=yes
; Uncomment the following line to run in non administrative install mode (install for current user only.)
PrivilegesRequired=lowest
PrivilegesRequiredOverridesAllowed=dialog
OutputDir={#DistDir}
OutputBaseFilename=activitywatch-setup
SetupIconFile="{#RootDir}\aw-qt\media\logo\logo.ico"
UninstallDisplayName={#MyAppName}
UninstallDisplayIcon={app}\{#MyAppExeName}
Compression=lzma
SolidCompression=yes
WizardStyle=modern

[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"

[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
Name: "StartMenuEntry" ; Description: "Start ActivityWatch when Windows starts"; GroupDescription: "Windows Startup"; MinVersion: 4,4;

[Files]
Source: "{#DistDir}\activitywatch\aw-qt.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "{#DistDir}\activitywatch\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
; NOTE: Don't use "Flags: ignoreversion" on any shared system files

[Icons]
Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
Name: "{userstartup}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: StartMenuEntry;

[Run]
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent

; Removes the previously installed version before installing the new one
; NOTE: Doesn't work? And also discouraged by the docs
;[InstallDelete]
;Type: filesandordirs; Name: "{app}\"

; Script generated by the Inno Setup Script Wizard.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!

#define MyAppName "ActivityWatch"
#define MyAppVersion GetEnv('AW_VERSION')
#define MyAppPublisher "ActivityWatch Contributors"
#define MyAppURL "https://activitywatch.net/"
#define MyAppExeName "aw-qt.exe"
#define RootDir "..\.."
#define DistDir "..\..\dist"

#pragma verboselevel 9

[Setup]
; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
AppId={{F226B8F4-3244-46E6-901D-0CE8035423E4}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
;AppVerName={#MyAppName} {#MyAppVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL="https://github.com/ActivityWatch/activitywatch/issues"
AppUpdatesURL="https://github.com/ActivityWatch/activitywatch/releases"
DefaultDirName={autopf}\{#MyAppName}
DisableProgramGroupPage=yes
; Uncomment the following line to run in non administrative install mode (install for current user only.)
PrivilegesRequired=lowest
PrivilegesRequiredOverridesAllowed=dialog
OutputDir={#DistDir}
OutputBaseFilename=activitywatch-setup
SetupIconFile="{#RootDir}\aw-qt\media\logo\logo.ico"
UninstallDisplayName={#MyAppName}
UninstallDisplayIcon={app}\{#MyAppExeName}
Compression=lzma
SolidCompression=yes
WizardStyle=modern

[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"

[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
Name: "StartMenuEntry" ; Description: "Start ActivityWatch when Windows starts"; GroupDescription: "Windows Startup"; MinVersion: 4,4;

[Files]
Source: "{#DistDir}\activitywatch\aw-qt.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "{#DistDir}\activitywatch\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
; NOTE: Don't use "Flags: ignoreversion" on any shared system files

[Icons]
Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
Name: "{userstartup}\(#MyAppName)"; Filename: "{app}\{#MyAppExeName}"; Tasks: StartMenuEntry;

[Run]
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent

; Removes the previously installed version before installing the new one
; NOTE: Doesn't work? And also discouraged by the docs
;[InstallDelete]
;Type: filesandordirs; Name: "{app}\"