views:

1099

answers:

2

As a followup to this question and after reading Rob Mensching's blog post that was referred to from this answer, it seems the way I'm trying to do it with msizap is a bad idea and I'm seeing some problems with it in testing.

In his post under what he'd do differently, he said:

With the data build a solution. I expect that a new MSI could be built to address the problems. Then you send the fix and instructions out to all of those with the problem. It would probably be something like a recache/reinstall (msiexec /fv, a supported switch) over top the problematic install then uninstall. That way the machine stays in a known state and the setup incrementally improves.

How would such an MSI be built? I'm using WiX and the problem is the uninstall process is trying to run an exe after it's been removed because it's scheduled after InstallFinalize. (forgot to specify that it should only be run on install not uninstall)

A: 

If the MSI just won't uninstall you just might have to use MSI zap. It doesn't remove any files but you can use Orca to see a list of all the files in the MSI and manually delete them.

Brian Ensink
+2  A: 

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
Trotts
Any links or resources on how to do that?
Davy8
Updated to provide more information
Trotts

related questions