Execute script once before installing a directory tree - inno-setup

I am trying to write an Inno Setup installer to install and then run other installers. The problem I have is when trying to install Cygwin. I have downloaded Cygwin and all packages I need so I can perform a local install. Then I want to add extra files and directories to the Cygwin installation.
My first attempt was like this:
[Files]
Source: "{#Cygwin}\Cygwin\*"; DestDir: {tmp}\cygwin; Flags: recursesubdirs;
Source: "{#Cygwin}\additional\*"; DestDir: {tmp}\cygwin\additional; Flags: recursesubdirs
[Run]
Filename: "{tmp}\cygwin\setup-x86_64.exe"; Parameters: "-q -L"; WorkingDir: "{tmp}";
But this means that I must have a script to add the additional files because there is nowhere to put them until Cygwin is actually installed by the command in the [Run] section.
I have tried using a BeforeInstall script in the [Files] section to run the Cygwin installer before adding the additional files, but because I have to use a wildcard with Source: "{#Cygwin}\additional\*"; the script is called once for every file in the directory tree.
The [Files] section of Inno Setup seems to only accept source files, and not a source directory, unless the directory has a wildcard.
Is there a way I can make it install everything from a directory tree without using a wildcard, or is there a way I can make the BeforeInstall script run just once, regardless of how many files are copied?

Install the Cygwin on the first call to BeforeInstall function only.
var
CygwinInstalled: Boolean;
procedure MyBeforeInstall;
begin
if CygwinInstalled then
begin
Log('Cygwin installed already');
end
else
begin
Log('Installing Cygwin');
{ install Cygwin here }
CygwinInstalled := true;
end;
end;

Related

Inno Setup is unable to install to LocalSystem's %LOCALAPPDATA%

