Hello,

I got a Package "Odyssey 4.8", its a Vendor MSI which call exe in it.
Installation works fine..
During Uninstallation,I have added vbscript in msi for uninstallation of exe and after that I have added Custom action to delete application folder after UninstallDriver Custom action.
I have put UninstallDriver Custom action in Synchronous mode still the Uninstallation of MSI finishes first and uninstallation of exe continues..
Is there any way to hold that Custom action "UninstallDriver" till it finishes in MSI?

I have added following Custom action in MSI in Immediate Execution

Return = WshShell.Run("C:\Program Files\Juniper Networks\Odsyssey Acces client\uninstall.exe", , true)

Custom action works fine but MSI finishes its Uninstallation and exit while execution of exe continues..

Thanks in advance !
0 Comments   [ + ] Show Comments

Comments

Please log in to comment

Rating comments in this legacy AppDeploy message board thread won't reorder them,
so that the conversation will remain readable.

Answers

0
My guess would be that the EXE that your CA calls is launching another process and then exiting. Thus the CA sees the process has ended and proceeds.
Answered 05/21/2009 by: VBScab
Red Belt

Please log in to comment
0
Thanks !

Yeah...I think it calls another process.
Answered 05/21/2009 by: Lee_555
Senior Yellow Belt

Please log in to comment
0
Is there any way to hold the MSI Uninstallation till the complete Uninstallation of exe end ?

Thanks !
Answered 05/21/2009 by: Lee_555
Senior Yellow Belt

Please log in to comment
0
I'm not sure whether the MSI itself offers this kind of functionality, but if you're willing to script a little more logic, you could change your custom action to call a script that:

1. Launches the uninstall.exe as you already have
2. Builds a check point while loop that does "while so and so is running, wait 1 second"

This will keep your script engaged while the uninstall finishes whatever it does and when the process ends, it will quit and continue with the MSI actions.

- Jay

EDIT: just in case, "so and so" is whatever the name of the child process that uninstall.exe calls :)
Answered 05/21/2009 by: jcarri06
Senior Purple Belt

Please log in to comment
0
K..not doing much else right now, so here you go:


Dim objSysInfo
Dim Process, strObject, SOANDSOPROCESS
Dim isProcessrunning
Set objSysInfo = CreateObject("winNTSystemInfo")
strObject = "winmgmts://" & objSysInfo.ComputerName
SOANDSOPROCESS = "WHATEVER.EXE"
IsProcessRunning = true
Do While isProcessRunning = true

isProcessRunning = false
For Each Process in GetObject( strObject ).InstancesOf( "win32_process" )
If UCase( Process.name ) = UCase( SOANDSOPROCESS ) Then
IsProcessRunning = True
End If
Next

WScript.Sleep(1000)
Loop


I'm not a big VBscripter so all your experts out there...be nice :) lol.

- Jay
Answered 05/21/2009 by: jcarri06
Senior Purple Belt

Please log in to comment
2
Nice. Here's something I knocked up a while ago, to sit and wait for one of those wretched Java-based setups to finish. Ignore the tests for Custom Action - I've been including that code in all my scripts for eons and can't be bothered to remove it. I've left in the Constants for Wordpad and Notepad so that you can test and tweak before letting it loose.'// Ian Northwood, DeepNet Limited. Programming obfuscation a speciality

Option Explicit

Dim blnResult
Dim blnIsCustomAction
Dim intIndex
Dim blnIsError
Dim strMsg
Dim objFSO
Dim objWSHShell
Dim objWSHShellApp
Dim objWMIService
Dim objEventSink
Dim strScriptFullName
Dim strScriptName
Dim strScriptRoot
Dim strScriptPath
Dim strScriptAppDrive

Dim dicArguments
Dim intWaitCounter
Dim intMaxTime
Dim blnProcessTerminated

Const intFSOForReading = 1
Const intFSOForWriting = 2
Const intFSOForAppending = 8
Const intFSOTristateFalse = 0
Const strNameSeparator = "|"

