/build/static/layout/Breadcrumb_cap_w.png

Uninstalling an old version from within msi

Hi,

I am creating an msi file to install a program but if the old version is already installed on the pc it needs to have the old version uninstalled first. I will be running the msi with a transform file but have edited the msi to include a custom script to check if the old version is installed first and if so uninstall it before installing the new version.

However when I run the cmd line for the msi and mst it errors saying that there is an old version on the pc already so it is obviously ignoring my line in the script to uinstall.

This is the script I have got:-

Option Explicit
Dim objShell, objFSO, WScript, strcomputer, strPath
Set objShell = CreateObject("WScript.Shell")
Set objFSO = CreateObject("Scripting.FileSystemObject")
strPath = "C:\Program Files\Bentley\MX"
' ####### Search PC for Bentley MX V8 #######
' ###########################################
If objFSO.FileExists("C:\Program Files\Bentley\MX\mfw\inst\uninst\setup.exe") Then
'#### Uninstall old Bentley MX V8 ####
'#####################################
CopyFile "\\ntserv01\sms\Bentley XM\Bentley MX\Bentley MX V8 XM\setup.iss", "C:\Program Files\Bentley\MX\mfw\inst\uninst\"
objShell.Run "cmd /c CD c:.. & cd Program Files\Bentley\MX\mfw\inst\uninst\ & setup.exe -s -f1setup.iss"
'WScript.Sleep(150000)
End If
' ######## CopyFile Function ########
Sub CopyFile(strSource, strDest)
Dim lfsObject
Dim Source
On Error Resume Next
Set lfsObject = CreateObject("Scripting.FileSystemObject")
Set Source = lfsObject.GetFile(strSource)
Source.Copy strDest
Set lfsObject = nothing
If Err.Number <> 0 Then WriteErr( strUserID & ": Copy " & strSource & " to " & strDest & " " & Err.Description & " " & Now() )
End Sub
'WScript.quit

The copy file part works so I know it is running the script and detects the old version on there but it seems to not even attempt to uninstall it.

Can anyone offer me some advice to how to get this to work? I am new to scripting and have never done custom actions in msi before!

Thanks

0 Comments   [ + ] Show comments

Answers (21)

Posted by: anonymous_9363 15 years ago
Red Belt
0
objShell.Run "cmd /c CD c:.. & cd Program Files\Bentley\MX\mfw\inst\uninst\ & setup.exe -s -f1setup.iss" Hi, Sarah. I can't see how this part of the script would ever run. Apart from anything else, you'd need quote marks around the paths. I wouldn't bother with launching CMD, either, as you're launching a console which then runs a windowing app! How about (untested, just OTTOMH):

Dim strCmdLine

strPath = strPath & "\mfw\inst\uninst"
strCmdLine = Chr(34) & strPath & "\setup.exe & Chr(34) & " -s -f1" & Chr(34) & strPath & Chr(34) & "\setup.iss" & Chr(34)
objShell.Run strCmdLine
Posted by: SarahG 15 years ago
Senior Yellow Belt
0
Thanks, I have now modified my script and when run on its own it works fine but when I add this script into the custom actions in my msi it still seems to ignore the script and just try to install the new software.

Am I missing something obvious??
Posted by: anonymous_9363 15 years ago
Red Belt
0
Where do you have the script sequenced, User Interface, Execute Immediate or Execute Deferred? If ED, what context did you select for its execution?
Posted by: SarahG 15 years ago
Senior Yellow Belt
0
It is in the Installation Sequence under Execute. The In-Script Execution is set to Immediate Execution and I have set it to be the FirstAction to be run.
Posted by: jmpouliot 15 years ago
Second Degree Blue Belt
0
Why don't you use the uninstall registry key?? located in

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall

and do a simple script which will get the uninstall string.


it's more simple!
Posted by: anonymous_9363 15 years ago
Red Belt
0
ORIGINAL: jmpouliot
Why don't you use the uninstall registry key?? located in

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall

and do a simple script which will get the uninstall string.
Jean, you have assumed that the old install was MSI-based. The giveaway that it isn't is the reference to an ISS file. These are files containing recorded installation steps and get passed to an InstallShield set-up stub executeable.
Posted by: jmpouliot 15 years ago
Second Degree Blue Belt
0
ORIGINAL: VBScab

ORIGINAL: jmpouliot
Why don't you use the uninstall registry key?? located in

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall

and do a simple script which will get the uninstall string.
Jean, you have assumed that the old install was MSI-based. The giveaway that it isn't is the reference to an ISS file. These are files containing recorded installation steps and get passed to an InstallShield set-up stub executeable.



