' AppV_MSI_Streaming_Installer ' (c)2009 TMurgent Technologies ' Version Dated Feb 24, 2009 ' ' This Script is used to "install" App-V (4.5) MSI Virtual Applications and configure them ' for "Streaming from a file share" mode. ' In this mode, a backend file server share is used. You must have install rights to run ' this script. ' ' When run without arguments, you will be prompted for browse to the virtual application package ' MSI file. You can avoid this prompt by adding the path/filename as an argument. In all cases, ' the script expects to find the SFT in the same folder. If OSDs are also in the folder, they ' will be checked to inform you of additional dependencies detected. ' ' Virtual Applications added will have shortcuts published to all users on this machine, ' but will require ACL access to the sft on the content share. The sft is fully loaded ' into the cache when this script is run, but a check is made for ACL and possible updates ' each time the app is used - thus enabling per user authorization even if the shortcut ' is published. An active upgrade to the SFT is possible, however this involves replacing the ' content share sft by a file using the same name (and thus can only be done when the file is not ' in use). ' ' ' ' TO USE: Run this script, point to a Content share folder. ' Option Explicit Dim WshShell Dim ShareName Dim mainFSO, mainBaseFolder, files, curFile Dim MSIName Dim SFTName Dim OSDCounts Dim DependenciesCounts Dim DependenciesNames Dim NewLine Dim SkipDependencies Dim DialogSource Dim DebugLevel MSIName="" SFTName="" OSDCounts = 0 DependenciesCounts = 0 DependenciesNames = "" NewLine=chr(10) ' ============================================ ' Modify these settings ' ============================================ ' ' ------------------------ SET DEBUG LEVEL to affect prompting ' 0 : Only prompt for MSI if needed, and if errors. ' 1 : Add prompt with results at end ' more: Additional debugging prompts DebugLevel = 9 ' ' ------------------------ SET DEFAULT FOLDER WHEN BROWSING ShareName = "\\roadhog\Content" ' ' ------------------------ SET SKIPDEPENDENCIES TO 1 IF DESIRED SKIPDEPENDENCIES = 0 ' ' "UserAccounts" works on XP, might need "MsComDlg" on Vista? DialogSource = "UserAccounts.CommonDialog" ' ============================================ ' End of setting modification area ' ============================================ ' ==================================== Sub GetShareFolderName ' Sub Prompts the user for an MSI, uses this to determine a folder name ' May be local or UNC Dim intResult Dim Ocd Set Ocd = CreateObject(DialogSource) ' UserAccounts or MsComDlg. ? Ocd.Filter = "Virtual Application Package Installer | *.msi" Ocd.Flags = &H0019 Ocd.FilterIndex=1 Ocd.InitialDir=ShareName intResult = Ocd.ShowOpen If intResult = 0 Then WScript.Echo "Installation Cancelled by Request" WScript.Quit Else Dim Cutoff, More Dim StrmsiName Dim thisCut Cutoff = 0 More = 1 StrmsiName = Ocd.Filename 'WScript.Echo StrmsiName Do While More = 1 thisCut = InStr(Cutoff+1, StrmsiName, "\") If thisCut > 0 Then 'WScript.Echo "Got at " & thisCut CutOff = thisCut Else 'WScript.Echo "Done" More = 0 End If Loop If CutOff > 1 Then ShareName = Mid(StrmsiName ,1,CutOff-1) Else WScript.Echo "Invalid Selection" WScript.Quit End If End If End Sub ' ===================================== Sub LookForDependencies( OsdFileName) ' Scan the contents of this OSD file for "DEPENDENCIES" Dim RFile, FileContents Dim StartPos, EndPos, ThisPosStart, ResPos, sPos Dim MyCount MyCount = 0 Set RFile = mainFSO.OpenTextFile( OsdFileName ) FileContents = RFile.ReadAll StartPos = inStr(FileContents, "DEPENDENCIES") EndPos = inStr(StartPos+8,FileContents, "/DEPENDENCIES") 'WScript.Echo "OSD: " & OsdFileName & " - Start=" & StartPos & " End=" & EndPos If StartPos > 0 Then If EndPos > 0 Then ThisPosStart = StartPos Do While ThisPosStart > 0 ResPos = InStr(ThisPosStart, FileContents, "HREF=") If ResPos > 0 Then If ResPos < EndPos Then MyCount = MyCount + 1 sPos = InStr(ResPos+6, FileContents,"""") if sPos > 0 Then DependenciesCounts = DependenciesCounts + 1 DependenciesNames = DependenciesNames & Mid(FileContents, ResPos, sPos - ResPos) & NewLine ThisPosStart = sPos + 8 Else ThisPosStart = ResPos + 4 End If Else ThisPosStart = 0 End If Else ThisPosStart = 0 End If Loop End If End If If MyCount > DependenciesCounts Then DependenciesCounts = MyCount End If End Sub ' ===================================== Function GetFileNames(BaseFolderName) ' Scan through the list of files in the folder and process as appropriate Set files = BaseFolderName.files For each curFile in files If DebugLevel > 7 Then WScript.Echo " FILE: " & curFile.Name End If Dim Splits Splits = Split(curFile.Name,".") If DebugLevel > 9 Then WScript.echo "Last Split: " & Splits( UBound(Splits)) End If ' If patterm match of last Split is .msi If Splits( UBound(Splits))="msi" Then If MSIName="" Then MSIName = curFile.Name Else If DebugLevel > 1 Then WScript.Echo "WARNING: Duplicate MSI Found (" & curFile.Name & ") - ignored" End If End If End If If Splits( UBound(Splits))="sft" Then If SFTName="" Then SFTName = curFile.Name Else ' Handle based on file names: lexographical (_3 versus _2 or _10 versus _9) If DebugLevel > 5 Then WScript.echo "Handle duplicate sfts:" & NewLine & " " & SFTName & NewLine & " " & curFile.Name End If Dim OldSplits, NewSplits OldSplits = Split(SFTName,"_") NewSplits = Split( curFile.Name, "_") If UBound(OldSplits) = 0 Then SFTNAME = curFile.Name 'WScript.Echo "old was not reved" Else If UBound(NewSplits) > 0 Then If Len(SFTName) = Len(curFile.Name) Then If StrComp(SFTName,curFile.Name) < 0 Then SFTName = curFile.Name 'Else 'WScript.echo "same length keep old" End If Else If Len(SFTName) < Len(curFile.Name) Then SFTName = curFile.Name 'Else 'WScript.echo "old name longer, so keep old" End If End If 'Else 'WScript.echo "new was not reved" End If End If If DebugLevel > 5 Then WScript.echo "Comparison Results: " & SFTName End If End IF End If if Splits( UBound(Splits))="osd" Then OSDCounts = OSDCounts + 1 If SkipDependencies = 0 Then LookForDependencies( BaseFolderName & "\" & curFile.Name) End If End If Next End Function ' ========================================================== Sub ProcessInstall(MainFolderShareName, MsiFileName, SftFileName) ' Process the virtual install Dim InsCmd Dim oExec Dim OverRideDouble ' This arg requires doubling of back slashes OverRideDouble = replace(MainFolderShareName & "\" & SftFileName, "\", "\\") InsCmd = "msiexec.exe /I " & MainFolderShareName & "\" & MsiFileName & " MODE=STREAMING OVERRIDEURL=" & OverRideDouble & " LOAD=TRUE /q" If DebugLevel > 1 Then Dim Msg Msg = "Ready to start installation of Virtual Application" If DebugLevel > 5 Then Msg = Msg & ": " & InsCmd End If WScript.Echo Msg End If Set oExec = WshShell.Exec(InsCmd) Do While oExec.Status = 0 WScript.Sleep 200 Loop Dim ResultMsg, ErrMsg ResultMsg = "End Installation of Virtual Application Package " & MsiFileName ResultMsg = ResultMsg & NewLine & " Number Applications: " & OSDCounts ResultMsg = ResultMsg & NewLine & " Dependencies Count: " & DependenciesCounts & " referenced by applications in this package." ErrMsg = oExec.stdout.ReadAll() If ErrMsg = "" Then ErrMsg = "SUCCESS" End If ResultMsg = ResultMsg & NewLine & " MsiExec reports: " & ErrMsg if DebugLevel > 0 Then If (DependenciesCounts > 0) Then ResultMsg = ResultMsg & NewLine & " Dependencies = " & NewLine & DependenciesNames End If End If WScript.Echo ResultMsg End Sub ' ===================================================== ' MAIN APPLICATION ' ===================================================== Set WshShell = CreateObject("WScript.Shell") Set mainFSO = CreateObject("Scripting.FileSystemObject") Dim args Set args = WScript.Arguments If args.Count = 0 Then Call GetShareFolderName Else Dim StrmsiName, More, Cutoff, thisCut StrmsiName = args.Item(0) More = 1 Do While More = 1 thisCut = InStr(Cutoff+1, StrmsiName, "\") If thisCut > 0 Then CutOff = thisCut Else More = 0 End If Loop If CutOff > 1 Then ShareName = Mid(StrmsiName ,1,CutOff-1) Else WScript.Echo "Invalid argument in command line." WScript.Quit End If End If Set mainBaseFolder = mainFSO.GetFolder(ShareName) GetFileNames(mainBaseFolder) If MSIName="" Then WScript.Echo "MSI File not found. Aborting" Else If SFTName="" Then WScript.Echo "SFT File not found. Aborting" Else Call ProcessInstall(ShareName, MSIName, SFTName) End If End If