I have multiple unrelated WPF and Winforms applications that I need to give to users.
Typically, there is one installer per application. However, they want a just a single file they can run to install every app in the appropriate location. So is there anyway to combine multiple installers into a single installer?
I want to be able to specify a root folder for everything to install under, but multiple sub folders will exist to split the apps up into their "type".
Ex: Say the I was deliver three apps: Spotify, VLC Player, and Google Chrome. I need a single installer that installs these into a user specified folder (let's say MainApplications). So when finished it would look like:
What I am currently trying: Going through the typical publish steps in Visual Studio for each project -> putting each installer in the same folder -> executing a file that runs each installer silently.
It is possible and actually we are delivering four our software setups in one installer suite. I am personally using InnoSetup
To do that, following strategy is used;
Edit: For point 3;
you can basically use following code managing your setup.exe files.
#define ApplicationAInstaller "ApplicationASetup.exe"
#define ApplicationA_Component_Description "ComponentName 1.X.X"
#define ApplicationA_GUID "{6B72B9EC-B06C-4058-C704-7AD0F752C7FC}" ;Comes from for each sub installer
#define ApplicationA_Min_Version "1.4"
[Types]
Name: "full"; Description: "Full installation"
Name: "custom"; Description: "Custom installation"; Flags: iscustom
[Components]
Name: "applicationA"; Description: "{#ApplicationA_Component_Description}"; Types: full custom; ExtraDiskSpaceRequired: 215300000
Name: "applicationB"; Description: "{#ApplicationB_Component_Description}"; Types: full custom; ExtraDiskSpaceRequired: 238000000
[Files]
Source: "Components\{#ApplicationAInstaller}"; DestDir: {tmp}; Flags: deleteafterinstall nocompression; Components:applicationA ; AfterInstall: InstallApplicationA; Check: NeedsToInstallApplicationA
Source: "Components\{#ApplicationBInstaller}"; DestDir: {tmp}; Flags: deleteafterinstall nocompression; Components:applicationB ; AfterInstall: InstallApplicationB; Check: NeedsToInstallApplicationB
[Code]
function GetApplicationA_REG_KEY(): String;
begin
Result := 'Software\Microsoft\Windows\CurrentVersion\Uninstall\{#ApplicationA_GUID}_is1';
if not RegKeyExists(HKEY_LOCAL_MACHINE, Result) then
begin
Result := 'Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{#ApplicationA_GUID}_is1';
end;
end;
function NeedsToInstallApplicationA: Boolean;
var
regKey : string;
current_version : string;
required_version : string;
begin
Result := True;
required_version := '{#ApplicationA_Min_Version}';
regKey := GetApplicationA_REG_KEY();
current_version := GetDisplayVersion(regKey);
Result := required_version <> current_version;
end;
procedure SilentInstall(regKey: String; requiredVersion: String; appName: String; appRootDir: String ; appInstaller: string);
var
StatusText : string;
CurrentFileText : string;
ResultCode : Integer;
params : string;
dir : string;
current_version : string;
begin
StatusText := WizardForm.StatusLabel.Caption;
CurrentFileText := WizardForm.FilenameLabel.Caption
WizardForm.ProgressGauge.Style := npbstMarquee;
try
current_version := GetDisplayVersion(regKey);
WizardForm.StatusLabel.Caption := 'Installing ' + appName + ' : ' + requiredVersion;
if (current_version <> '-1') and (requiredVersion <> current_version) then
begin
WizardForm.StatusLabel.Caption := 'Updating ' + appName + ' to the latest version : ' + requiredVersion;
UninstallUsingReg(regKey, true);
end;
{ some custom messages for some applications}
if ExpandConstant('{#ApplicationAName}') = appName then
begin
WizardForm.FilenameLabel.Caption := 'This may take a few minutes, please wait ...'
end;
dir := '"'+ExpandConstant('{app}') + '\' + appRootDir + '"';
params := '/norestart /VERYSILENT'+' /DIR=' + dir;
if not Exec(ExpandConstant('{tmp}\' + appInstaller), params, '', SW_HIDE, ewWaitUntilTerminated, ResultCode) then
begin
{ you can interact with the user that the installation failed }
MsgBox(appName+' failed with code: ' + IntToStr(ResultCode) + '.', mbError, MB_OK);
end;
finally
WizardForm.StatusLabel.Caption := StatusText;
WizardForm.ProgressGauge.Style := npbstNormal;
WizardForm.FilenameLabel.Caption := CurrentFileText;
end;
end;
procedure InstallApplicationA;
var
regKey : string;
begin
regKey := GetApplicationA_REG_KEY();
SilentInstall(regKey, '{#ApplicationA_Min_Version}', '{#ApplicationA_AppName}' , '{#ApplicationA_Install_DIR}', '{#ApplicationAInstaller}');
end;