How to re-cache an MSI file

Sometimes, installed MSIs fail to uninstall. There can be various reasons, but one of the most common ones is when a custom action fails.

'Error 1720: There is a problem with this Windows Installer package. A script required for this install to complete could not be run. Contact your support personnel or package vendor.

This could happen because the target script/exe is missing, or because the custom action script has an error in it, or due to a missing condition (maybe the custom action only had to run during the installation). What’s relevant is that the MSI will always try to run that custom action and probably fail.

The solution to this issue is editing the MSI and fixing the issue (disabling the custom action for uninstall, for example), and then re-caching it. This way, Windows Installer will use the new version. It’s important that the elements used for identification remain the same: ProductCode, PackageCode, ProductVersion and so on.

To re-cache an MSI, one might want to use the command line:

msiexec /fv "path_to_msi" <optional parameters>

However, the script below takes this further and goes through the following logic:
– It reads MSI info from the MSI file (ProductCode, PackageCode)
– It checks if a product with the corresponding ProductCode is installed
– If Yes, it retrieves the PackageCode and if it’s the same as the one we’re trying to re-cache, then it actually re-caches the MSI


'Logic:
'The Script retrieves the PackageCode and ProductCode from the MSI file, then it searches (by ProductCode). 
'If the Product IS installed and the PackageCode is THE SAME, it recaches the MSI

Option Explicit

Const ERR_NOTFOUND = 2605
Const ERR_MSI = 2603 'general MSI io/query failure
Const ERR_DIFPACKAGE = 3000

'Create Objects, declare variables
dim installer
dim strProductCodeIn, strPackageCodeFound, strMsiFilePath, strLogFile, strPackageCodeIn
dim retCode, verFound, bStatus

retCode = 1

strMsiFilePath = wscript.arguments(0) 
strProductCodeIn = GetMsiProperty(strMsiFilePath, "ProductCode")
strPackageCodeIn = GetMsiPackageCode(strMsiFilePath)


strLogFile = wscript.arguments(1)
If  strProductCodeIn = "" Or strPackageCodeIn = "" Then wscript.quit(ERR_MSI)

' Check if Product Is Installed, by Checking PC
On error resume next
Set installer = Wscript.CreateObject("WindowsInstaller.Installer") 
strPackageCodeFound = installer.ProductInfo(strProductCodeIn,"PackageCode")

On error Goto 0
If strPackageCodeFound = "" then 
	bStatus = False
	retCode = ERR_NOTFOUND
ElseIf strPackageCodeFound = strPackageCodeIn Then
	bStatus = True ' present
	dim strCmd, shell 
	Set shell = CreateObject("Wscript.Shell")
	
	strCmd = "msiexec /fv " & Chr(34) & strMsiFilePath & Chr(34) & " /norestart /qn REBOOT=ReallySuppress /L*v " & Chr(34) & strLogFile & Chr(34)
	retCode = shell.Run(strCmd, 0, True)
Else
	'ProductCode foundversion found but cannot be replaced.
	retCode = ERR_DIFPACKAGE
End If

Set installer = Nothing
wscript.quit retCode

Please feel free to use the script.

The extra functions can be found on the corresponding articles, here:

GetMsiProperty
GetMsiPackageCode

The exit codes of the script can be customized, of course.