Inno Setup: How to overwrite on install but not on change? - installation

I know how to overwrite the files using this method
[Files]
Source: "Project\*"; DestDir: "{app}"; \
Flags: ignoreversion recursesubdirs onlyifdoesntexist; Permissions: everyone-full
But when I change the program using the Change option in 'Install Or Change Program' section I want to not overwrite the files.
I create the change option for my installer like this:
[setup]
AppModifyPath="{srcexe}" /modify=1
How do I do this?

First, your code seems wrong. With the onlyifdoesntexist flag, the files are never overwritten, contrary to what you claim. So for most purposes, simply using this flag will do.
Anyway, a solution is to create two [Files] entries, one that overwrites and one that does not. And use the Pascal scripting to pick the entry for a respective installation mode.
[Files]
Source: "Project\*"; DestDir: "{app}"; Flags: ... onlyifdoesntexist; Check: IsUpgrade
Source: "Project\*"; DestDir: "{app}"; Flags: ...; Check: not IsUpgrade
Example of IsUpgrade implementation:
[Code]
function IsUpgrade: Boolean;
var
S: string;
InnoSetupReg: string;
AppPathName: string;
begin
InnoSetupReg :=
'Software\Microsoft\Windows\CurrentVersion\Uninstall\{#SetupSetting("AppId")}_is1';
{ The ExpandConstant is here for Inno Script Studio, }
{ which generated AppId in a form of GUID. }
{ The leading { of the GUID has to be doubled in Inno Setup, }
{ and the ExpandConstant collapses that back to single {. }
InnoSetupReg := ExpandConstant(InnoSetupReg);
AppPathName := 'Inno Setup: App Path';
Result :=
RegQueryStringValue(HKLM, InnoSetupReg, AppPathName, S) or
RegQueryStringValue(HKCU, InnoSetupReg, AppPathName, S);
end;
See also Pascal scripting: Check parameters.

Related

How to detect Update vs. New Installation with InnoSetup? [duplicate]

I know how to overwrite the files using this method
[Files]
Source: "Project\*"; DestDir: "{app}"; \
Flags: ignoreversion recursesubdirs onlyifdoesntexist; Permissions: everyone-full
But when I change the program using the Change option in 'Install Or Change Program' section I want to not overwrite the files.
I create the change option for my installer like this:
[setup]
AppModifyPath="{srcexe}" /modify=1
How do I do this?
First, your code seems wrong. With the onlyifdoesntexist flag, the files are never overwritten, contrary to what you claim. So for most purposes, simply using this flag will do.
Anyway, a solution is to create two [Files] entries, one that overwrites and one that does not. And use the Pascal scripting to pick the entry for a respective installation mode.
[Files]
Source: "Project\*"; DestDir: "{app}"; Flags: ... onlyifdoesntexist; Check: IsUpgrade
Source: "Project\*"; DestDir: "{app}"; Flags: ...; Check: not IsUpgrade
Example of IsUpgrade implementation:
[Code]
function IsUpgrade: Boolean;
var
S: string;
InnoSetupReg: string;
AppPathName: string;
begin
InnoSetupReg :=
'Software\Microsoft\Windows\CurrentVersion\Uninstall\{#SetupSetting("AppId")}_is1';
{ The ExpandConstant is here for Inno Script Studio, }
{ which generated AppId in a form of GUID. }
{ The leading { of the GUID has to be doubled in Inno Setup, }
{ and the ExpandConstant collapses that back to single {. }
InnoSetupReg := ExpandConstant(InnoSetupReg);
AppPathName := 'Inno Setup: App Path';
Result :=
RegQueryStringValue(HKLM, InnoSetupReg, AppPathName, S) or
RegQueryStringValue(HKCU, InnoSetupReg, AppPathName, S);
end;
See also Pascal scripting: Check parameters.

Inno Setup Jumps to Second Page if Program is Installed [duplicate]

This question already has answers here:
Inno Setup generated installer does not show "Select Destination Location" page on some systems
(3 answers)
Closed 4 years ago.
On first install everything runs smooth, but if I run the installer again it just jumps to the second page asking where i want to put the additional files, and then in the ready page only the parameters for the additional files folders is shown. The ignore version flag is set, what else could it be?
[Setup]
AppName={#MyAppName}
AppVersion={#MyAppVersion}
;AppVerName={#MyAppName} {#MyAppVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
DefaultDirName={pf}\{#MyAppName}
DefaultGroupName={#MyAppName}
Compression=lzma
SolidCompression=yes
OutputBaseFilename=aeolian_meditation_setup
WizardSmallImageFile=compiler:greenlogo.bmp
WizardImageFile=compiler:glogo.bmp
DirExistsWarning=yes
[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"
[Files]
;Main program that will be installed in {app} folder
Source: "D:\ocean_swift\Flowstone Projects\Aeolian Meditation Advanced\OS Aeolian Meditation Advanced A191.exe"; DestDir: "{app}"; Flags: ignoreversion
;Database file that will installed where user choosed
Source: "D:\ocean_swift\Flowstone Projects\Aeolian Meditation Advanced\onts\OpenSans-Regular.ttf"; DestDir: "{fonts}"; Flags: onlyifdoesntexist; FontInstall: "Open Sans"
Source: "C:\Program Files (x86)\VSTPlugins\OS Aeolian Meditation Advanced A191.dll"; DestDir: "{code:GetDataDir}"
[Code]
var
DataDirPage: TInputDirWizardPage;
procedure InitializeWizard;
begin
// Create the page
DataDirPage := CreateInputDirPage(wpSelectDir,
'Select 32bit VST Plugin Directory', 'Where should the 32bit VSTi plugin be installed??',
'Select the folder in which Setup should install the 32bit VSTi plugin, then click Next.',
False, '');
DataDirPage.Add('');
DataDirPage.Values[0] := 'C:\Program Files (x86)\VSTPlugins\';
end;
procedure RegisterPreviousData(PreviousDataKey: Integer);
begin
// Store the selected folder for further reinstall/upgrade
SetPreviousData(PreviousDataKey, 'DataDir', DataDirPage.Values[0]);
end;
function NextButtonClick(CurPageID: Integer): Boolean;
begin
// Set default folder if empty
if DataDirPage.Values[0] = '' then
DataDirPage.Values[0] := ExpandConstant('{sd}\DataDir');
Result := True;
end;
function UpdateReadyMemo(Space, NewLine, MemoUserInfoInfo, MemoDirInfo, MemoTypeInfo,
MemoComponentsInfo, MemoGroupInfo, MemoTasksInfo: String): String;
var
S: String;
begin
// Fill the 'Ready Memo' with the normal settings and the custom settings
S := '';
S := S + MemoDirInfo + NewLine + NewLine;
S := S + '32bit VSTi' + NewLine;
S := S + Space + DataDirPage.Values[0] + NewLine;
Result := S;
end;
function GetDataDir(Param: String): String;
begin
{ Return the selected DataDir }
Result := DataDirPage.Values[0];
end;
If you look here you will see that the default value for DisableProgramGroupPage is auto. As described there:
If this is set to auto, at startup Setup will look in the registry to
see if the same application is already installed, and if so, it will
not show the Select Start Menu Folder wizard page.
If you review the other Disable entries in the help file you will see them behave the same. It is logical to only show these pages during a new install. Change the default behaviour by setting these to no.

How to install only file based on condition (external configuration file) in Inno Setup

I have an XML file with some tags like names of DLL files.
I want a Inno Setup script code to install only files stated in the XML file (I can read from XML file).
My question is: How can I embed all DLL files and according to the XML file, I only install only the required files.
The idea I just need one XML for each release, and I never change the DLL files.
Use the Check parameter to programmatically decide if a certain file should be installed:
[Files]
Source: "Dll1.dll"; DestDir: "{app}"; Check: ShouldInstallDll1
Source: "Dll2.dll"; DestDir: "{app}"; Check: ShouldInstallDll2
[Code]
function ShouldInstallDll1: Boolean;
begin
Result := ???;
end;
function ShouldInstallDll2: Boolean;
begin
Result := ???;
end;
If it better suits your logic, you can also use a single "check" function and use the CurrentFileName magic variable, to test, if the file being installed, is the one you want to really install:
[Files]
Source: "Dll1.dll"; DestDir: "{app}"; Check: ShouldInstallDll
Source: "Dll2.dll"; DestDir: "{app}"; Check: ShouldInstallDll
[Code]
var
FileToInstall: string;
function InitializeSetup(): Boolean;
begin
FileToInstall := ??? { 'Dll1.dll' or 'Dll2.dll' based on the XML file }
Result := True;
end;
function ShouldInstallDll: Boolean;
var
Name: string;
begin
Name := ExtractFileName(CurrentFileName);
Result := (CompareText(Name, FileToInstall) = 0);
end;
The latter approach can be used, even if you pack files using a wildcard:
[Files]
Source: "*.dll"; DestDir: "{app}"; Check: ShouldInstallDll

Download a file while installing by inno setup

[Files]
Source: D:\VBproject\YY\inst\YY.exe; DestDir: {app}; Flags: ignoreversion
Source: D:\VBproject\YY\inst\YY.dll; DestDir: {app}; Flags: ignoreversion
That is my file list above, and I want to install the YY.dll from the internet for example
http://www.example.com/yy.dll , not pack it to setup.exe
Is there any way can do that? Thanks alot
I have used the ISTool plugin:
#include 'C:\Program Files (x86)\ISTool\isxdl.iss'
[Code]
const
dotnetRedistURL = 'http://www.example.com/yy.dll ';
procedure CurStepChanged(CurStep: TSetupStep);
begin
if CurStep = ssPostInstall then
begin
isxdl_AddFile(dotnetRedistURL, ExpandConstant('{app}\data\yy.dll'));
isxdl_SetOption('Updating', 'Updating to the latest DLL...');
isxdl_SetOption('Please wait...', 'Updating...');
isxdl_DownloadFiles(WizardForm.Handle);
end;
end;

How to get installation path during uninstall?

My idea is to offer the user the choice of installing the application either in {localappdata} or {commonappdata}, and that part works fine. However, I'm using custom Inno Setup skinning support, which requires a couple functions to get loaded during installation/uninstallation.
I have a DefaultAppDataFolder(): String function which returns the folder that the user chose (either common or local appdata one), and it works fine during installation. However, when I try to uninstall the app, it throws following error right upon execution:
Script error: Could not call proc
You can see that in the DefaultAppDataFolder() function I'm getting the uninstall directory in a bit shady way, extracting the file directory twice from the {UninstallExe} constant, maybe there is a better way to retrieve this?
Here is my script:
#define ApplicationName "MyApp"
#define ApplicationExe "app.exe"
#define ApplicationInstanceMutex "APPMUTEX"
#define InstallerFilename "install_app"
#define SkinName "Carbon.vsf"
[Setup]
AppName={#ApplicationName}
AppVerName={#ApplicationName}
DefaultDirName={code:DefaultAppDataFolder}
DefaultGroupName={#ApplicationName}
UninstallFilesDir={code:DefaultAppDataFolder}\uninstall
UninstallDisplayName={#ApplicationName}
Compression=lzma2
SolidCompression=yes
OutputDir=.\
DisableDirPage=yes
OutputBaseFilename={#InstallerFilename}
UninstallDisplayIcon={code:DefaultAppDataFolder}\{#ApplicationExe}
DisableProgramGroupPage=yes
AppMutex={#ApplicationInstanceMutex}
WizardImageFile=installer_images\installer-1.bmp
WizardSmallImageFile=installer_images\installer-2.bmp
[Files]
Source: "skins\VclStylesInno.dll"; DestDir: "{code:DefaultAppDataFolder}"; Flags: uninsneveruninstall ignoreversion
Source: "skins\{#SkinName}"; DestDir: "{code:DefaultAppDataFolder}"; Flags: ignoreversion
Source: "root_files\*.*"; DestDir: "{code:DefaultAppDataFolder}"; Flags: ignoreversion
Source: "client_files\*.*"; DestDir: "{code:DefaultAppDataFolder}"; Flags: ignoreversion
Source: "ssl_libs\*.*"; DestDir: "{code:DefaultAppDataFolder}"; Flags: ignoreversion
[Icons]
Name: "{group}\{#ApplicationName}"; Filename: "{code:DefaultAppDataFolder}\{#ApplicationExe}"; WorkingDir: "{code:DefaultAppDataFolder}"
Name: "{group}\Uninstall"; Filename: "{uninstallexe}"
Name: "{commondesktop}\{#ApplicationName}"; Filename: "{code:DefaultAppDataFolder}\{#ApplicationExe}"; Tasks: desktopicon
[Run]
Filename: "{code:DefaultAppDataFolder}\{#ApplicationExe}"; Description: "Launch {#ApplicationName}"; Flags: postinstall nowait runascurrentuser
[Tasks]
Name: commondir; Description: "&All users"; GroupDescription: "Install For:"; Flags: exclusive
Name: localdir; Description: "&Current user"; GroupDescription: "Install For:"; Flags: exclusive unchecked
Name: desktopicon; Description: "Create a &desktop icon"
[Code]
procedure LoadVCLStyleS(VClStyleFile: String); external 'LoadVCLStyleW#files:VclStylesInno.dll stdcall setuponly';
procedure UnLoadVCLStylesS; external 'UnLoadVCLStyles#files:VclStylesInno.dll stdcall setuponly';
procedure LoadVCLStyleU(VClStyleFile: String); external 'LoadVCLStyleW#{code:DefaultAppDataFolder}\VclStylesInno.dll stdcall uninstallonly';
procedure UnLoadVCLStylesU; external 'UnLoadVCLStyles#{code:DefaultAppDataFolder}\VclStylesInno.dll stdcall uninstallonly';
var
ApplicationUninstalled: Boolean;
WizardInitialized: Boolean;
function InitializeSetup(): Boolean;
var
C1: Integer;
begin
ExtractTemporaryFile('{#SkinName}');
LoadVCLStyleS(ExpandConstant('{tmp}\{#SkinName}'));
result := TRUE;
end;
procedure InitializeWizard();
begin
WizardInitialized := TRUE;
end;
procedure DeinitializeSetup();
begin
UnLoadVCLStylesS;
end;
function InitializeUninstall(): Boolean;
begin
LoadVCLStyleU(ExpandConstant('{code:DefaultAppDataFolder}\{#SkinName}'));
result := TRUE;
end;
procedure InitializeUninstallProgressForm();
begin
ApplicationUninstalled := TRUE;
end;
procedure DeinitializeUninstall();
begin
UnLoadVCLStylesU;
UnloadDLL(ExpandConstant('{code:DefaultAppDataFolder}\VclStylesInno.dll'));
if ApplicationUninstalled then
begin
DeleteFile(ExpandConstant('{code:DefaultAppDataFolder}\VclStylesInno.dll'));
RemoveDir(ExpandConstant('{code:DefaultAppDataFolder}'));
end;
end;
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
var
mres: integer;
begin
case CurUninstallStep of
usPostUninstall: begin
mres := MsgBox('Do you want to delete user data?', mbConfirmation, MB_YESNO or MB_DEFBUTTON2)
if mres = IDYES then
begin
DelTree(ExpandConstant('{localappdata}\{#ApplicationName}'), TRUE, TRUE, TRUE);
DelTree(ExpandConstant('{userappdata}\{#ApplicationName}'), TRUE, TRUE, TRUE);
end;
end;
end;
end;
function DefaultAppDataFolder(Param: String): String;
begin
if IsUninstaller then
result := ExtractFileDir(ExtractFileDir(ExpandConstant('{uninstallexe}')))
else
if (WizardInitialized) and
(IsTaskSelected('localdir')) then
result := ExpandConstant('{localappdata}') + '\Programs\{#ApplicationName}'
else
result := ExpandConstant('{commonappdata}') + '\Programs\{#ApplicationName}';
end;
The error you got is not related to the uninstaller; you can narrow the problem down to this:
[Setup]
AppName=My Program
AppVersion=1.5
DefaultDirName=My Program
[Code]
procedure DoSomething;
external 'DoSomething#{code:GetLibPath}\MyLib.dll stdcall setuponly';
function GetLibPath(Value: string): string;
begin
Result := 'C:\ValidPathToLib';
end;
From the above script it seems that you cannot use scripted constants for imported DLL file names. And even delayed loading didn't workaround this limit. But for your case you can use just {app} path since your library is actually there (if I get your intention right):
...
procedure LoadVCLStyleU(VClStyleFile: string);
external 'LoadVCLStyleW#{app}\VclStylesInno.dll stdcall uninstallonly';
procedure UnLoadVCLStylesU;
external 'UnLoadVCLStyles#{app}\VclStylesInno.dll stdcall uninstallonly';
...

Resources