I have a Windows service that runs as the local system account. It stores some data in its %LOCALAPPDATA% folder. For LocalSystem, this is the following location:
C:\Windows\system32\config\systemprofile\AppData\Local
This works without a hitch; the service creates its own subfolder and writes files to it.
Now, using Inno Setup, I'm trying to install an initial file to that folder:
[Files]
Source: "LICENSE"; DestDir: "{sys}\config\systemprofile\AppData\Local\InnoTest"; Flags: ignoreversion
The log suggests that this works fine:
[14:20:10,722] -- File entry --
[14:20:10,725] Dest filename: C:\Windows\system32\config\systemprofile\AppData\Local\InnoTest\LICENSE
[14:20:10,729] Time stamp of our file: 2022-10-24 13:56:42.000
[14:20:10,731] Installing the file.
[14:20:10,734] Creating directory: C:\Windows\system32\config\systemprofile\AppData\Local\InnoTest
[14:20:10,738] Successfully installed the file.
It says "successfully installed the file", but neither the file nor the directory is anywhere to be seen.
Run the installer again, however, and you will be told that the destination file exists:
[14:24:47,381] -- File entry --
[14:24:47,395] Dest filename: C:\Windows\system32\config\systemprofile\AppData\Local\InnoTest\LICENSE
[14:24:47,398] Time stamp of our file: 2022-10-24 13:56:42.000
[14:24:47,400] Dest file exists.
[14:24:47,403] Time stamp of existing file: 2022-10-24 13:56:42.000
[14:24:47,405] Installing the file.
[14:24:47,410] Successfully installed the file.
So: The installer thinks the file exists, but I can't see it.
This is specifically an issue with the System account; if I switch to, say, the %LOCALAPPDATA% of the LocalService account, things work as expected:
[Files]
Source: "LICENSE"; DestDir: "{win}\ServiceProfiles\LocalService\AppData\Local\InnoTest"; Flags: ignoreversion
If anyone could shed some light on what is (or isn't) going on here, it would be much appreciated.
Minimal, reproducible and complete example here.
This is almost a duplicate of:
File/DLL installed to {sys} does not appear in C:\Windows\system32
Though your question probably deserves specific answer.
If I understand correctly, you want to install to System32 on both 32-bit and 64-bit systems. So there are two options:
Use the 64-bit mode;
If you do not want (or not dare) to switch whole installer to 64-bit mode, you will need two entries in the [Files] section. One for 64-bit systems with 64bit flag and one for 32-bit systems without the flag:
[Files]
Source: "LICENSE"; \
DestDir: "{sys}\config\systemprofile\AppData\Local\InnoTest"; \
Flags: ignoreversion; Flags: 64bit; Check: IsWin64
Source: "LICENSE"; \
DestDir: "{sys}\config\systemprofile\AppData\Local\InnoTest"; \
Flags: ignoreversion; Check: not IsWin64

Register an exe file as OLE server with Inno Setup

I want to register an EXE file as OLE server with Inno Setup.
When I use my EXE file path with Regserver parameter in cmd, everything is correct.
But when I use the regserver flag in Inno Setup like this:
Source: "{src}\App\MyApp.exe"; DestDir: "{app}"; Flags: external regserver
I got this error
Unable to register the DLL/OCX: Regsvr32 failed with exit code 0x4.
What should I do?
The regserver flag is for DLL/OCX files only. There's no standard way to register EXE files, so it cannot be handled natively by Inno Setup, the same way you cannot use regsvr32 with an EXE file.
If your EXE file has a custom command-line parameter for the registration, use it in Inno Setup in the Run section:
[Run]
Filename: "{app}\MyApp.exe"; Parameters: "Regserver"

Inno Setup - Run programs after ssPostInstall code that processes installed files

I am installing DirectX and vc_2013_x64 from components section (and from Run section) and I am using this code too: How to add .arc decompression to Inno Setup? (answer of Martin Prikryl). How to move the installation of DirectX and vc_2013_x64 (if it is possible) to after the decompressing page (and before the finish page)? (Because with this code the installation is in the middle between installing page and decompressing page).
I am using this script to execute the sub-installers:
[Run]
Filename: {src}\_Redist\dxwebsetup.exe; StatusMsg: Installing DirectX...
Filename: {src}\_Redist\vcredist_x64.exe; StatusMsg: Installing vcredist_x64...
There are many ways, e.g.:
Run the ExtractArc from the AfterInstall parameter of the archive Run entry, instead of the CurStepChanged event function:
Source: {#ArcArchive}; DestDir: "{tmp}"; Flags: nocompression deleteafterinstall; \
AfterInstall: ExtractArc
Or execute the dxwebsetup.exe and vcredist_x64.exe from the CurStepChanged before the ExtractArc, using the Exec function.

Install only if external file exists

I would like to instruct Inno Setup to install a certain external file only if does exist.
Like so:
Source: "d:\sources\SomeDLL.dll"; DestDir: {app}; \
Flags: external regserver uninsneveruninstall ignoreversion
However, I did not find a flag that would instruct Inno Setup to skip this line / file if d:\sources\SomeDLL.dll does not exist.
Is this possible?
Thank you!
Use the skipifsourcedoesntexist flag:
This flag instructs the compiler -- or Setup, if the external flag is also used -- to silently skip over the entry if the source file does not exist, instead of displaying an error message.
Try this flag:
onlyifdestfileexists
Only install the file if a file of the same name already exists on the user's system. This flag may be useful if your installation is a patch to an existing installation, and you don't want files to be installed that the user didn't already have.
http://www.jrsoftware.org/ishelp/index.php?topic=filessection

Kill process before (re)install using "taskkill /f /im" in Inno Setup

I install a service/daemon, which needs to be killed before uninstall and reinstall.
I already found out how to do it for uninstall:
[UninstallRun]
Filename: "taskkill"; Parameters: "/im ""My Service.exe"" /f"; Flags: runhidden
The [Run] section, however, runs after install, so I can't use it for that. What is the best way to kill the process using taskkill before install?
Please note that I specifically want to kill the process. A more complex solution using IPC offers no benefits in my case, I just want to execute taskkill before installing a particular file.
I found a way using the BeforeInstall parameter and a simple Pascal Script function in the code section. I added a string parameter so it can be reused for multiple processes.
[Files]
Source: "My Service 1.exe"; DestDir: "{app}"; Flags: ignoreversion; \
BeforeInstall: TaskKill('My Service 1.exe')
Source: "My Service 2.exe"; DestDir: "{app}"; Flags: ignoreversion; \
BeforeInstall: TaskKill('My Service 2.exe')
[Code]
procedure TaskKill(FileName: String);
var
ResultCode: Integer;
begin
Exec('taskkill.exe', '/f /im ' + '"' + FileName + '"', '', SW_HIDE,
ewWaitUntilTerminated, ResultCode);
end;
Unless the installer needs to run on a Windows XP machine, or you have set CloseApplications directive to no (the default is yes), the installer should close the application automatically:
The functionality is available since Inno Setup 5.5 on Windows Vista and newer.
Though sometimes yes is not enough, you need to use force:
Installer created via Inno Setup, can't close applications during installation on Windows 10

Resources