Const strProcessToStart = "SETUP.EXE"
Const strProcessToWatch = "JAVAW.EXE"
'Const strProcessToStart = "NOTEPAD.EXE"
'Const strProcessToWatch = "WORDPAD.EXE"

Call Main
Call CleanUp

Sub Main
Dim blnMainResult
Dim strPrimaryProcess
Dim strPrimaryProcessPath
Dim strSecondaryProcess
Dim lngProcessID

intMaxTime = 900 '// 15 minutes!

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objWSHShell = CreateObject("Wscript.Shell")
Set objWSHShellApp = CreateObject("Shell.Application")
Set dicArguments = CreateObject("Scripting.Dictionary")
Set objWMIService = GetObject("WINMGMTS:{impersonationLevel=impersonate,(Security)}!\\.\ROOT\CIMV2")

dicArguments.CompareMode = vbTextcompare '// Ignore case in command-line arguments

'//------------------------------------------------------------------------------------------------------------//
'// Set some variables for script usage
'//------------------------------------------------------------------------------------------------------------//
With objFSO
strScriptFullName = WScript.ScriptFullName
strScriptName = WScript.ScriptName
strScriptRoot = .GetFile(strScriptFullName).ParentFolder.ParentFolder
strScriptPath = .GetFile(strScriptFullName).ParentFolder
strScriptAppDrive = .GetFile(strScriptFullName).Drive
End With

'//------------------------------------------------------------------------------------------------------------//
'// Force use of CScript
'//------------------------------------------------------------------------------------------------------------//
Call ForceCScriptExecution(True)

blnIsCustomAction = False

On Error Resume Next
If IsObject(Session) Then
'// We may have arrived here because error-trapping is off
If Err.Number = 0 Then
blnIsCustomAction = True
End If
End If
On Error Goto 0

'// Get the folder you want to process and the target folder
If WScript.Arguments.Count < 2 Then
strMsg = "the name of the process you want to "

Select Case WScript.Arguments.Count
Case 0
strMsg = strMsg & "launch"
strPrimaryProcess = InputBox("Enter " & strMsg, "Process Name", strProcessToStart)
If Len(strPrimaryProcess) = 0 Then
blnIsError = True
strMsg = "You must specify " & strMsg
Call Say(strMsg, blnIsError, blnIsCustomAction)
Exit Sub
End If

strMsg = strMsg & "monitor"
strSecondaryProcess = InputBox("Enter " & strMsg, "Process Name", strProcessToWatch)
If Len(strPrimaryProcess) = 0 Then
blnIsError = True
strMsg = "You must specify " & strMsg
Call Say(strMsg, blnIsError, blnIsCustomAction)
Exit Sub
End If

Case 1
strMsg = strMsg & "monitor"
strSecondaryProcess = InputBox("Enter " & strMsg, "Process Name", strProcessToWatch)
If Len(strPrimaryProcess) = 0 Then
blnIsError = True
strMsg = "You must specify " & strMsg
Call Say(strMsg, blnIsError, blnIsCustomAction)
Exit Sub
End If

Case Else
strMsg = strMsg & "monitor"
strSecondaryProcess = InputBox("Enter " & strMsg, "Process Name", strProcessToWatch)
If Len(strPrimaryProcess) = 0 Then
blnIsError = True
strMsg = "You must specify " & strMsg
Call Say(strMsg, blnIsError, blnIsCustomAction)
Exit Sub
End If
End Select
Else
strPrimaryProcess = WScript.Arguments(0)
strSecondaryProcess = WScript.Arguments(1)
End If

'// Create the Event Notification sink
Set objEventSink = CreateObject("WbemScripting.SWbemSink")

WScript.ConnectObject objEventSink,"EVENTSINK_"

