Read path of uninstaller from Registry and run, if existent [duplicate] - windows-10

This question already has answers here:
Inno Setup: How to automatically uninstall previous installed version?
(13 answers)
Closed 9 months ago.
Problem (EDITED) In an InnoSetup script I have to uninstall another installation of my program, which has previously been installed by InnoSetup, too. Although it regards the same program in this particular case, its way of installing has changed (it was a stand-alone installation, now it became part of another program), so the usual update procedure of InnoSetup cannot take care of this situation.
I can read its "UninstallString" from the Windows Registry with a {reg:...} expression as shown below.
Although the Registry entry exists, InnoSetup tells me "Cannot execute file, CreateProcess fails with Code 87".
According to the error message the path read from the Registry is correct; if I execute exactly this path in the command window, the uninstallation works fine.
[Run]
Filename="{reg:HKLM\SOFTWARE\WOW6432\Microsoft\Windows\CurrentVersion\Uninstall\MyProg_is1,UninstallString}"; Parameters: "/silent"; Flags: skipifdoesntexist
Also, if I put that path directly into the [Run] section as "Filename=", it works.
Any ideas what I've done wrong?

This is my solution for the given situation. PrepareToInstall is a call-back function, which is automatically called by the Pascal scripting engine prior to the installation, if it exists.
[Code]
(**
* Uninstall previous stand-alone installation of a program, if present
**)
Function PrepareToInstall(var NeedsRestart: Boolean): String;
var
UninstallString: String;
ResultCode: Integer;
pgmname : String;
begin
pgmname := 'PrevProgramName';
Result := '';
if RegQueryStringValue(
HKLM32, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\' + pgmname + '_is1',
'UninstallString', UninstallString) then
// Strip quotation marks from the path
StringChangeEx(UninstallString, '"', '', True);
if UninstallString <> '' then
if Exec(UninstallString, '/verysilent', '', 1, ewWaitUntilTerminated, ResultCode) then
Result := ''
else
Result := 'Error executing uninstaller, Code ' + IntToStr(ResultCode);
end;

Related

Is it possible to find out if the uninstaller wants a reboot?

Uninstallation of my program sometimes requires a reboot of the computer, depending if some *.dll files are in use by Windows Explorer or not.
Now I want to automatically uninstall an older version of my program if a newer version is installed. I used
MyUninstallerName := OldInstallationDirectory + 'unins000.exe';
function UninstallPreviousVersion : Boolean;
var
ResultCode : Integer;
begin
Result := Exec(MyUninstallerName, '/silent', '', SW_SHOW, ewWaitUntilTerminated, ResultCode);
end;
The uninstallation of the older version is called as first step of the installation of a newer version after the 'Ready to install' page:
procedure CurStepChanged(CurStep: TSetupStep);
begin
if (CurStep = ssInstall) then
UninstallPreviousVersion;
end;
This works fine if a reboot of the computer is not necessary. However, if the computer has to be rebooted, then the uninstaller displays
a window asking 'Do you want to reboot the computer?' after the old version has been uninstalled, while at the same time the installation of the new version continues. This is a highly confusing user experience.
I solved this tentatively by preventing the restart by the uninstaller, and I restart each time the previous version was uninstalled.
function UninstallPreviousVersion : Boolean;
var
ResultCode : Integer;
begin
PreviousVersionWasUninstalled := True;
Result := Exec(MyUninstallerName, '/silent /norestart', '', SW_SHOW, ewWaitUntilTerminated, ResultCode);
end;
function NeedRestart(): Boolean;
begin
Result := PreviousVersionWasUnInstalled;
end;
While this is better (the question 'Do you want to reboot the computer?' appears at the very end of the uninstallation/installation process), it is still not perfect, however. Now each time an uninstallation is performed this results in a reboot, while the reboot is actually only necessary
sometimes and sometimes not.
Is it possible to find out (for example from the ResultCode) if the uninstaller wants a reboot?

Preparing to Uninstall like Preparing to Install Page in Inno Setup

