We recently moved from a Visual Studio setup msi-based installer to Inno Setup but we're having issues with upgrading existing installs using this new installer. I know that the upgrade code remains static even as the product code changes every update, so I initially tried to set that as the AppId in the Inno Setup project, but that doesn't work. I then tried a number of other guid's none of which work.
Is there a way to properly upgrade an msi install with an Inno Setup installer?
No, I don't think so - InnoSetup is expressly NOT an MSI-based installer.
You will need to first properly uninstall your old MSI-based installation using e.g. msiexec /X (product-code or MSI file name), and then you can install the new stuff using InnoSetup.
To install an .MSI file from within an Inno Setup installation, try these lines:
[Files]
Source: "Your-MSI-File.msi"; DestDir: "{tmp}"
[Run]
Filename: "msiexec.exe"; Parameters: "/i ""{tmp}\Your-MSI-File.msi"""
Credits for Alex Yackimoff
http://www.jrsoftware.org/iskb.php?msi
I needed to do this with an MSI package (replace with Inno Setup installer). I used the following code in the Inno Setup installer to automatically uninstall the MSI package (if installed) at installation time:
function PrepareToInstall(var NeedsRestart: Boolean): string;
var
OldAppGuid, SubKeyName: string;
OldAppFound: Boolean;
ResultCode: Integer;
begin
NeedsRestart := false;
result := '';
if IsAdminInstallMode() then
begin
OldAppGuid := '{nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn}';
SubKeyName := 'SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\' + OldAppGuid;
OldAppFound := RegKeyExists(HKEY_LOCAL_MACHINE, SubKeyName);
if not OldAppFound then
begin
SubKeyName := 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\' + OldAppGuid;
OldAppFound := RegKeyExists(HKEY_LOCAL_MACHINE, SubKeyName);
end;
if OldAppFound then
begin
Exec(ExpandConstant('{sys}\msiexec.exe'), // Filename
'/X ' + OldAppGuid + ' /qb- REBOOT=ReallySuppress', // Params
'', // WorkingDir
SW_SHOW, // ShowCmd
ewWaitUntilTerminated, // Wait
ResultCode); // ResultCode
end;
end;
end;
Of course, replace {nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn} with the MSI package's product ID GUID.
Related
Inno Setup Version 5.5.9
Windows 7
We created an install package which installs the program in the current user's appdata\local\programs folder ({userpf}).
When trying to uninstall the program from command-line using Administrator account, it does the delete from the file structure, but the entry on the "Programs and Features" still exists. You have to go into the registry and delete the key.
We did various tests.
When you uninstall a 32-bit application from a user, who is a a administrator, it works, but not when you try to uninstall, where the user is not an administrator.
When you uninstall a 64-bit application, it does not work for an administrator or user.
When you look at the registry the 32-bit administrator's entry is under HKEY_LOCAL_MACHINE and the 32-bit user and 64-bit user and administrator's entry is under HKEY_USERS.
It seem that when entry is under HKEY_USERS, it does not delete it when uninstalling by administrator.
We use PDQ to log onto a PC to uninstall files. If the user go into programs and features and uninstall it works 100%. When uninstalling Silverlight using PDQ it works.
Thank you
Hendriette
If an application is installed using Inno Setup without Administrator privileges, its Uninstall registry entry is stored to HKCU hive of the local account running the installation.
To uninstall such installation, you have to use the same local account.
If you run the uninstaller using any other account, it won't have an access to HKCU of the original account (which did run the installer) and won't be able to remove it.
It does not matter that you run the uninstaller by a user with Administrator privileges. Inno Setup does not remember, what account did run the installation. So even Administrator won't be able to identify, what registry hive to look to for the HKCU Uninstall registry entry.
If you need to support a remote administration, you have to both install and uninstall with Administrator privileges.
If you need to keep your approach, you would have to modify the uninstaller to programmatically identify the original account and remove the Uninstall registry entry:
const
{ Can use #SetupSetting('AppId') [in curly brackets], }
{ if AppId directive is specified in [Setup] section }
AppId = 'My Program';
procedure RemoveNonAdminUninstallKeys;
var
Subkeys: TArrayOfString;
Subkey: string;
UninstallerLocation: string;
UninstallKey: string;
InstallLocation: string;
I: Integer;
begin
UninstallerLocation :=
AddBackslash(ExtractFilePath(ExpandFileName(ExpandConstant('{uninstallexe}'))));
Log(Format('Uninstalling from "%s"', [UninstallerLocation]));
RegGetSubkeyNames(HKU, '', Subkeys);
for I := 0 to GetArrayLength(Subkeys) - 1 do
begin
Subkey := Subkeys[I];
Log(Format('Testing account "%s"', [Subkey]));
UninstallKey :=
Subkey + '\Software\Microsoft\Windows\CurrentVersion\Uninstall\' +
AppId + '_is1';
{ Would be more appropriate to compare "uninstallexe" against a path }
{ in UninstallString, but that would require more complicated parsing. }
{ Using InstallLocation is easier, and safe, }
{ as long as UninstallFilesDir has its default value of "app" }
if RegQueryStringValue(HKU, UninstallKey, 'InstallLocation', InstallLocation) then
begin
InstallLocation := AddBackslash(ExpandFileName(InstallLocation));
Log(Format('Checking installation location "%s"', [InstallLocation]));
if CompareText(InstallLocation, UninstallerLocation) = 0 then
begin
Log(Format('Installation location matches, deleting Uninstall key "%s"', [
UninstallKey]));
if RegDeleteKeyIncludingSubkeys(HKU, UninstallKey) then
begin
Log(Format('Uninstall key "%s" deleted', [UninstallKey]));
end
else
begin
MsgBox(Format('Failed to delete Uninstall key "%s"', [UninstallKey]),
mbError, MB_OK);
end;
Break; { Do not try other acccounts }
end;
end;
end;
end;
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
begin
if CurUninstallStep = usPostUninstall then
begin
if IsAdminLoggedOn then
begin
Log('Administrator uninstallation, ' +
'will try to detect if uninstalling non-administrator installation');
RemoveNonAdminUninstallKeys;
end;
end;
end;
I am working on an installer for the deployment of a piece of software and a variety of it's dependencies. One of these dependencies is an unsigned driver. This driver already has it's own installer, so my Inno Setup script simply calls this .exe if the driver is not currently installed.
The installation of this driver then hangs before the windows unsigned driver prompt shows. It's worth noting that the prompt WILL eventually show, but each time this takes at least 10 minutes and that is just unacceptable.
I have tried running with and without elevated privileges. This problem is not encountered when running the driver installation executable separately.
I have this issue on the following operating systems:
Windows 7 Professional SP1 - with the KB3033929 security update.
Windows 8.1 Professional
Edit to include code*
[Files]
Source: src\ff\driverexecutable.exe; DestDir: {tmp}\ff; Check: ffDriverVerCheck();
[Run]
Filename: {tmp}\ff\driverexecutable.exe; Check: ffDriverVerCheck();
[Code]
function ffDriverVerCheck(): boolean;
var
CVersionMS, CVersionLS, IVersionMS, IVersionLS: cardinal;
begin
if FileExists(ExpandConstant('{sys}\DRIVERS\xxx.sys')) and FileExists(ExpandConstant('{sys}\WdfCoInstaller01011.dll')) then begin
if GetVersionNumbers(ExpandConstant('{sys}\DRIVERS\xxx.sys'), CVersionMS, CVersionLS) and GetVersionNumbers(ExpandConstant('{src}\src\ff\driverexecutable.exe'), IVersionMS, IVersionLS)then begin
if CVersionMS < IVersionMS then begin
Result := True;
end else begin
Result := False;
end;
end;
end else begin
Result := True;
end;
end;
The code above will only extract the driverexecutable.exe to temp to be installed if an old, or no, version of the driver currently exists on the system.
I need to do one serup application with couple of dll's:
In the pas-script do used compinstkey.dll which call the aspr_ide.dll.
I did in the iss-script:
[Files]
Source: aspr_ide.dll; DestDir: {app};Flags: ignoreversion
Source: CompInstKey.dll; DestDir: {app}; Flags: ignoreversion
....
[Code]
....
function GetRegistrationKeys: PAnsiChar;
external 'GetRegistrationKeys#files:aspr_ide.dll stdcall';
procedure Installinfo(ApplicationName, Version, UserName, Key: PAnsiChar);
external 'Installinfo#files:CompInstKey.dll stdcall';
....
function InitializeSetup: Boolean;
begin
Result := False;
ExtractTemporaryFile('aspr_ide.dll');
ExtractTemporaryFile('CompInstKey.dll');
end;
.....
in the Windows XP it work fine, but Windows7 do throw the error "Cannot import dll: C:...\Temp\tp.GHPtmp\aspr_ide.dll". At the pause of installation the aspr_ide.dll in this moment exist at the "C:...\Temp\tp.GHPtmp\".
What do I wrong? Why dont run under Windows 7? How can I correct it?
You should use the delayload keyword on your function declarations like:
function GetRegistrationKeys: PAnsiChar;
external 'GetRegistrationKeys#files:aspr_ide.dll stdcall delayload';
procedure Installinfo(ApplicationName, Version, UserName, Key: PAnsiChar);
external 'Installinfo#files:CompInstKey.dll stdcall delayload';
The delayload causes the setup to only load the DLL when it is called rather than at startup as documented at http://www.jrsoftware.org/ishelp/index.php?topic=scriptdll.
First, I suggest you update Inno to version 5.5.9, which has the fewest bugs. Second, recompile the DLL on the Win7 system because DLLs may not be the same in the XP and Win7 dependent dynamic libraries.
I cannot find a way for Inno Setup to install drivers.
I have checked these questions here:
Inno setup: install drivers with rundll32 or dpinst?
How to run a file before setup with Inno Setup and How to install DirectX redistributable from Inno-setup?.
My code is like this:
[Files]
Source: "drivers\dpinst64.exe"; DestDir: "{app}\drivers"; Check: Is64BitInstallMode; Components: drivers;
[code]
function PrepareToInstall(var NeedsRestart: Boolean): String;
var
ResultCode: Integer;
begin
if IsWin64 then begin
ExtractTemporaryFile('drivers\dpinst64.exe');
Exec(ExpandConstant('{tmp}\dpinst64.exe'), '-install "' + ExpandConstant('{tmp}') + '"', '', SW_SHOW, ewWaitUntilTerminated, ResultCode);
end;
end;
1) Right now my installer crashes because it cannot find drivers\dpinst64.exe when extracting the temporary file.
2) Before this i tried simply running the .exe in [run] but nothing happened. When the .exe was run, the run lasted 5 miliseconds and then I got the -2147483648 return code. Exec(ExpandConstant('{win}\notepad.exe'), '', '', SW_SHOW, ewWaitUntilTerminated, ResultCode) runs just fine in InitializeSetup.
What could be the problem here? Is there another better way to initiate driver instalation right before the installer finishes its work?
If you try this, what will happen?
What is the parameter to install dpinst64.exe? From your attempt, it looks like this (assuming that {tmp} ends up being Windows %TEMP%):
%TEMP%\dpinst64.exe -install "%TEMP%"
Is it the correct statement to install dpinst64.exe?
[Files]
Source: "drivers\dpinst64.exe"; DestDir: "{tmp}"; Check: Is64BitInstallMode; Components: drivers;
[Code]
function PrepareToInstall(var NeedsRestart: Boolean): String;
var
ResultCode: Integer;
begin
if IsWin64 then begin
ExtractTemporaryFile('dpinst64.exe');
Exec(ExpandConstant('{tmp}\dpinst64.exe'), '-install "' + ExpandConstant('{tmp}') + '"', '', SW_SHOW, ewWaitUntilTerminated, ResultCode);
end;
end;
I assume that the driver dpinst64.exe is the only file in the drivers folder of your source that needs to be distributed in your installer. If it is not the case, then you should type as follows:
[Files]
Source: "drivers\*"; DestDir: "{tmp}"; Check: Is64BitInstallMode; Components: drivers;
To answer your questions:
1. You should use ExtractTemporaryFile('dpinst64.exe'); instead of ExtractTemporaryFile('drivers\dpinst64.exe');.
2. For the failure to run the DPINST64.EXE, you might need to extract the INF, SYS, and any other dependencies for the driver into the directory where DPINST64.EXE. You would need multiple ExtractTemporaryFile statements to extract multiple files.
keep in mind that dpinst is software first meaning it doesn't actually install the device until it is plugged in.
I have this following code with Inno Setup.
But how can I apply this similar function to .msi file?
msiexec /I "\package\file.msi" /qb? How?
procedure AfterMyProgInstall(S: String);
var
ErrorCode: Integer;
begin
{MsgBox('Please wait the libraries are getting installed, ' +
'without the libraries it wont work.', mbInformation, MB_OK);}
ExtractTemporaryFile(S);
{SW_SHOW, SW_SHOWNORMAL, SW_SHOWMAXIMIZED, SW_SHOWMINIMIZED, SW_SHOWMINNOACTIVE, SW_HIDE}
ShellExec('', ExpandConstant('{app}\package\' + S), '', '', SW_SHOWNORMAL,
ewWaitUntilTerminated, ErrorCode);
end;
Try this:
ShellExec('', 'msiexec.exe',
ExpandConstant('/I "{tmp}\package\file.msi" /qb'),
'', SW_SHOWNORMAL, ewWaitUntilTerminated, ErrorCode);
Or:
[Files]
Source: file.msi; DestDir: {tmp}; Flags: deleteafterinstall;
[Run]
Filename: "msiexec.exe"; Parameters: "/i ""{tmp}\file.msi"" /qb"; WorkingDir: {tmp};
Note that: I'm using Inno Setup 5.5.3 on Windows 7, and that this code
is for the Inno Setup script in the run section. With this code you can
run msi files without any problems. Here is the code:
[Run]
Filename: `{src}\PhysX.msi;` Description: Nvidia PhysX; Verb: open; Flags: shellexec postinstall waituntilterminated runascurrentuser skipifsilent
Building on the answer #kobik gave. I had to include the '.exe' in the Filename.
Like so:
if not ShellExec('', 'msiexec.exe', ExpandConstant('{tmp}\package\file.msi'),
'', SW_SHOWNORMAL, ewWaitUntilTerminated, ErrorCode)
then
MsgBox('Msi installer failed to run!' + #13#10 + ' ' +
SysErrorMessage(ErrorCode), mbError, MB_OK);
Although kobik's option to use "msiexec.exe /i" in Run section generally works, we faced a problem of admin right downgrade with it:
[Run]
Filename: "msiexec.exe"; Parameters: "/i ""{tmp}\file.msi"" /qb"; WorkingDir: {tmp};
When msiexec.exe /i file.msi runs this way it requests the admin rights with UAC (as expected, it is really required in our case). But somewhere in the middle of this installation part when "file.msi" is trying to start a windows service it appeared to be right-downgraded and have not enough privileges to start windows service.
However when it's launched via shellexec it goes ok without this problem. So this is how it worked to me:
[Run]
Filename: "{tmp}\file.msi"; Flags: skipifsilent shellexec waituntilterminated hidewizard;