Depending on the type of problem you have with the uninstall and how you are deploying it it is entirely possible to write a script and deploy it to edit the cached MSI on the machine to fix the problem. You can iterate through the MSI's in the C:\Windows\Installer\ folder to look for the one for your product ( Opening them all and reading the summary information for example )
Once you have found the one you need you can modify the tables directly to fix whatever problem you have introduced (i.e. disabling custom action on uninstall etc), next time the uninstall is called the problem will not occur.
Resources:
Windows Installer SQL Reference
Windows Installer Automation Interface Reference
Including a sample script, I should point out that this is mainly for say an internally deployed application not really something you could send to customers as a fix but its possible to create a binary that does the same thing and have it included in say a setup.exe where the binary kicks off first to clean up and remove the previous uninstall and then put down the new one, or alternatively just send out the binary to fix the uninstall issues.
Option Explicit
Dim objFS, objShell
Dim objFolder, objFiles, objFile
Dim objInstaller
Dim installerPath, titleToFind
Dim queries
Const msiOpenDatabaseReadOnly = 0
Const msiOpenDatabaseTransact = 1
Set objInstaller = CreateObject("WindowsInstaller.Installer")
Set objShell = CreateObject("WScript.Shell")
installerPath = objShell.ExpandEnvironmentStrings("%SystemRoot%") & "\Installer\"
'Set the title you want to use for comparison
titleToFind = "Sample"
' Define the queries you wish to run against the database if found
queries = Array( "UPDATE `InstallExecuteSequence` SET `Condition` = 'NOT Installed' WHERE `Action` = 'SampleAction'", _
"DELETE FROM `InstallExecuteSequence` WHERE `Action` = 'SampleAction'")
Set objFS = CreateObject("Scripting.FileSystemObject")
On Error Resume Next
If ( objFS.FolderExists(installerPath)) Then
Set objFolder = objFS.GetFolder(installerPath)
Set objFiles = objFolder.Files
For Each objFile in objFiles
If ( StrComp ( Right(objFile.Name, 4), ".msi") = 0 ) Then
If ( CheckMSI (installerPath & objFile.Name, titleToFind) ) Then
UpdateMSI ( installerPath & objFile.name)
Exit For
End If
End If
Next
End If
Set objFS = Nothing
Set objShell = Nothing
Set objFile = Nothing
Set objFiles = Nothing
Set objFolder = Nothing
Set objInstaller = Nothing
' Check if the title in the MSI matches the one you are looking for
Function CheckMSI ( msiPath, title)
Dim objDatabase, objSummary
Dim msiTitle
Set objDatabase = objInstaller.OpenDatabase ( msiPath, msiOpenDatabaseReadOnly ) : CheckError
Set objSummary = objDatabase.SummaryInformation(0)
msiTitle = objSummary.Property(2)
If ( StrComp ( msiTitle, title) = 0 ) Then
CheckMSI = true
Else
CheckMSI = false
End If
Set objSummary = Nothing
Set objDatabase = Nothing
End Function
' Loop though the queries specified above and execute them against the MSI
Function UpdateMSI (msiPath)
Dim objDatabase
Dim objView
Dim query
Set objDatabase = objInstaller.OpenDatabase(msiPath, msiOpenDatabaseTransact) : CheckError
For Each query in queries
Set objView = objDatabase.OpenView (query) : CheckError
objView.Execute : CheckError
Next
objDatabase.Commit
Set objView = Nothing
Set objDatabase = Nothing
End Function
Sub CheckError
Dim message, errRec
If Err = 0 Then Exit Sub
message = Err.Source & " " & Hex(Err) & ": " & Err.Description
If Not installer Is Nothing Then
Set errRec = installer.LastErrorRecord
If Not errRec Is Nothing Then message = message & vbLf & errRec.FormatText
End If
Fail message
End Sub
Sub Fail ( message )
WScript.Echo message
WScript.Quit 2
End Sub