I need to check if several .exe files are running or not (which are installed by setup) and then prompt user to close them if they are running and if not cancel the uninstall process.
Is there any way to have something like Prepare page in install for uninstaller?
Or How can I implement such checking? Even a message box would be perfect also.
If it is your application, make it create a mutex. Then you can use AppMutex directive, which works even for uninstaller.
[Setup]
AppMutex=MyProgMutex
If you cannot modify the application, you need to code the check for running application in Inno Setup. You can for example use IsAppRunning function from the answer by #RRUZ to How to check with Inno Setup, if a process is running at a Windows 2008 R2 64bit? in InitializeUninstall event function.
function InitializeUninstall(): Boolean;
var
Message: string;
begin
while IsAppRunning('MyProg.exe') do
begin
Message := 'The program is running, please close it';
if MsgBox(Message, mbError, MB_OKCANCEL) = IDCANCEL then
begin
Result := False
Exit;
end;
end;
Result := True;
end;
For a similar question on installer, see:
Is it possible to check if program is already running before trying to install it? (Inno Setup)

Inno Setup - How to prevent installation when application is installed already?

I've installed my program. But if I try to install it again, it does and the program is replaced.
I saw this question Inno Setup - How to display notifying message while installing if application is already installed on the machine?
Can I create a certain registry entry so I can check it and prevent a new installation? In this question there is some related information: Skip installation in Inno Setup if other program is not installed.
You do not need to create any registry key. The installer already creates a registry key for the uninstaller. You can just check that. It's the same key, that the answer to question, you refer to, uses. But you do not need to do the check for version. Just check an existence. Also you should check both the HKEY_LOCAL_MACHINE and the HKEY_CURRENT_USER:
#define AppId "myapp"
[Setup]
AppId={#AppId}
[Code]
function InitializeSetup(): Boolean;
begin
Result := True;
if RegKeyExists(HKEY_LOCAL_MACHINE,
'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{#AppId}_is1') or
RegKeyExists(HKEY_CURRENT_USER,
'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{#AppId}_is1') then
begin
MsgBox('The application is installed already.', mbInformation, MB_OK);
Result := False;
end;
end;
Or just reuse IsUpgrade function from Can Inno Setup respond differently to a new install and an update?

How to call "npm install" from Inno Setup?

I'm writing an installer in Inno Setup which installs Node.js, extracts a zip file containing all the node project files, and then needs to install the node app using npm install.
The manual process consists of opening a command prompt, browsing to the directory where these files are (in my case extracted to its Program Files folder corresponding with the {app} folder setting), and then running that exact command line npm install --quiet. However, when doing this in Inno Setup, it fails...
function InstallNodeApp: Integer;
var
C: String;
begin
C:= 'npm install --quiet';
if not Exec(C, '', ExpandConstant('{app}'), SW_SHOWNORMAL, ewWaitUntilTerminated, Result) then begin
Result:= -1;
end;
end;
I've tried putting --quiet in the parameters as well as calling cmd.exe with this command line as a parameter, and many other combinations of attempts, but nothing is working - the execution just fails. The error I get is always The system cannot find the file specified..
How can I perform this node install while receiving the result/exit code?
The problem was that I was using Exec but because of the nature of npm, it needed to use a shell command. So instead, as TLama mentioned in the comments, I used ShellExec and everything worked.
function InstallNodeApp: Integer;
var
C, P, D: String;
begin
C:= 'npm';
P:= 'install --silent';
D:= ExpandConstant('{app}');
if not ShellExec('', C, P, D, SW_HIDE, ewWaitUntilTerminated, Result) then begin
Result:= -1;
end;
end;

Inno-Setup ask once install two times

Using InnoSetup I want to prompt a user if they wish to install an additional piece of software - think of it as a plug-in. My issue is that the additional software package I wish to install is broken up into two MSI files. I want to only prompt the user once to install the package but have each file in the [Run] section check the same value. How do I go about doing this?
[Code]
function InstallSomething:Boolean;
begin
if (MsgBox('Do you want to install something?', mbInformation, mb_YesNo) = idYes) then
Result:=True
else
Result:=False;
end;
[Run]
Filename: {sd}\Software\MyAppA.msi; Check: InstallSomething;
Filename: {sd}\Software\MyAppB.msi; Check: InstallSomething;
So the user should only get the message once but each file should install if true or skip if false.
Can you not store the result of your function call in a boolean variable and use that boolean value in your [Run] section?
It seems that this is possible after reading the following documentation: http://www.jrsoftware.org/ishelp/index.php?topic=scriptcheck

Resources