yeah I forgot, you got a point !!
Posted by: SarahG 15 years ago
Senior Yellow Belt
0
VBScab is right, the uninstall string is for an exe and not msi based :(

Does anyone know about the custom actions and why it doesn't seem to be looking at it?
Posted by: anonymous_9363 15 years ago
Red Belt
0
Sarah, have you logged the installation with a verbose log file? Other than running the MSI through a debugger, that's the only way you're going to know if the CA is being run.
Posted by: timmsie 15 years ago
Fourth Degree Brown Belt
0
Have you installed the msi + mst with logging, you may spot something obvious?

It sounds like you haven't but are there any conditions on the CA?
Posted by: SarahG 15 years ago
Senior Yellow Belt
0
I have set logging on it and everything comes back with a return value of 1 but the lines for my script are:-

Action start 10:19:32: CheckOldVersionInstalled.
Action ended 10:19:33: CheckOldVersionInstalled. Return value 0.

So it is trying to run it but seems to fail but I'm not sure why!
Posted by: SarahG 15 years ago
Senior Yellow Belt
0
In my script it copies the iss file to the pc and then is supposed to run the uninstall using the iss file. I have run the msi + mst and logged it and it comes back with the Return value of 0 but I have checked and it has copied the iss file, it just never seems to of run the uninstall.

Below is a copy of my script incase you can spot anything obvious?!?! Also, it didn't like wscript.sleep command, what command can I use to make it wait a couple of mins for the uninstall to happen before it moves onto the next action?

Dim strCmdLine, objShell, strpath
Set objShell = CreateObject("WScript.Shell")
Set objFSO = CreateObject("Scripting.FileSystemObject")
strPath = "C:\Program Files\Bentley\MX\mfw\inst\uninst"
'If objFSO.FileExists(Chr(34) & strPath & "\setup.exe" & Chr(34)) Then
'#### Uninstall old Bentley MX V8 ####
'#####################################
CopyFile "\\ntserv01\sms\Bentley XM\Bentley MX\Bentley MX V8 XM\setup.iss", strpath & "\"
strCmdLine = Chr(34) & strPath & "\setup.exe" & Chr(34) & " -s -f1" & Chr(34) & strPath & "\setup.iss" & Chr(34)
objShell.Run strCmdLine
' WScript.Sleep(150000)
'End If
' ######## CopyFile Function ########
Sub CopyFile(strSource, strDest)
Dim lfsObject
Dim Source
On Error Resume Next
Set lfsObject = CreateObject("Scripting.FileSystemObject")
Set Source = lfsObject.GetFile(strSource)
Source.Copy strDest
Set lfsObject = nothing
If Err.Number <> 0 Then WriteErr( strUserID & ": Copy " & strSource & " to " & strDest & " " & Err.Description & " " & Now() )
End Sub
Posted by: anonymous_9363 15 years ago
Red Belt
0
ORIGINAL: SarahG
Also, it didn't like wscript.sleep command, what command can I use to make it wait a couple of mins for the uninstall to happen before it moves onto the next action?
You can't use any 'WScript[dot]' directive bcause that is explicitly for Windows Scripting Host. The VB Script engine in Windows Installer is self-contained and knows nothing of WSH.

Here's a delay Sub:

Sub Sleep(ByVal intDelayInSecs)
Dim datStart
Dim blnCompleted

datStart = Now()
blnCompleted = False

While Not blnCompleted
If DateDiff("s", datStart, Now()) >= CInt(intDelayInSecs) Then
blnCompleted = True
End If
Wend
End Sub
However, because your script has no control over how long the uninstall will take, it would be much neater to wait in a loop until the folder has been deleted, then wait another couple of seconds, THEN continue.

On that point, I wonder if the uninstall is failing because it's trying to remove the folder but cannot because it contains the ISS file? Try copying the ISS file to %TEMP% instead.
Posted by: SarahG 15 years ago
Senior Yellow Belt
0
Ahh so is that why my uninstall isn't working as I have the line Set objShell = CreateObject("WScript.Shell") in there? How do I go about running the uninstall then without using that wscript.shell??

Thanks for the delay sub, do I run it by having sleep 120 in there?

Sorry for my apparent ignorance at this scripting malarky, as I said, I am new to it especially now I can't use wscript!
Posted by: anonymous_9363 15 years ago
Red Belt
0
No, not at all. If you had this:

. Set objShell = WScript.CreateObject("WScript.Shell")

then THAT would fail.

As for the delay, I would use it for now, passing in the number of seconds that you want the delay to wait, yes. Long term, I'd highly recommend using the folder existence loop I mentioned.
Posted by: SarahG 15 years ago
Senior Yellow Belt
0
Ahh, after leaving my pc alone for a while after I thought it had failed I noticed that the uninstall part carried on and finally did uninstall the product. I added in your sleep sub and now it has time to uninstall before the new install starts! :) HURRAH! So it looks like it was the wscript.sleep line that was the problem all along!

If I have the old version of the software installed and I comment out the IF statement lines it runs fine, uninstalls and installs new version. However, I do need it to check if the old product exists on the pc so it only runs the script if the software exists and ignores the script if it isn't installed. Should I be able to use IF statements in MSI VB Scripting or is it a different type of code for this?