'// If the Primary Process name contains a backslash, then we need to parse the path.
'// If it doesn't, we can use the script's path
intIndex = InStrRev(strPrimaryProcess, "\")
If intIndex > 0 Then
strPrimaryProcessPath = Mid(strPrimaryProcess, 1, intIndex - 1)
strPrimaryProcess = Mid(strPrimaryProcess, intIndex+1, Len(strPrimaryProcess) - intIndex)
Else
strPrimaryProcessPath = strScriptPath
End If

blnMainResult = ProcessLaunch(strPrimaryProcess, strPrimaryProcessPath)

If Not blnMainResult Then
'// Failed to start process
Exit Sub
End If

'// Wait for a short delay, find the secondary process and get its process ID, ready to watch that process
Call Sleep(10)

blnMainResult = FindProcess(".", strSecondaryProcess, lngProcessID)
If Not blnMainResult Then
'// Failed to find secondary process
Exit Sub
End If

Call WatchProcess(lngProcessID)

Do While blnProcessTerminated = False
intWaitCounter = intWaitCounter + 1
Wscript.Sleep(1000)
strMsg = "Waiting..." & intWaitCounter & ", " & intMaxTime
Call Say(strMsg, blnIsError, blnIsCustomAction)
If intWaitCounter > intMaxTime Then
blnIsError = True
strMsg = String(3, vbCRLF) & "Process ID:" & lngProcessID & " timed out at " & Now()
Call Say(strMsg, blnIsError, blnIsCustomAction)
blnMainResult = KillProcessByID(lngProcessID)
'blnMainResult = KillProcessByName(strSecondaryProcess)
End If
Loop

End Sub

Sub ForceCScriptExecution(ByVal blnQuoteArguments)
Dim objShellRun
Dim strArgument
Dim strArguments
Dim strCmdLine
Dim intIndex

'// If running in CScript, do nothing
If UCase(Right(WScript.FullName, 11)) = "CSCRIPT.EXE" Then
Exit Sub
End If

If WScript.Arguments.Count > 0 Then
strArguments = ""
'For Each strArgument In WScript.Arguments
' If Len(strArguments) = 0 Then
' strArguments = strArgument
' Else
' strArguments = strArguments & " " & strArgument
' End If
'Next

For intIndex = 0 To (WScript.Arguments.Count - 1)
If Len(strArguments) = 0 Then
strArguments = WScript.Arguments(intIndex)
Else
strArguments = strArguments & " " & WScript.Arguments(intIndex)
End If
Next

If blnQuoteArguments Then
strArguments = Chr(34) & strArguments & Chr(34)
End If
End If

'// If running in WScript, execute the script using CScript
'// and then quit this script
If UCase(Right(WScript.FullName, 11)) = "WSCRIPT.EXE" Then
Set objShellRun = CreateObject("WScript.Shell")
'objShellRun.Run "CSCRIPT.EXE """ & WScript.ScriptFullName & """", 1, False

strCmdLine = "CSCRIPT.EXE "

If InStr(WScript.ScriptFullName, " ") > 0 Then
strCmdLine = strCmdLine & Chr(34)
End If

strCmdLine = strCmdLine & WScript.ScriptFullName

If InStr(WScript.ScriptFullName, " ") > 0 Then
strCmdLine = strCmdLine & Chr(34)
End If

If Len(strArguments) > 0 Then
strCmdLine = strCmdLine & " "
strCmdLine = strCmdLine & strArguments
End If

objShellRun.Run strCmdLine, 1, False

Set objShellRun = Nothing
WScript.Quit
End If

'// If script engine is anything else, quit with an error
WScript.Echo "Unknown scripting engine."
WScript.Quit
End Sub

Function ProcessLaunch(ByVal strProcessName, ByVal strProcessPath)
Dim objProcess
Dim objStartup
Dim objConfig
Dim lngReturn
Dim lngID

Const intSW_HIDE = 0 '// Hides the window and activates another window.
Const intSW_NORMAL = 1 '// Activates and displays a window.
'// If the window is minimised or maximised, the system restores it to the original size and position.
'// An application specifies this flag when displaying the window for the first time.
Const intSW_SHOWMINIMIZED = 2 '// Activates the window, and displays it as a minimised window.
Const intSW_SHOWMAXIMIZED = 3 '// Activates the window, and displays it as a maximised window.
Const intSW_SHOWNOACTIVATE = 4 '// Displays a window in its most recent size and position.
'// This value is similar to SW_SHOWNORMAL, except that the window is not activated.
Const intSW_SHOW = 5 '// Activates the window, and displays it at the current size and position.
Const intSW_MINIMIZE = 6 '// Minimises the specified window, and activates the next top level window in the Z order.
Const intSW_SHOWMINNOACTIVE = 7 '// Displays the window as a minimised window. This value is similar to SW_SHOWMINIMZED,
'// except that the window is not activated.
Const intSW_SHOWNA = 8 '// Displays the window at the current size and position. This value is similar to SW_SHOW,
'// except that the window is not activated.
Const intSW_RESTORE = 9 '// Activates and displays the window. If the window is minimised or maximised, the system
'// restores it to the=original size and position. An application specifies this flag when
'// restoring a minimised window.
Const intSW_SHOWDEFAULT = 10 '// Sets the show state based on the SW_ value that is specified in the STARTUPINFO structure
'// passed to the CreateProcess function by the program that starts the application.
Const intSW_FORCEMINIMIZE = 11 '// Windows Server 2003, Windows 2000, and Windows XP:
'// Minimises a window, even when the thread that owns the window is hung.
'// Only use this flag when minimising windows from a different thread.

ProcessLaunch = False

Set objStartup = objWMIService.Get("Win32_ProcessStartup")
Set objConfig = objStartup.SpawnInstance_
objConfig.ShowWindow = intSW_NORMAL

Set objProcess = objWMIService.Get("Win32_Process")

'Err.Clear
lngReturn = objProcess.Create(strProcessName, strProcessPath, objConfig, lngID)

If lngReturn = 0 Then
blnIsError = False
strMsg = String(3, vbCRLF) & "Process " & strProcessName & ", ID:" & lngID & ", started at " & Now()

Call Say(strMsg, blnIsError, blnIsCustomAction)
Else
blnIsError = True
strMsg = "Failed to launch " & strProcessName & vbCRLF
strMsg = strMsg & "Error " & lngReturn & ":"
Select Case lngReturn
Case 2
strMsg = strMsg & "Access denied."
Case 3
strMsg = strMsg & "Insufficient privilege."
Case 8
strMsg = strMsg & "Unknown failure."
Case 9
strMsg = strMsg & "Path not found."
Case 21
strMsg = strMsg & "Invalid parameter."
End Select

Call Say(strMsg, blnIsError, blnIsCustomAction)
Exit Function
End If

blnProcessTerminated = False
intWaitCounter = 0

ProcessLaunch = True
End Function

Function KillProcessByName(ByVal strName)

Dim strQuery
Dim objProcess
Dim colProcess

KillProcessByName = False

strQuery = ""
strQuery = strQuery & "SELECT "
strQuery = strQuery & "* "
strQuery = strQuery & "FROM "
strQuery = strQuery & "Win32_Process "
strQuery = strQuery & "WHERE "
strQuery = strQuery & "NAME='"
strQuery = strQuery & strName
strQuery = strQuery & "'"

On Error Resume Next
Set colProcess = objWMIService.ExecQuery(strQuery)

If Err.Number = 0 Then
If colProcess.Count <> 0 Then
For Each objProcess In colProcess
objProcess.Terminate()
If Err.Number = 0 Then
blnIsError = False
strMsg = String(3, vbCRLF) & "Process ID:" & lngID & " terminated at " & Now()
objEventSink.Cancel()
blnProcessTerminated = True
KillProcessByName = True
Else
blnIsError = True
strMsg = String(3, vbCRLF) & "Failed to terminate process ID:" & lngID
End If

Call Say(strMsg, blnIsError, blnIsCustomAction)
Next
End If
End If

On Error Goto 0
End Function

Function KillProcessByID(ByVal lngID)

Dim strQuery
Dim objProcess
Dim colProcess

KillProcessByID = False

strQuery = ""
strQuery = strQuery & "SELECT "
strQuery = strQuery & "* "
strQuery = strQuery & "FROM "
strQuery = strQuery & "Win32_Process "
strQuery = strQuery & "WHERE "
strQuery = strQuery & "ProcessID="
strQuery = strQuery & lngID

On Error Resume Next
Set colProcess = objWMIService.ExecQuery(strQuery)

If Err.Number = 0 Then
If colProcess.Count <> 0 Then
For Each objProcess In colProcess
objProcess.Terminate()
If Err.Number = 0 Then
blnIsError = False
strMsg = String(3, vbCRLF) & "Process ID:" & lngID & " terminated at " & Now()
objEventSink.Cancel()
blnProcessTerminated = True
KillProcessByID = True
Else
blnIsError = True
strMsg = String(3, vbCRLF) & "Failed to terminate process ID:" & lngID
End If

Call Say(strMsg, blnIsError, blnIsCustomAction)
Next
End If
End If

On Error Goto 0
End Function

Function FindProcess(ByVal strMachine, ByVal strProcessName, ByRef lngID)
Dim objProcess
Dim colProcess
Dim strName
Dim lngProcID

FindProcess = False
Set colProcess = objWMIService.ExecQuery ("Select * from Win32_Process")

For Each objProcess in colProcess
strName = objProcess.Name
lngProcID = objProcess.ProcessID
If UCase(strName) = UCase(strProcessName) Then
Exit For
End If
Next

If IsEmpty(lngProcID) Then
strMsg = ""
strMsg = strMsg & "Cannot find process '" & strProcessName & "'"
Else
strMsg = ""
strMsg = strMsg & "Process to watch '" & strProcessName & "' has ID: " & lngProcID
FindProcess = True
lngID = lngProcID
End If

Call Say(strMsg, blnIsError, blnIsCustomAction)

Set colProcess = Nothing

End Function

Sub WatchProcess(ByVal lngID)

Dim strQuery

strQuery = ""
strQuery = strQuery & "SELECT "
strQuery = strQuery & "* "
strQuery = strQuery & "FROM "
strQuery = strQuery & "__InstanceOperationEvent "
strQuery = strQuery & "WITHIN 1 "
strQuery = strQuery & "WHERE "
strQuery = strQuery & "TargetInstance "
strQuery = strQuery & "ISA "
strQuery = strQuery & "'Win32_Process' "
strQuery = strQuery & "AND "
strQuery = strQuery & "TargetInstance.ProcessID='"
strQuery = strQuery & lngID & "'"

objWMIService.ExecNotificationQueryAsync objEventSink, strQuery

strMsg = ""
strMsg = strMsg & "Setting watch on process ID: " & lngID

Call Say(strMsg, blnIsError, blnIsCustomAction)

End Sub

Sub EVENTSINK_OnObjectReady(ByVal objInstance, ByVal objAsyncContext)

If objInstance.Path_.Class = "__InstanceDeletionEvent" Then
blnIsError = False
strMsg = String(3, vbCRLF) & "Process ID:" & objInstance.TargetInstance.ProcessID & " completed at " & Now()
Call Say(strMsg, blnIsError, blnIsCustomAction)

objEventSink.Cancel()
blnProcessTerminated = True
End If

End Sub

Sub EVENTSINK_OnCompleted(ByVal objInstance, ByVal objAsyncContext)

blnIsError = False
strMsg = "ExecQueryAsync completed"
Call Say(strMsg, blnIsError, blnIsCustomAction)
blnProcessTerminated = True

End Sub

Sub Say(ByVal strMsgText, ByVal blnError, ByVal blnCustomAction)
Dim intMSILogMsgType
Dim intEventLogMsgType
Dim objMSIRecord

Const intLogEventSuccess = 0
Const intLogEventError = 1
Const intLogEventWarning = 2
Const intLogEventInformation = 4
Const intLogEventAuditSuccess = 8
Const intLogEventAuditFailure = 16

Const msiMessageTypeFatalExit = &H00000000 '// Premature termination, possibly fatal out of memory.
Const msiMessageTypeError = &H01000000 '// Formatted error message, [1] is message number in Error table.
Const msiMessageTypeWarning = &H02000000 '// Formatted warning message, [1] is message number in Error table.
Const msiMessageTypeUser = &H03000000 '// User request message, [1] is message number in Error table.
Const msiMessageTypeInfo = &H04000000 '// Informative message for log, not to be displayed.
Const msiMessageTypeFilesInUse = &H05000000 '// List of files in use that need to be replaced.
Const msiMessageTypeResolveSource = &H06000000 '// Request to determine a valid source location.
Const msiMessageTypeOutOfDiskSpace = &H07000000 '// Insufficient disk space message.
Const msiMessageTypeActionStart = &H08000000 '// Start of action,
'// [1] action name,
'// [2] description,
'// [3] template for ACTIONDATA messages.
Const msiMessageTypeActionData = &H09000000 '// Action data. Record fields correspond to the template of ACTIONSTART message.
Const msiMessageTypeProgress = &H0A000000 '// Progress bar information. See the description of record fields below.
Const msiMessageTypeCommonData = &H0B000000 '// To enable the Cancel button set [1] to 2 and [2] to 1.
'// To disable the Cancel button set [1] to 2 and [2] to 0

If blnError Then
intMSILogMsgType = msiMessageTypeError
intEventLogMsgType = intLogEventError
Else
intMSILogMsgType = msiMessageTypeInfo
intEventLogMsgType = intLogEventSuccess
End If

If blnCustomAction Then
Set objMSIRecord = Session.Installer.CreateRecord(0)
objMSIRecord.StringData(0) = strMsgText
Session.Message intMSILogMsgType, objMSIRecord
Set objMSIRecord = Nothing
Else
'// Make an entry in Event Log
objWSHShell.LogEvent intEventLogMsgType, strMsg

WScript.Echo strMsgText
End If
End Sub

Sub Sleep(ByVal intSleepPeriod)
'// Timer returns the number of seconds that have elapsed since midnight.

Dim intStartTime
Dim intEndTime
Dim intCurrentTime

On Error Resume Next

intStartTime = Timer
intEndTime = intStartTime + intSleepPeriod

Do While Timer <= intEndTime
Loop

On Error Goto 0

End Sub

Sub CleanUp
Set objWMIService = Nothing
Set objFSO = Nothing
Set objWSHShellApp = Nothing
Set objWSHShell = Nothing
Set dicArguments = Nothing
End Sub
Answered 05/22/2009 by: VBScab
Red Belt

Please log in to comment
0
Thanks Guys !!
Its resolved now....I have added following Custom action in Msi to check for files till it gets uninstalled and then executing removing of Application folder.
So until "uninstall.exe" doesn't uninstall all files, following Custom exection will be in loop.

sFlag="False"
Set fso = CreateObject("Scripting.FileSystemObject")
Do Until SFlag = "True"
If NOT fso.FileExists("C:\Program Files\Common Files\Juniper Networks\Tunnel Manager\Uninstall.exe") AND _
NOT fso.FileExists("C:\Program Files\Common Files\Juniper Networks\JSCDT\jnprShare.dll") AND _
NOT fso.FileExists("C:\Program Files\Common Files\Juniper Networks\TNC Client\uninstall.exe") AND _
NOT fso.FileExists("C:\Program Files\Common Files\Juniper Networks\JUNS\Uninstall.exe") AND _
NOT fso.FileExists("C:\Program Files\Common Files\Juniper Networks\JNPRNA\nsStatsDump.exe") AND _
NOT fso.FileExists("C:\Program Files\Juniper Networks\Odyssey Access Client\uninst.exe") AND Then


strFolder = oWshShell.ExpandEnvironmentStrings("%ProgramFiles%") & "\Juniper Networks\Odyssey Access Client"
If (objFSO.FolderExists(strFolder)) Then
objFSO.DeleteFolder(strFolder),true
End If

Thanks again for giving me ideas.....
Answered 05/25/2009 by: Lee_555
Senior Yellow Belt

Please log in to comment
0
nothing to do that day huh Ian ?

nice script.
Answered 05/25/2009 by: jmcfadyen
Fifth Degree Black Belt

Please log in to comment
0
No, actually, I happened to be browsing the folder containing my script collection, looking for some code I'd used a while ago.
Answered 05/26/2009 by: VBScab
Red Belt

Please log in to comment
Answer this question or Comment on this question for clarity