Many thanks for all your help so far VBScab, Very Much Appreciated!! :)
Posted by: anonymous_9363 15 years ago
Red Belt
0
There's no reason why you couldn't add it to your script but the "best practise" way to do this would be to create a property and give it a nonsense value, e.g. 0 or "NotPresent". Then you'd use AppSearch to populate that property if a file or registry entry relating to the old product was present. Then, you'd add a condition to the one which your CA hopefully already has (If NOT Installed) which tests for that property NOT being the nonsense value. Thus, if your property was 'ISV5PRESENT' with a default value of 0, your CA's condition would change from

. If NOT Installed

to

. If NOT Installed AND ISV5PRESENT<>0

Do remember to ditch the delay functionality in your release version. I appreciate you're new to scripting but lessons learned now will serve you well in the future. Too many scripts make assumptions about software environments and, whilst they work in a perfect environment, when was the last time YOU worked in such an environment? The sort of thing I'm talking about would be, for example, if your script has code to create a directory, it should check immediately aftertrwards whether that directory got created and not simply rely on an error being generated by the scripting runtime.

Good luck!
Posted by: SarahG 15 years ago
Senior Yellow Belt
0
Hi,

I have finally figured out what AppSearch is and think I have done it right. I have my Install Condition to be MXV8INSTALLED <> V8Installed but it doesn't seem to be running my script. When I run the msi I have looked at the log file it creates and I still get:-

Action start 10:45:40: AppSearch.
Action ended 10:45:40: AppSearch. Return value 1.
Action start 10:45:40: CheckOldVersionInstalled.
Action ended 10:48:40: CheckOldVersionInstalled. Return value 0.

Shouldn't my CheckOldVersionInstalled have a return value of 1 if it has run?
Posted by: anonymous_9363 15 years ago
Red Belt
0
ORIGINAL: SarahG
Shouldn't my CheckOldVersionInstalled have a return value of 1 if it has run?
The value '1' doesn't mean that it's run: it means that it returned a value "ERROR_SUCCESS". Yet another perversity of WI is that the engine translates success values (normally 0 in Wndows) to '1'. So, your log entry 'Return value 0.' means that no success value was returned.

That doesn't necessarily mean that the script failed, only that it didn't reurn a success value.You need to force it to do that.

- Move your code into a function
- Change the script type to 'Executefrom installation' and add the function call name.
- Have your script exit with whatever the return value from the function was. For example:


Dim intReturn

intReturn = Main()
WScript.Quit(intReturn) '// Note that we can use WScript directive here because the script is external to the WI engine

Function Main()
Dim blnReturn

'// Always assume the worst
Main = 3 '// 3 is interpreted as 'Fatal error'

'// Do something which could succeed or fail
blnReturn = CallSomeOtherFunctionOrSub
If blnReturn = False Then
Exit Function
End If

Main = 0
End Function
Now, if the call CallSomeOtherFunctionOrSub is successful, Main returns 0 which WI will translate to 1 and you'll get the desired result.

Lastly, I don't think the value being returned in your current script is being acted on, as the install continues even though the CA didn't succeed. Make sure you have the processing option for the CA to NOT ignore errors, if you want the install to stop if removal of th eold version fails.
Posted by: SarahG 15 years ago
Senior Yellow Belt
0
I have figured out why my script didnt appear to be working, it was a rookie error, it was set to copy a file from a location; only I forgot I changed the directory name the other day so it's issue was that it couldn't find it!! Grrrr!

I read your post from the other day where you told me to ditch the sleep function in my final msi but how else can I make it wait while the uninstall happens via my script before the main msi carries on and installs the new version?

Sorry to keep asking questions about all this!
Posted by: anonymous_9363 15 years ago
Red Belt
0
Well, if it makes you feel any better, I see scripts like that all the time and is why I keep banging on about robust code. For example, if you're going to use 'On Error Resume Next', you *MUST* check for errors after *EACH* operation. Thus, in your example, the line 'Set Source = lfsObject.GetFile(strSource)' would have produced an error, since (as you now know) the source file doesn't exist.

In my Production code, I typically use a routine which parses a file's path and checks that each part of it exists. Thus, for any error, I can tell the user EXACTLY which folder is missing.

OK, coming the Sleep part, did I say you should ditch it? I think I said you should not rely on a simple delay but should check for the folder's removal. You can do that but you need to retain the delay. Once you've added the robustness noted above, you will know how to test for a folder's existence. Thus, your code might look something like this:

While objFSO.FolderExists(strPath)
Call Delay(1) '// Just twiddle your thumbs for 1 second
Wend
'// Carry on with next part of the script


or

Do
Call Delay(1)
Loop Until Not objFSO.FolderExists(strPath)
'// Carry on with next part of the script


NB!
If this were me, I'd build in ANOTHER loop so that, if for some reason the folder didn't get deleted, the script would continue in some capacity. As the code above is now, it would loop forever! I'm sure by now, though, you get the idea.
Rating comments in this legacy AppDeploy message board thread won't reorder them,
so that the conversation will remain readable.
 
This website uses cookies. By continuing to use this site and/or clicking the "Accept" button you are providing consent Quest Software and its affiliates do NOT sell the Personal Data you provide to us either when you register on our websites or when you do business with us. For more information about our Privacy Policy and our data protection efforts, please visit GDPR-HQ