Installing all Microsoft Visual C++ Runtime modules

On my blog post Latest Supported Visual C++ Downloads you a link to the Microsoft site Latest Supported Visual C++ Downloads. Once downloaded, how to install all these updates?

I made a script that installs all the Microsoft C++ Runtime modules. On x86 only the x86 modules are installed. On x64, both x86 and x64 modules are installed.

The list has been populated on June, 14th, 2014. So the list of C++ Runtime modules might – and will be – change.

The folder list should be as follows:

Folder PATH listing for volume OS
Volume serial number is 000000C0 0475:818B
C:.
|   install_all_VisualCPlusPlusRuntimes_v101.vbs
|   
+---01.Microsoft Visual C++ 2005 SP1 Redistributable Package
|       vcredist_x64.exe
|       vcredist_x86.exe
|       
+---02.Microsoft Visual C++ 2005 SP1 Redistributable Package ATL Security Update
|       vcredist_x64.exe
|       vcredist_x86.exe
|       
+---03.Microsoft Visual C++ 2005 SP1 Redistributable Package MFC Security Update
|       vcredist_x64.EXE
|       vcredist_x86.EXE
|       
+---04.Microsoft Visual C++ 2008 SP1 Redistributable Package
|       vcredist_x64.exe
|       vcredist_x86.exe
|       
+---05.Microsoft Visual C++ 2008 SP1 Redistributable Package MFC Security Update
|       vcredist_x64.exe
|       vcredist_x86.exe
|       
+---06.Microsoft Visual C++ 2010 SP1 Redistributable Package
|       vcredist_x64.exe
|       vcredist_x86.exe
|       
+---07.Microsoft Visual C++ 2010 SP1 Redistributable Package MFC Security Update
|       vcredist_x64.exe
|       vcredist_x86.exe
|       
+---08.Microsoft Visual C++ 2012 Update 3
|       vcredist_x64.exe
|       vcredist_x86.exe
|       
+---09.Microsoft Visual C++ 2013 Redistributable Package
|       vcredist_x64.exe
|       vcredist_x86.exe
|       
+---10.Microsoft Visual C++ 2015 Redistributable Update 3
|       vcredist_x64.exe
|       vcredist_x86.exe
\---11.Microsoft Visual C++ Redistributable for Visual Studio 2017
        vcredist_x64.exe
        vcredist_x86.exe

The only thing you have to do is to download all the redistributable packages:

The code:

' ================================================================================================
' Install all Microsoft C++ Runtimes
' Created by Willem-Jan Vroom
' Version history:
'
' 0.0.1
'    Initial version
'
' 1.0.0
'    Final version
'
' 1.0.1
'    Added support for C++ Redistributable 2015
'
' 2.0.0
'    Addes support for Microsoft Visual C++ Redistributable for Visual Studio 2017
' ================================================================================================

' ------------------------------------------------------------------------------------------------
' Declare the most variables. 
' ------------------------------------------------------------------------------------------------

  Option Explicit

  Dim objShell            : set objShell              = WScript.CreateObject("WScript.Shell")
  Dim objLogFileFSO       : Set objLogFileFSO         = CreateObject("Scripting.FileSystemObject")
  Dim objFSO              : Set objFSO                = CreateObject("Scripting.FileSystemObject")
  Dim objProcessEnv       : Set objProcessEnv         = objShell.Environment("PROCESS")
  Dim objWMIService       : Set objWMIService         = GetObject("winmgmts:\\.\root\cimv2")
  Dim fn_objWMIService    : Set fn_objWMIService      = GetObject("winmgmts:\\.\root\cimv2")
  Dim objReg              : Set objReg                = GetObject("winmgmts:\\.\root\default:StdRegProv")
  Dim objLogFile
  Dim CurrentDir          : CurrentDir                = Left(Wscript.ScriptFullname, InstrRev(Wscript.ScriptFullname, "\"))
                            CurrentDir                = Left(CurrentDir,len(CurrentDir)-1)
  Dim strcomputerName     : strcomputerName           = objProcessEnv("COMPUTERNAME")
  Dim strLogLocation      : strLogLocation            = "C:\WINDOWS\system32\Logfiles"
  Dim strOutputFile       : strOutputFile             = strLogLocation & "\" & strcomputerName & "_Installation_C++Runtimes_" & Replace(FormatDateTime(Now(), 2),"/","-") & ".log"
  Dim strArchitecture     : strArchitecture           = ""
  Dim strCurrentUser      : strCurrentUser            = ""
  Dim strCurrentUserSID   : strCurrentUserSID         = "" 
  Dim strOS               : strOS                     = ""
  Dim strCommand          : strCommand                = ""
  Dim strLine             : strLine                   = ""
  Dim strQuery            : strQuery                  = ""
  Dim strCommonDesktop    : strCommonDesktop          = ""
  Dim strArray            : strArray                  = ""  
  Dim strAppDataFolder    : strAppDataFolder          = ""
  Dim strProcess          : strProcess                = ""
  Dim strValue            : strValue                  = ""
  Dim strKeyPath          : strKeyPath                = ""
  Dim strValueName        : strValueName              = ""
  Dim strLogFile
  Dim dwValue             : dwValue                   = 0
  Dim arrFolderUnsorted   : Redim arrFolderUnsorted(0)

  Dim arrArguments
  Dim arrLanguage
  Dim valCounter
  Dim valResult 
  Dim valReturnCode

  Dim colProcess, objProcess 
  Dim valOSBuildNumber
  Dim objSubfolder
  Dim colItems, objItem, Subfolder
  Dim colSoftware, objSoftware
  Dim arrValues, strHolder

  dim objFolders, objFolder, arrFolders, arrVersions, objVersion


  Const ForWriting           = 2
  Const ForReading           = 1
  Const ForAppending         = 8
  Const OverWriteFiles       = True
  const HKEY_CURRENT_USER    = &H80000001
  const HKEY_LOCAL_MACHINE   = &H80000002
  Const HKEY_USERS           = &H80000003 

' ------------------------------------------------------------------------------------------------
' Create the log location (if not exists)
' Open the logfile.  
' ------------------------------------------------------------------------------------------------

  CreateFolderStructure(strLogLocation)
  OpenLogFile()
  WriteToLog "- ACTION: script started."
  WriteToLog " "

' ------------------------------------------------------------------------------------------------
' Detect the current OS.  
' ------------------------------------------------------------------------------------------------

  Set colItems = objWMIService.ExecQuery("Select Caption,BuildNumber from Win32_OperatingSystem")
  For Each objItem in colItems
      strOS            = objItem.Caption
      valOSBuildNumber = objItem.BuildNumber
  next

' ------------------------------------------------------------------------------------------------
' Detect the current processor architecture.  
' ------------------------------------------------------------------------------------------------

  if objFSO.FolderExists(objShell.ExpandEnvironmentStrings("%windir%") & "\SysWOW64\Config") then  
     strArchitecture = "x64"
     arrVersions=Array("x86","x64")
       else
     strArchitecture = "x86"
     arrVersions=Array("x86")
  end if


  WriteToLog("########## Details regarding operating system                        ##########")
  WriteToLog("Found Operating System:       " & strOS)
  WriteToLog("Found architecture:           " & strArchitecture)
  WriteToLog("########## End details regarding operating system                    ##########")
  WriteToLog(" ")
  WriteToLog("########## Installation                                              ##########")

' ------------------------------------------------------------------------------------------------
' Sets SEE_MASK_NOZONECHECKS to 1.  
' That will avoid a screen that asks for permission to run an application from
' a share or mapped drive. 
' ------------------------------------------------------------------------------------------------

  objProcessEnv("SEE_MASK_NOZONECHECKS") = 1
  
  ' ------------------------------------------------------------------------------------------------
  ' Start the installation:
  '  1. Loop throu all the subfolders, sorted.
  '     Source: 
  '     http://blogs.technet.com/b/heyscriptingguy/archive/2004/11/22/how-can-i-list-the-members-of-a-group-in-alphabetical-order.aspx
  '  2. Depending on the foldername the correct parameters are passed to the vcredit_xx.exe
  '     Logging enabled.
  ' ------------------------------------------------------------------------------------------------ 

  ' 1: Get all the subfolder of the parent folder.

  valCounter  = 0
  set objFolders = objFSO.GetFolder(CurrentDir)
  Set arrFolders = objFolders.SubFolders
  For Each objFolder in arrFolders
      arrFolderUnsorted(valCounter) = objFolder.Name
      Redim Preserve arrFolderUnsorted(valCounter+1)
      valCounter = valCounter + 1
  Next   

  Redim Preserve arrFolderUnsorted(valCounter-1)
  Dim j

  ' 2: Sort them:

  For valCounter = (UBound(arrFolderUnsorted) - 1) to 0 Step -1
    For j= 0 to valCounter
        If UCase(arrFolderUnsorted(j)) > UCase(arrFolderUnsorted(j+1)) Then
            strHolder = arrFolderUnsorted(j+1)
            arrFolderUnsorted(j+1) = arrFolderUnsorted(j)
            arrFolderUnsorted(j) = strHolder
        End If
    Next
  Next

  ' 3: Install

  For valCounter = 0 to ubound(arrFolderUnsorted) ' I know: the folders are sorted now....

      if instr(arrFolderUnsorted(valCounter),"2005") > 0 Then InstallCPlusPlus2005Runtimes
      if instr(arrFolderUnsorted(valCounter),"2008") > 0 Then InstallCPlusPlus2008Runtimes
      if instr(arrFolderUnsorted(valCounter),"2010") > 0 Then InstallCPlusPlus2010Runtimes
      if instr(arrFolderUnsorted(valCounter),"2012") > 0 Then InstallCPlusPlus2012Runtimes
      if instr(arrFolderUnsorted(valCounter),"2013") > 0 Then InstallCPlusPlus2013OrLater
      if instr(arrFolderUnsorted(valCounter),"2015") > 0 Then InstallCPlusPlus2013OrLater
      if instr(arrFolderUnsorted(valCounter),"2017") > 0 Then InstallCPlusPlus2013OrLater

  next

  WriteToLog("########## End Installation                                          ##########")
  WriteToLog("########## Summary of installed C++ Redistributables                 ##########")
  WriteToLog(" ")

  Set colSoftware = objWMIService.ExecQuery("Select * from Win32_Product Where Name LIKE '%Visual C++%' AND (Name LIKE '%Redistributable%' OR Name LIKE '%Runtime%')")
  For Each objSoftware in colSoftware
      WriteToLog("Name:     " & objSoftware.Name)
      WriteToLog("Version:  " & objSoftware.Version)
      WriteToLog(" ")
  Next


  WriteToLog(" ")
  WriteToLog("########## End summary of installed C++ Redistributables             ##########")
  WriteToLog(" ")

  CloseLogFile()
  wscript.quit 0

Sub OpenLogFile() 

' ------------------------------------------------------------------------------------------------
' Subroutine: OpenLogFile() 
' The name of the logfile is mentinoed in the variabele strOutputFile.
' ------------------------------------------------------------------------------------------------

  If objLogFileFSO.FileExists(strOutputFile) Then
     Set objLogFile = objLogFileFSO.OpenTextFile(strOutputFile, ForWriting)
         Else
     Set objLogFile = objLogFileFSO.CreateTextFile(strOutputFile)
  End If

End Sub

Sub CloseLogFile()

' ------------------------------------------------------------------------------------------------
' Subroutine: CloseLogFile()
' Close the log file.
' ------------------------------------------------------------------------------------------------

  WriteToLog "- ACTION: script ended."
  objLogFile.Close
  Set objLogfileFSO = Nothing

End Sub

Function WriteToLog(sLogMessage)

' ------------------------------------------------------------------------------------------------
' Function: WriteToLog(sLogMessage)
' Writes an entry 'sLogMessage' in the logfile.
' ------------------------------------------------------------------------------------------------

  if instr(sLogMessage, "- ACTION: ") = 0 then sLogMessage = "          " & sLogMessage
  objLogFile.WriteLine("Time: " & now & "  " & sLogMessage)

End Function

Sub CreateFolderStructure(strFolderNameToBeCreated)

' ------------------------------------------------------------------------------------------------
' Subroutine: CreateFolderStructure(strFolderNameToBeCreated)
' Creates the map as mentioned in strFolderNameToBeCreated.
' ------------------------------------------------------------------------------------------------

  Dim arrFoldersTMP : arrFoldersTMP = split (strFolderNameToBeCreated,"\")
  Dim strFolder  : strFolder  = ""
  Dim objFolderTMP
 
  For Each objFolderTMP in arrFoldersTMP
      strFolder = strFolder & objFolderTMP
      If NOT objFSO.FolderExists(strFolder) Then
             objFSO.CreateFolder(strFolder)
      end If
      strFolder = strFolder & "\"
  Next
 
End Sub

Function fnKillProcess(strProcessName)

' ------------------------------------------------------------------------------------------------
' Function: fnKillProcess(strProcessName)
' Terminates the given processname.
' ------------------------------------------------------------------------------------------------

  Set colProcess = fn_objWMIService.ExecQuery ("Select * From Win32_Process")
  For Each objProcess In colProcess
    If Instr(LCase(objProcess.Name),LCase(strProcessName)) > 0 Then
       objShell.Run "TASKKILL /F /T /IM " & objProcess.Name, 0, False
       objProcess.Terminate()
       WriteToLog("Terminating application: " & objProcess.Name)
    End If
  Next

End Function

Sub InstallCPlusPlus2005Runtimes
' ------------------------------------------------------------------------------------------------
' Subroutine: InstallCPlusPlus2005Runtimes
' Installs the C++ 2005 Runtime modules.
' ------------------------------------------------------------------------------------------------

  for Each objVersion in arrVersions
      if instr(arrFolderUnsorted(valCounter),"MFC") > 0 Then
         strLogFile = right(arrFolderUnsorted(valCounter),len(arrFolderUnsorted(valCounter))-3) & " (" & objVersion & ").log" 
         strCommand = chr(34) & CurrentDir & "\" & arrFolderUnsorted(valCounter) & "\vcredist_" & objVersion & ".exe" & chr(34) & " /t:c:\temp /q"
         strCommand = strCommand & ":a /c:" & chr(34) & "msiexec /i vcredist.msi /qb! /l*v "
         strCommand = strCommand & chr(34) & chr(34) & strLogLocation & "\" & strLogFile & chr(34) & chr(34) & chr(34)
         WriteToLog("Running command: " & strCommand)
         valResult = objShell.Run(strCommand, 6, True)
         WriteToLog("Result: " & valResult)
           else
         strCommand = chr(34) & CurrentDir & "\" & arrFolderUnsorted(valCounter) & "\vcredist_" & objVersion & ".exe" & chr(34) & " /q"
         WriteToLog("Running command: " & strCommand)
         valResult = objShell.Run(strCommand, 6, True)
         WriteToLog("Result: " & valResult)
     end if
  next

End sub

Sub InstallCPlusPlus2008Runtimes
' ------------------------------------------------------------------------------------------------
' Subroutine: InstallCPlusPlus2008Runtimes
' Installs the C++ 2008 Runtime modules.
' ------------------------------------------------------------------------------------------------

  for Each objVersion in arrVersions
      strLogFile = right(arrFolderUnsorted(valCounter),len(arrFolderUnsorted(valCounter))-3) & " (" & objVersion & ").log" 
      strCommand = chr(34) & CurrentDir & "\" & arrFolderUnsorted(valCounter) & "\vcredist_" & objVersion & ".exe" & chr(34) & " /q /l "
      strCommand = strCommand & chr(34) & strLogLocation & "\" & strLogFile & chr(34)
      WriteToLog("Running command: " & strCommand)
      valResult = objShell.Run(strCommand, 6, True)
      WriteToLog("Result: " & valResult)
  next
  
End sub

Sub InstallCPlusPlus2010Runtimes
' ------------------------------------------------------------------------------------------------
' Subroutine: InstallCPlusPlus2010Runtimes
' Installs the C++ 2010 Runtime modules.
' ------------------------------------------------------------------------------------------------

  for Each objVersion in arrVersions
      strLogFile = right(arrFolderUnsorted(valCounter),len(arrFolderUnsorted(valCounter))-3) & " (" & objVersion & ").log" 
      strCommand = chr(34) & CurrentDir & "\" & arrFolderUnsorted(valCounter) & "\vcredist_" & objVersion & ".exe" & chr(34) & " /passive /norestart /log "
      strCommand = strCommand & chr(34) & strLogLocation & "\" & strLogFile & chr(34)
      WriteToLog("Running command: " & strCommand)
      valResult = objShell.Run(strCommand, 6, True)
      WriteToLog("Result: " & valResult)
  next

End sub

Sub InstallCPlusPlus2012Runtimes
' ------------------------------------------------------------------------------------------------
' Subroutine: InstallCPlusPlus2012Runtimes
' Installs the C++ 2012 Runtime modules.
' ------------------------------------------------------------------------------------------------

  for Each objVersion in arrVersions
      strLogFile = right(arrFolderUnsorted(valCounter),len(arrFolderUnsorted(valCounter))-3) & " (" & objVersion & ").log" 
      strCommand = chr(34) & CurrentDir & "\" & arrFolderUnsorted(valCounter) & "\vcredist_" & objVersion & ".exe" & chr(34) & " /install /passive /norestart /log "
      strCommand = strCommand & chr(34) & strLogLocation & "\" & strLogFile & chr(34)
      WriteToLog("Running command: " & strCommand)
      valResult = objShell.Run(strCommand, 6, True)
      WriteToLog("Result: " & valResult)
  next

End sub

Sub InstallCPlusPlus2013OrLater
' ------------------------------------------------------------------------------------------------
' Subroutine: InstallCPlusPlus2013OrLater
' Installs the C++ 2013 Runtime modules.
' ------------------------------------------------------------------------------------------------

  for Each objVersion in arrVersions
      strLogFile = right(arrFolderUnsorted(valCounter),len(arrFolderUnsorted(valCounter))-3) & " (" & objVersion & ").log" 
      strCommand = chr(34) & CurrentDir & "\" & arrFolderUnsorted(valCounter) & "\vcredist_" & objVersion & ".exe" & chr(34) & " /install /passive /norestart /log "
      strCommand = strCommand & chr(34) & strLogLocation & "\" & strLogFile & chr(34)
      WriteToLog("Running command: " & strCommand)
      valResult = objShell.Run(strCommand, 6, True)
      WriteToLog("Result: " & valResult)
  next

End sub          

C++ Detection Rule

C++ Detection Rule




Error 80073D01 during deployment MSIX application

The deployment of a MSIX application may fail in case roaming user profiles are used. This can be solved easily with a policy adjustment.

The issue is there if this policy setting has been enabled:

Delete cached copies of roaming profiles enabled

Delete cached copies of roaming profiles enabled

If used then enable the use of Allow deployment operations in special folders:

Allow deployment operations in special folders

Allow deployment operations in special folders

The explanation:




Deploy a MSIX application with SCCM and test it on a client

As per SCCM 2012 build 1806 it is possible to deploy MSIX packages. In this article, I will describe all the steps that are needed to deploy a MSIX package on a Windows 10 v1809 client.

In the article Package and install a MSIX package Paint.Net 4.1.5 was packaged as a MSIX package. In Create a MSIX modification package a modification package was created to add some addins. In this article both applications will be used.

Before you can install a MSIX package on a client, you have to:

  1. Deploy the certificate
  2. Enable sideloading apps

This can be done via policies:

Certficate in Trusted Root

Certficate in Trusted Root

Sideload apps

Sideload apps

Before the Paint.Net Addin package can be deployed, it is mandatory to have the dependency application deployed first.

DescriptionPicture
PaintNet has been distributed to all the distribution points as it is a dependency application (SCCM wise).
Paint.Net deployment type.
Paint.Net has been distributed to all distribution points.

After implementing the dependency application the addin application can be deployed:

DescriptionPicture
Create a new application in SCCM. Click [Browse] to select the msix file.
Browse to the msix plugin file.
Click [Next]
Click [Next]
Check the details and click [Next]
Confirm the settings and click [Next]
Click [Close]
Go to the properties of the deployment type.
Go to the Dependencies tab and click [Add]
Fill in the dependency group name and click [Add]
Find the PaintNet application and click [Ok]
Click [Ok]
Click [Apply]
Click [Ok]
Changing the icon that shows up in Software Center.

Properties of the application.
Go to the tab Application Catalog and click [Browse]
Find the correct icon to show in Software Center.
Click [Apply]
Click [Ok]
Deploy the application.
Select the collection and click [Next]
Select the distributiion points and click [Next]
Click [Next]
Click [Next]
Click [Next]
Click [Next]
Click [Next]
Click [Finish]

And last but not least the deployment on the client:

DescriptionPicture
Find the application in Software Center.
Double click on the application.
Click [Install]
Close software center after a successful installation.
Start the application from the Start Menu
The addins are visible.
And the addins are used.




Install all the MSI installation files in a folder

All the files in a folder

All the files in a folder

If you want to install a bunch of MSI files you put them in a folder and install them with a batch file. There is a downsize: you must modify the batch file for each situation. With this PowerShell script you can install all the MSI files in the folder, including applying transform and patch files. You can add your own properties in a csv settings file.

There are some parameters:

  • Install: Use this swith to specify an installation.
  • Uninstall: Use this swith to specify an uninstall.
  • MSIPath: Specify the location where the MSI files are located.
  • Loglocation: Specify the logfile location.

The default log location is C:\Windows\system32\LogFiles.

You can add a transform file.

You can add a transform file.

You can specify a settings file for each application.

You can specify a settings file for each application.

The default settings file is applied to all installations in the folder.

The default settings file is applied to all installations in the folder.

You can also use SCCM to install all the MSI files in the folder. Create an application with one deployment type. You can add all the product codes from each MSI file to identify a successful installation.

SCCM: The 'Application Catalog' tab.

SCCM: The ‘Application Catalog’ tab.

SCCM: Program

SCCM: Program

The Install line is:

The Uninstall line is:

SCCM: Detection rule

SCCM: Detection rule

Installation on the client.

Installation on the client.

Uninstall on the client.

Uninstall on the client.

A demonstration of this script can be found on my YouTube channel: the script and AppV Repository or view it below:

The script:

<#

.SYNOPSIS
    Installs all MSI's in a folder. By default, it is the folder where the script is located, but you can specify another location.
 
.DESCRIPTION
    Installs all MSI's in a folder. By default, it is the folder where the script is located, but you can specify another location.
    If there is a MST that starts with the MSI name, then the MSI / MST combination will be installed.   
    If there is a MSP that starts with the MSI name, then the MSI / MSP combination will be installed. 

    Valid combinations:
    -> MSI file: Application_1.20.msi
    -> MST file: Application_1.20_transform.mst
    -> MSP file: Application_1.20_Update_to_1.40.msp
    -> CSV file: Application_1.20.csv

    Invalid combinations:
    -> MSI file: Application_1.20.msi
    -> MST file: Application_1.20.msi.mst     (.msi should be removed)
    -> MST file: Application_patch.msp        (not the full msi file name)
 
.EXAMPLE
     Installs all the MSIs that are in the folder where the script is located.
     ."\install_all_msi (v10).ps1"

.EXAMPLE
     Installs all the MSIs that are in the folder where the script is located.
     ."\install_all_msi (v10).ps1" -Install

.EXAMPLE
     Uninstalls all the MSIs that are in the folder \\server\share\MSIs.
     ."\install_all_msi (v10).ps1" -Uninstall -MSIPath \\server\share\MSIs

.EXAMPLE
     Uninstalls all the MSIs that are in the folder \\server\share\MSIs. Logfiles are written to C:\Logs
     ."\install_all_msi (v10).ps1" -Uninstall -MSIPath \\server\share\MSIs -LogLocation C:\Logs

.NOTES
    Author:  Willem-Jan Vroom
    Website: https://www.vroom.cc/
    Twitter: @TheStingPilot

 v0.1:
   * Initial version.

 v1.0:
   * Added: properties handler:
         - defaultproperties.csv -> will be applied to all MSI packages or MSI / MST combination in the folder.
                                    This file can be in either the location where the script is, or in the location
                                    where the MSI files are. 
         - .csv     -> will be applied only to the given MSI or MSI / MST combination.
                                    This file must be in the same directory as the MSIs.
     This file must have the following header layout:
         Property,Value
         ALLUSERS,1
         ADDLOCAL,ALL
     The content may be different. 
   * Install and uninstall switch added.
   * Check if user has admin rights. It throws up an error in case not.
   * Added the command line options MSIPath and LogLocation.
   * Added patch support. The pathname must start with the same name as the MSI.
   * Check if both install and uninstall switches are used.
   * Bugfix: error messages when there are quotes around the MSI file name.

#>

[CmdLetBinding()]

param
 (
  # Use this switch to specify an installation.
  [Parameter(Mandatory=$False)]
  [Switch] $Install,

  # Use this switch to specify an uninstall.
  [Parameter(Mandatory=$False)]
  [Switch] $Uninstall,

  # Specify the location where the MSI files are located.
  [Parameter(Mandatory=$False)]
  [String] $MSIPath = "",

  # Specify the logfile location.
  [Parameter(Mandatory=$False)]
  [String] $LogLocation = ""
 )

# ========================================================================================================================
# Function block
# ========================================================================================================================

  Function CreateLogFile
  {

   <#
   .NOTES
   ========================================================================================================================
   Created with:     Windows PowerShell ISE
   Created on:       9-January-2019
   Created by:       Willem-Jan Vroom
   Organization:     
   Functionname:     CreateLogFile
   ========================================================================================================================
   .SYNOPSIS

   This function creates the logfile 

   #>

   param
    (
     [string] $LogFile
    )

   New-Item $LogFile -Force -ItemType File | Out-Null
  }
 
Function WriteToLog
 {
  <#
  .NOTES
  ========================================================================================================================
  Created with:     Windows PowerShell ISE
  Created on:       9-January-2019
  Created by:       Willem-Jan Vroom
  Organization:     
  Functionname:     WriteToLog
  ========================================================================================================================
  .SYNOPSIS

  This function adds a line to the logfile

  #>

  param
     (
      [string] $LogFile,
      [string] $line
     )

   $timeStamp = (Get-Date).ToString('G').Replace("/","-")
   $line = $timeStamp + " - " + $line
   Add-Content -Path $LogFile -Value $line -Force
 }

 Function Import-PropertyFile
  {
   <#
   .NOTES
   ========================================================================================================================
   Created with:     Windows PowerShell ISE
   Created on:       9-January-2019
   Created by:       Willem-Jan Vroom
   Organization:     
   Functionname:     Import-PropertyFile
   ========================================================================================================================
   .SYNOPSIS

   This function imports all the properties that are mentioned in the given property-file 

   #>

   param
    (
     [string] $PropertyFile
    )

   $arrItems      = @()
   $strProperties = ""

   if(Test-Path $PropertyFile)
    {
     $arrItems = @(Import-CSV $PropertyFile)

     if($arrItems.Count -ge 1)
      {
       WriteToLog -LogFile $strLogFile -line "The property file $PropertyFile is applied."
       ForEach($objItem in $arrItems)
        {
         $strProperty    = $objItem.Property
         $strValue       = $objItem.Value
         $strLine        = $strProperty + "=" + $strValue + " "
         $strProperties += $strLine 
        }
       }
    }
    
    Return $strProperties
  }

Function Get-MSIFileInformation
 {

  <#
  .NOTES
  ========================================================================================================================
  Created with:     Windows PowerShell ISE
  Created on:       9-January-2019
  Created by:       Willem-Jan Vroom
  Organization:     
  Functionname:     Get-MSIFileInformation
  ========================================================================================================================
  .SYNOPSIS

  This function reads the various properties from a MSI file. 
  This function has been found on http://www.scconfigmgr.com/2014/08/22/how-to-get-msi-file-information-with-powershell/
  All credits, including the copyright go to Nickolaj Andersen.

  #>


  param
   (
    [parameter(Mandatory=$true)]
    [ValidateNotNullOrEmpty()]
    [System.IO.FileInfo]$Path,
 
    [parameter(Mandatory=$true)]
    [ValidateNotNullOrEmpty()]
    [ValidateSet("ProductCode", "ProductVersion", "ProductName", "Manufacturer", "ProductLanguage", "FullVersion")]
    [string]$Property
   )
  
  Process 
   {
    try 
     {
        # Read property from MSI database
        $WindowsInstaller = New-Object -ComObject WindowsInstaller.Installer
        $MSIDatabase = $WindowsInstaller.GetType().InvokeMember("OpenDatabase", "InvokeMethod", $null, $WindowsInstaller, @($Path.FullName, 0))
        $Query = "SELECT Value FROM Property WHERE Property = '$($Property)'"
        $View = $MSIDatabase.GetType().InvokeMember("OpenView", "InvokeMethod", $null, $MSIDatabase, ($Query))
        $View.GetType().InvokeMember("Execute", "InvokeMethod", $null, $View, $null)
        $Record = $View.GetType().InvokeMember("Fetch", "InvokeMethod", $null, $View, $null)
        $Value = $Record.GetType().InvokeMember("StringData", "GetProperty", $null, $Record, 1)
         
        # Commit database and close view
        $MSIDatabase.GetType().InvokeMember("Commit", "InvokeMethod", $null, $MSIDatabase, $null)
        $View.GetType().InvokeMember("Close", "InvokeMethod", $null, $View, $null)           
        $MSIDatabase = $null
        $View = $null
 
        # Return the value
        return $Value
     } 
    catch 
     {
      Write-Warning -Message $_.Exception.Message ; break
     }
  }
  End 
  {
    # Run garbage collection and release ComObject
    [System.Runtime.Interopservices.Marshal]::ReleaseComObject($WindowsInstaller) | Out-Null
    [System.GC]::Collect()
  }
}

Function Check-HasAdminRights
 {

  <#
  .NOTES
  ========================================================================================================================
  Created with:     Windows PowerShell ISE
  Created on:       11-January-2019
  Created by:       Willem-Jan Vroom
  Organization:     
  Functionname:     Check-HasAdminRights
  ========================================================================================================================
  .SYNOPSIS

  This function checks if an user has admin rights. The function returns $true or $false

  #>

  If (([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"))
   {
    Return $True
   }
    else
   {
    Return $False
   }
 }

 Function Remove-TrailingCharacter
{

<#
.NOTES
=============================================================================================================================================
Created with:     Windows PowerShell ISE
Created on:       18-January-2019
Created by:       Willem-Jan Vroom
Organization:     
Functionname:     Remove-TrailingCharacter
=============================================================================================================================================
.SYNOPSIS

This function removes a trailing character from a string
#>

param
(
 [string] $RemoveCharacterFrom = "",
 [string] $Character           = "" 
)

if($RemoveCharacterFrom.Length -gt 0)
 { 
  if(($RemoveCharacterFrom.SubString($RemoveCharacterFrom.Length-1,1)) -eq $Character) 
  {
   $RemoveCharacterFrom = $RemoveCharacterFrom.Substring(0,$RemoveCharacterFrom.Length-1)
  }
 }


 Return $RemoveCharacterFrom

}

 Function Add-TrailingCharacter
{

<#
.NOTES
=============================================================================================================================================
Created with:     Windows PowerShell ISE
Created on:       06-September-2018
Created by:       Willem-Jan Vroom
Organization:     
Functionname:     Add-TrailingCharacter
=============================================================================================================================================
.SYNOPSIS

This function adds a trailing backslash to a string
#>

param
(
 [string] $AddCharacterTo = "",
 [string] $Character      = ""
)

if($AddCharacterTo.Length -gt 0)
 { 
  $AddCharacterTo = Remove-TrailingCharacter -RemoveCharacterFrom $AddCharacterTo -Character $([char]34)
  if(($AddCharacterTo.SubString($AddCharacterTo.Length-1,1)) -ne $Character) 
  {
   $AddCharacterTo = $AddCharacterTo + $Character
  }
 }
Else
 {
  $AddCharacterTo = $Character
 }

 Return $AddCharacterTo

}

Function Get-AllFilesWithPattern
 {

  <#
  .NOTES
  ========================================================================================================================
  Created with:     Windows PowerShell ISE
  Created on:       13-January-2019
  Created by:       Willem-Jan Vroom
  Organization:     
  Functionname:     Get-AllFilesWithPattern
  ========================================================================================================================
  .SYNOPSIS

  Find all files in the given folder that matches a filter.

  #>

  param
   (
    [string] $FolderToLookIn,
    [string] $Pattern
   )

   $FolderToLookIn = Remove-TrailingCharacter -Character "\" -RemoveCharacterFrom $FolderToLookIn

   $arrItems = @()
   $arrItems = Get-ChildItem -Path $FolderToLookIn -Filter $Pattern
   $arrItems | Sort-Object -Property Name | Out-Null

   Return $arrItems
 }

Function Get-LastItemOfAnArryAndPutItInAString
 {

  <#
  .NOTES
  ========================================================================================================================
  Created with:     Windows PowerShell ISE
  Created on:       13-January-2019
  Created by:       Willem-Jan Vroom
  Organization:     
  Functionname:     Get-LastItemOfAnArryAndPutItInAString
  ========================================================================================================================
  .SYNOPSIS

  Returns the last item of string.

  #>

  param
   (
    [string] $FileName,
    [string] $OldExtension,
    [string] $NewExtension,
    [string] $WhereToLook
   )

   $arrFiles    = @()
   $strFileName = ""
   $FilePattern = $FileName -Replace($OldExtension,$NewExtension)
   $arrFiles    = Get-AllFilesWithPattern -FolderToLookIn $WhereToLook -Pattern $FilePattern
   
   if($arrFiles.Count -gt 0)
    {
     $strFileName = $arrFiles[-1].ToString()
    }
 
   Return $strFileName
}

# ========================================================================================================================
# End function block
# ========================================================================================================================

# ========================================================================================================================
# Define the variables.
# ========================================================================================================================

  $strCurrentDir          = Split-Path -parent $MyInvocation.MyCommand.Definition

  if($MSIPath.Length -eq 0)
   {
    $MSIPath = $strCurrentDir
   }
  
  if($LogLocation.Length -eq 0)
   {
    $LogLocation = $Env:Windir + "\SYSTEM32\LogFiles"
   }
      
  $LogLocation            = Add-TrailingCharacter -AddCharacterTo $LogLocation   -Character "\"
  $MSIPath                = Add-TrailingCharacter -AddCharacterTo $MSIPath       -Character "\"
  $strCurrentDir          = Add-TrailingCharacter -AddCharacterTo $strCurrentDir -Character "\"

  $strTransform           = ""
  $strDefaultPropFile     = $MSIPath + "defaultproperties.csv"

  $strDefaultProperties   = ""
  $strActivity            = ""
  $strPatch               = ""
  $strSingleOrMultipleMSI = "MultipleMSI"

  $arrDefaultProperties   = @()
  $arrMSIFiles            = @()
  $arrMSPFiles            = @()
  $arrMSIFiles            = Get-AllFilesWithPattern -FolderToLookIn $MSIPath -Pattern "*.msi"
  $numMSIFiles            = $arrMSIFiles.Count
  $numCounter             = 1

# ========================================================================================================================
# Stop the script for a non admin user
# ========================================================================================================================

  if(-not(Check-HasAdminRights))
  {
   Write-Error "The current user has no admin rights. Please rerun the script with elevated rights." -Category PermissionDenied
   Exit 999
  }

# ========================================================================================================================
# Create the log file location if not exists
# ========================================================================================================================

  if(-not (Test-Path $LogLocation))
   {
    New-Item -Path $LogLocation -ItemType Directory -Force -Confirm:$False | Out-Null
   }

# ========================================================================================================================
# Define the logfile for the install or uninstall of all the MSIs. 
# ========================================================================================================================
 
  $strLastPartOfFileName = " (" + (Get-Date).ToString('G') + ").log"
  $strLastPartOfFileName = $strLastPartOfFileName.Replace(":","-").Replace("/","-")

  if($numMSIFiles -eq 1)
   {
    $strSingleOrMultipleMSI = "SingleMSI"
   }

  if($Install -or (-not $Uninstall))
   {
    $strLogFile            = $LogLocation + $strSingleOrMultipleMSI + $strLastPartOfFileName
    $strActivity           = "Installing MSIs in the folder $MSIPath"
   }
    else
   {
    $strLogFile            = $LogLocation + "Uninstall" + $strSingleOrMultipleMSI + $strLastPartOfFileName
    $strActivity           = "Uninstalling MSIs in the folder $MSIPath"
   }
 
  CreateLogFile -LogFile $strLogFile

# ========================================================================================================================
# Give an error message if both parameters install and uninstall are used. 
# ========================================================================================================================

if($Install -and $Uninstall)
 {
  $strErrorMessage = "Both install and uninstall parameters are mentioned. That is not possible. Only one of them should be used."
  WriteToLog -line "========================================================================================================================" -LogFile $strLogFile
  WriteToLog -line "FATAL ERROR!" -LogFile $strLogFile
  WriteToLog -line $strErrorMessage -LogFile $strLogFile
  WriteToLog -line "========================================================================================================================" -LogFile $strLogFile
  Write-Error $strErrorMessage -Category InvalidArgument
  Exit 991
 }

# ========================================================================================================================
# Write default settings to the logfile
# ========================================================================================================================

  WriteToLog -line "========================================================================================================================" -LogFile $strLogFile
  WriteToLog -line "Log location: $LogLocation" -LogFile $strLogFile
  WriteToLog -line "MSI Path:     $MSIPath"     -LogFile $strLogFile
  WriteToLog -line $($strActivity + ":")        -LogFile $strLogFile

  ForEach ($objMSIFile in $arrMSIFiles)
   {
    WriteToLog -line " * $($objMSIFile.Name)" -LogFile $strLogFile
   }
  WriteToLog -line "========================================================================================================================" -LogFile $strLogFile

# ========================================================================================================================
# In case of an installation:
# Define the default properties.
# ========================================================================================================================

  if ($Install -or (-not $Uninstall))
   {
    $strDefaultProperties = Import-PropertyFile -PropertyFile $strDefaultPropFile
   }
  
# ========================================================================================================================
# Start the real installation or uninstall.
# The installation is skipped if a MSI has already been installed.
# The uninstall is only done if the product has already been installed.
# ========================================================================================================================

  ForEach ($objMSIFile in $arrMSIFiles)
   {
    Write-Progress -Activity $($strActivity + ".") -Status "Processing $objMSIFile." -PercentComplete ($numCounter / $numMSIFiles * 100)
    $strMSIFileName    = $MSIPath + $objMSIFile
    $strProductName    = Get-MSIFileInformation -Path $strMSIFileName -Property ProductName
    $strProductVersion = Get-MSIFileInformation -Path $strMSIFileName -Property ProductVersion
    $strProductCode    = Get-MSIFileInformation -Path $strMSIFileName -Property ProductCode
    $strRegPathX64     = "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\"+$strProductCode
    $strRegPathX64     = $strRegPathX64 -replace(" ","")
    $strRegPathX86     = $strRegPathX64.Replace("WOW6432Node\","")
    
    if($Install -or (-not $Uninstall))
     {
           
      # ========================================================================================================================
      # The install
      # ========================================================================================================================

      $strTransform      = ""
      $strPatch          = ""
      $strMSTFileName    = Get-LastItemOfAnArryAndPutItInAString -FileName $objMSIFile -OldExtension ".msi" -NewExtension "*.mst" -WhereToLook $MSIPath
      $strMSPFileName    = Get-LastItemOfAnArryAndPutItInAString -FileName $objMSIFile -OldExtension ".msi" -NewExtension "*.msp" -WhereToLook $MSIPath
      $strPropFile       = $strMSIFileName.Replace("msi","csv")

      # ========================================================================================================================
      # Apply a patch (msp file) (if available)
      # ========================================================================================================================

      if($strMSPFileName.Length -ge 1)
       {
        $strPatch = " /update " + $([char]34) + $MSIPath + $strMSPFileName + $([char]34)
        WriteToLog -LogFile $strLogFile -line "The patch file '$strMSPFileName' has been found and is applied."
       }

      # ========================================================================================================================
      # Apply a transform file (mst) (if available)
      # ========================================================================================================================

      if($strMSTFileName.Length -ge 1)
       {
        $strMSTFileName = $MSIPath + $strMSTFileName
        $strTransform   = "TRANSFORMS=" + $([char]34) + $strMSTFileName + $([char]34)+" "
        WriteToLog -LogFile $strLogFile -line "The transform file '$strMSTFileName' has been found and is applied."
       }

      WriteToLog -line "Installing application: $strProductName"    -LogFile $strLogFile
      WriteToLog -line "ProductVersion:         $strProductVersion" -LogFile $strLogFile

      if(-not ((Test-Path $strRegPathX64) -or (Test-Path $strRegPathX86)))
       {
        $strProperties     = " "
        $strProperties     = Import-PropertyFile -PropertyFile $strPropFile
        $strMSILogFile     = "/l*v " + $([char]34) + $LogLocation + $strProductName + " " + $strProductVersion +".log" + $([char]34)
        $strArguments      = "/i "   + $([char]34) + $MSIPath + $objMSIFile + $([char]34) + $strPatch + " /qb! " + $strDefaultProperties + $strProperties + $strTransform + $strMSILogFile

        $strArguments      = $strArguments  -replace("   ","")
      
        WriteToLog -line "Command that is run:       msiexec $($strArguments)" -LogFile $strLogFile
        $StartProcess = (Start-Process -FilePath "msiexec.exe" -ArgumentList $strArguments -Wait -PassThru)
        WriteToLog -line "Result: $($StartProcess.ExitCode)" -LogFile $strLogFile
        WriteToLog -line "========================================================================================================================" -LogFile $strLogFile
       }
        else
       {
        WriteToLog -line "This application has already been installed, thus skipping." -LogFile $strLogFile
        WriteToLog -line "========================================================================================================================" -LogFile $strLogFile
       }
     }
      else
     {   
        
      # ========================================================================================================================
      # The uninstall
      # ========================================================================================================================

      WriteToLog -line "Uninstalling application: $strProductName"    -LogFile $strLogFile
      WriteToLog -line "ProductVersion:           $strProductVersion" -LogFile $strLogFile
      
      if((Test-Path $strRegPathX64) -or (Test-Path $strRegPathX86))
       {
        $strMSILogFile     = "/l*v " + $([char]34) + $LogLocation + "Uninstall_"+ $strProductName + " " + $strProductVersion +".log" + $([char]34)
        $strArguments      = "/x "   + $strProductCode + " /qb! " + $strMSILogFile

        $strArguments      = $strArguments  -replace("   ","")

        WriteToLog -line "Command that is run:         msiexec $($strArguments)" -LogFile $strLogFile
        $StartProcess = (Start-Process -FilePath "msiexec.exe" -ArgumentList $strArguments -Wait -PassThru)
        WriteToLog -line "Result: $($StartProcess.ExitCode)" -LogFile $strLogFile

        WriteToLog -line "========================================================================================================================" -LogFile $strLogFile
       }
        else
       {
        WriteToLog -line "This application has not been installed, thus skipping." -LogFile $strLogFile
        WriteToLog -line " " -LogFile $strLogFile
       }
     }
    $numCounter++
   }

# ========================================================================================================================
# Done!
# ========================================================================================================================

Current version: install_all_msi (v10).zip




Cannot edit the object which is in use as SCCM blocks an application

You might have had this earlier: editing an application in SCCM 2012, the SCCM console crashes. And when you want to continue, you get the error: The following objects are not available and will not be edited.

In this article, I will describe 2 solutions.

Via PowerShell

  1. The easiest way is via PowerShell. In the SCCM Console start PowerShell:

  2. Give the following command:
    Unlock-CMObject -InputObject $(Get-CMApplication -Name Microsoft_CPlusPlusRuntimes_1.0.1_ENG_WIN7_P1)
    


    There is no feedback, but you can edit the application again.

Via SQL Server Management Studio

An alternative way is via SQL Server Management Studio.

  1. Start SQL Server Management Studio
  2. Create a new query. And connect to the correct database.
  3. Give the following command:
    select * from SEDO_LockState where LockStateID<>0
    

  4. There might be multiple lines, as multiple users may have various applications locked. Find the correct one. Copy the LockstateID.
  5. Give the following command:
    DELETE from SEDO_LockState where LockID = 'Copied LockstateID'
    

  6. And you can edit your application again.

 




Installing OneDrive NextGen Synchronisation Client

The OneDrive for Business client can give some difficulties, like unable to synchronize:

Unable to sync files with OneDrive for Business.

Unable to sync files with OneDrive for Business.

This can be solved by installing the new OneDrive Next Gen Sync Client. In this article I will describe how this application can be installed via SCCM 2012.

Please be informed that you still need the OneDrive for Business client to synchronize with Sharepoint lists.

Background information

The OneDrive NextGen Sync Client is installed the users’ %LOCALAPPDATA% directory. As the application is installed the %LOCALAPPDATA% directory, you can assume that you can install the application with user rights in SCCM 2012. If you do so, the installation will fail:

Failure as admin rights are needed to install.

Failure as admin rights are needed to install.


As you can see, there are ‘hidden’ admin rights needed.

The solution

  • Created a vscript that installs the application and sets a detection rule after a successful installation. The detection rule can be used in SCCM 2012 to detect a successful installation.
    ' ================================================================================================
    ' Installs Microsoft OneDriveNextGenSyncClient 17.3.6386.0412
    ' Created by Willem-Jan Vroom
    ' Version history:
    '
    ' 0.0.1
    '    Initial version
    '
    ' 1.0.0
    '    Final version
    '
    ' ================================================================================================
    
    ' ------------------------------------------------------------------------------------------------
    ' Declare the most variables.
    ' ------------------------------------------------------------------------------------------------
    
      Option Explicit
    
      Dim objShell                          : set objShell                      = WScript.CreateObject("WScript.Shell")
      Dim objLogFileFSO                     : Set objLogFileFSO                 = CreateObject("Scripting.FileSystemObject")
      Dim objFSO                            : Set objFSO                        = CreateObject("Scripting.FileSystemObject")
      Dim objProcessEnv                     : Set objProcessEnv                 = objShell.Environment("PROCESS")
      Dim objWMIService                     : Set objWMIService                 = GetObject("winmgmts:\\.\root\cimv2")
      Dim fn_objWMIService                  : Set fn_objWMIService              = GetObject("winmgmts:\\.\root\cimv2")
      Dim objReg                            : Set objReg                        = GetObject("winmgmts:\\.\root\default:StdRegProv")
      Dim objLogFile
      Dim CurrentDir                        : CurrentDir                        = Left(Wscript.ScriptFullname, InstrRev(Wscript.ScriptFullname, "\"))
                                              CurrentDir                        = Left(CurrentDir,len(CurrentDir)-1)
      Dim strcomputerName                   : strcomputerName                   = objProcessEnv("COMPUTERNAME")
      Dim strLogLocation                    : strLogLocation                    = "C:\WINDOWS\system32\Logfiles"
      Dim strOutputFile                     : strOutputFile                     = strLogLocation & "\" & strcomputerName & "_Installation_OneDriveSyncClient_" & Replace(FormatDateTime(Now(), 2),"/","-") & ".log"
      Dim strArchitecture                   : strArchitecture                   = "x86"
      Dim strCurrentUser                    : strCurrentUser                    = ""
      Dim strCurrentUserSID                 : strCurrentUserSID                 = ""
      Dim strOS                             : strOS                             = ""
      Dim strCommand                        : strCommand                        = ""
      Dim strLine                           : strLine                           = ""
      Dim strQuery                          : strQuery                          = ""
      Dim strCommonDesktop                  : strCommonDesktop                  = ""
      Dim strArray                          : strArray                          = ""
      Dim strProcess                        : strProcess                        = ""
      Dim strLanguage                       : strLanguage                       = ""
      Dim strInstallLanguage                : strInstallLanguage                = ""
      Dim strDetectUserUILanguage           : strDetectUserUILanguage           = ""
      Dim strValue                          : strValue                          = ""
      Dim strKeyPath                        : strKeyPath                        = ""
      Dim strValueName                      : strValueName                      = ""
      Dim strCommonPrograms                 : strCommonPrograms                 = ""
      Dim strFileToDelete                   : strFileToDelete                   = ""
      dim strProgramFilesFolder             : strProgramFilesFolder             = objProcessEnv("ProgramFiles")
      Dim strDotNetInstallPath              : strDotNetInstallPath              = ""
      Dim strWindir                         : strWinDir                         = objShell.ExpandEnvironmentStrings("%WinDir%")
      Dim dwValue                           : dwValue                           = 0
    
      Dim arrFileNamesToKill                : arrFileNamesToKill                = Array("doesnotexist.exe")
      Dim objFileNameToKill
    
      Dim arrArguments
      Dim arrLanguage
      Dim strDNSDomain                      : strDNSDomain                      = CreateObject("ADSystemInfo").DomainShortName
      Dim valCounter
      Dim valResult
      Dim valReturnCode
    
      Dim colProcess, objProcess
      Dim valOSBuildNumber
      Dim objSubfolder
      Dim colItems, objItem, Subfolder
      Dim colSoftware, objSoftware
      Dim arrValues, arrSubKeys
    
      Const ForWriting           = 2
      Const ForReading           = 1
      Const ForAppending         = 8
      Const OverWriteFiles       = True
      const HKEY_CURRENT_USER    = &H80000001
      const HKEY_LOCAL_MACHINE   = &H80000002
      Const HKEY_USERS           = &H80000003
    
      Const DEBUGMODE            = False
    
    ' ------------------------------------------------------------------------------------------------
    ' Create the log location (if not exists)
    ' Open the logfile.
    ' ------------------------------------------------------------------------------------------------
    
      CreateFolderStructure(strLogLocation)
      OpenLogFile()
      WriteToLog "- ACTION: script started."
      WriteToLog " "
    
    ' ------------------------------------------------------------------------------------------------
    ' Detect the current OS.
    ' ------------------------------------------------------------------------------------------------
    
      Set colItems = objWMIService.ExecQuery("Select Caption,BuildNumber from Win32_OperatingSystem")
      For Each objItem in colItems
          strOS            = objItem.Caption
          valOSBuildNumber = objItem.BuildNumber
      next
    
    ' ------------------------------------------------------------------------------------------------
    ' Find the currently logged on userid.
    ' And translate it to the users SID.
    ' ------------------------------------------------------------------------------------------------
    
      Set colProcess = objWMIService.ExecQuery("Select * from Win32_Process where Name = 'explorer.exe'")
      For Each objProcess in colProcess
          valReturnCode = objProcess.GetOwner(strCurrentUser)
          if valReturnCode <> 0 Then strCurrentUser = "Unable to find the username!"
      Next
    
      strCurrentUser = Ucase(strDNSDomain) & "\" & strCurrentUser
    
      strCurrentUserSID = GetSIDFromUser(strCurrentUser)
    
    ' ------------------------------------------------------------------------------------------------
    ' Detect the current processor architecture.
    ' ------------------------------------------------------------------------------------------------
    
      if objFSO.FolderExists(strWindir & "\syswow64") Then
         strArchitecture = "x64"
         strProgramFilesFolder = objProcessEnv("ProgramFiles(x86)")
      end if
    
    ' ------------------------------------------------------------------------------------------------
    ' Detect the current User Interface MUI.
    ' ------------------------------------------------------------------------------------------------
    
      strDetectUserUILanguage = DetectUserUILanguage
    
      WriteToLog("########## Details regarding operating system and logged on user     ##########")
      WriteToLog("Found Operating System:       " & strOS)
      WriteToLog("Found architecture:           " & strArchitecture)
      WriteToLog("Logged on userid:             " & strCurrentUser)
      WriteToLog("Logged on user SID:           " & strCurrentUserSID)
      WriteToLog("Language:                     " & strDetectUserUILanguage)
      WriteToLog("########## End details regarding operating system and logged on user ##########")
      WriteToLog(" ")
      WriteToLog("########## Installation                                              ##########")
    
      ' ------------------------------------------------------------------------------------------------
      ' Close applications that might screw up the installation.
      ' ------------------------------------------------------------------------------------------------
    
      For Each objFileNameToKill in arrFileNamesToKill
        fnKillProcess(objFileNameToKill)
      Next
    
    ' ------------------------------------------------------------------------------------------------
    ' Sets SEE_MASK_NOZONECHECKS to 1.
    ' That will avoid a screen that asks for permission to run an application from
    ' a share or mapped drive.
    ' ------------------------------------------------------------------------------------------------
    
      objProcessEnv("SEE_MASK_NOZONECHECKS") = 1
    
    ' ------------------------------------------------------------------------------------------------
    ' Uninstall other Samsung Kies installations.
    ' if the command line option '/uninstall' is given, then only a uninstall and quit.
    ' ------------------------------------------------------------------------------------------------
    
      if wscript.arguments.count > 0 Then
         if lcase(wscript.arguments(0)) = "/uninstall" or lcase(wscript.arguments(0)) = "/remove" then
    	WriteToLog("The command line option '" & wscript.arguments(0) & "' was found, so only a silent uninstall will be performed.")
            RemoveOneDriveSyncClient()
            WriteToLog("########## End Uninstall                                      ##########")
            WriteToLog(" ")
            CloseLogFile()
            wscript.quit
         end if
      end if
    
    ' ------------------------------------------------------------------------------------------------
    ' Start the base installation
    ' ------------------------------------------------------------------------------------------------
    
      strCommand = chr(34) & CurrentDir & "\OneDriveSetup.exe" & chr(34)
      strCommand = strCommand & " /silent"
      WriteToLog ("Running command: " & strCommand)
      valResult = objShell.Run (strCommand,6,True)
      WriteToLog("Result: " & valResult)
      if valResult = 0 Then 
         GenereteSCCM2012DetectionRule
      end if
      WriteToLog("########## End Installation                                          ##########")
      WriteToLog(" ")
      CloseLogFile()
      wscript.quit valReturnCode
    
    Sub OpenLogFile()
    
    ' ------------------------------------------------------------------------------------------------
    ' Subroutine: OpenLogFile()
    ' The name of the logfile is mentinoed in the variabele strOutputFile.
    ' ------------------------------------------------------------------------------------------------
    
      If objLogFileFSO.FileExists(strOutputFile) Then
         Set objLogFile = objLogFileFSO.OpenTextFile(strOutputFile, ForAppending)
             Else
         Set objLogFile = objLogFileFSO.CreateTextFile(strOutputFile)
      End If
    
    End Sub
    
    Sub CloseLogFile()
    
    ' ------------------------------------------------------------------------------------------------
    ' Subroutine: CloseLogFile()
    ' Close the log file.
    ' ------------------------------------------------------------------------------------------------
    
      WriteToLog "- ACTION: script ended."
      objLogFile.Close
      Set objLogfileFSO = Nothing
    
    End Sub
    
    Function WriteToLog(sLogMessage)
    
    ' ------------------------------------------------------------------------------------------------
    ' Function: WriteToLog(sLogMessage)
    ' Writes an entry 'sLogMessage' in the logfile.
    ' ------------------------------------------------------------------------------------------------
    
      if instr(sLogMessage, "- ACTION: ") = 0 then sLogMessage = "          " & sLogMessage
      objLogFile.WriteLine("Time: " & now & "  " & sLogMessage)
    
    End Function
    
    Function GetSIDFromUser(UserName)
    
    ' ------------------------------------------------------------------------------------------------
    ' Function: GetSIDFromUser(UserName)
    ' Gets the SID from the give username.
    ' We use the registry to avoid an empty result if run under the SYSTEM account.
    ' ------------------------------------------------------------------------------------------------
    
      Dim DomainName, Result, WMIUser
    
      If InStr(UserName, "\") > 0 Then
        DomainName = Mid(UserName, 1, InStr(UserName, "\") - 1)
        UserName = Mid(UserName, InStr(UserName, "\") + 1)
      Else
        DomainName = CreateObject("WScript.Network").UserDomain
      End If
    
      On Error Resume Next
      Set WMIUser = GetObject("winmgmts:{impersonationlevel=impersonate}!" _
        & "/root/cimv2:Win32_UserAccount.Domain='" & DomainName & "'" _
        & ",Name='" & UserName & "'")
      If Err.Number = 0 Then
        Result = WMIUser.SID
      Else
        Result = ""
      End If
      On Error GoTo 0
    
      GetSIDFromUser = Result
    
    End Function
    
    Sub CreateFolderStructure(strFolderNameToBeCreated)
    
    ' ------------------------------------------------------------------------------------------------
    ' Subroutine: CreateFolderStructure(strFolderNameToBeCreated)
    ' Creates the map as mentioned in strFolderNameToBeCreated.
    ' ------------------------------------------------------------------------------------------------
    
      Dim arrFoldersTMP : arrFoldersTMP = split (strFolderNameToBeCreated,"\")
      Dim strFolder  : strFolder  = ""
      Dim objFolderTMP
    
      For Each objFolderTMP in arrFoldersTMP
          strFolder = strFolder & objFolderTMP
          If NOT objFSO.FolderExists(strFolder) Then
                 objFSO.CreateFolder(strFolder)
          end If
          strFolder = strFolder & "\"
      Next
    
    End Sub
    
    Function DetectUserUILanguage
    
    ' ------------------------------------------------------------------------------------------------
    ' Function: DetectUserUILanguage
    ' Detects the User Interface Language 
    ' Modified by Willem-Jan Vroom d.d 12 Feb 14: also detects Czech, Finnish, Hungarian, Polish,
    ' Russian, Slovak, Slovian and Turkish languages on Windows 7.
    ' ------------------------------------------------------------------------------------------------
      
      if instr(lcase(strOS),"windows xp") > 0 then
         strKeyPath               = strCurrentUserSID & "\Control Panel\Desktop"
         strValueName             = "MultiUILanguageId"
         strLanguage              = "eng"
         objReg.GetStringvalue HKEY_USERS,strKeyPath,strValueName,strValue
         if NOT IsEmpty(strValue) Then
            strValue = UCase(right(strValue,4))
            if strValue = "040C" then strLanguage = "fra"
            if strValue = "0410" then strLanguage = "ita"
            if strValue = "0407" then strLanguage = "ger"
            if strValue = "040A" then strLanguage = "spa"
            if strValue = "0C0A" then strLanguage = "spa"
         end if
          else
         strKeyPath               = strCurrentUserSID & "\Control Panel\Desktop"
         strValueName             = "PreferredUILanguages"
         strLanguage              = "eng"
         objReg.GetMultiStringValue HKEY_USERS,strKeyPath,strValueName,arrValues
         if not IsNull(arrValues) Then
            strValue=arrValues(0)
            if NOT IsEmpty(strValue) then
               strValue = lcase(left(strValue,2))
               if strValue = "fr" then strLanguage = "fra"
               if strValue = "it" then strLanguage = "ita"
               if strValue = "de" then strLanguage = "ger"
               if strValue = "es" then strLanguage = "spa"
               if strValue = "cs" then strLanguage = "cze"
               if strValue = "fi" then strLanguage = "fin"
               if strValue = "hu" then strLanguage = "hun"
               if strValue = "pl" then strLanguage = "pol"
               if strValue = "ru" then strLanguage = "rus"
               if strValue = "sk" then strLanguage = "sky"
               if strValue = "sl" then strLanguage = "slv"
               if strValue = "sv" then strLanguage = "swe"
               if strValue = "tr" then strLanguage = "tur"
               if strValue = "nb" then strLanguage = "nor"
            end if
         end if
         if strLanguage = "eng" then
            strKeyPath               = ".DEFAULT\Control Panel\Desktop\MuiCached"
            strValueName             = "MachinePreferredUILanguages"
            objReg.GetMultiStringValue HKEY_USERS,strKeyPath,strValueName,arrValues
            if not IsNull(arrValues) Then
               strValue=arrValues(0)
               if NOT IsEmpty(strValue) then
                  strValue = lcase(left(strValue,2))
                  if strValue = "fr" then strLanguage = "fra"
                  if strValue = "it" then strLanguage = "ita"
                  if strValue = "de" then strLanguage = "ger"
                  if strValue = "es" then strLanguage = "spa"
                  if strValue = "cs" then strLanguage = "cze"
                  if strValue = "fi" then strLanguage = "fin"
                  if strValue = "hu" then strLanguage = "hun"
                  if strValue = "pl" then strLanguage = "pol"
                  if strValue = "ru" then strLanguage = "rus"
                  if strValue = "sk" then strLanguage = "sky"
                  if strValue = "sl" then strLanguage = "slv"
                  if strValue = "sv" then strLanguage = "swe"
                  if strValue = "tr" then strLanguage = "tur"
                  if strValue = "nb" then strLanguage = "nor"
               end if
            end if
         end if     
      end if
    
      DetectUserUILanguage = strLanguage
    End Function
    
    Function fnKillProcess(strProcessName)
    
    ' ------------------------------------------------------------------------------------------------
    ' Function: fnKillProcess(strProcessName)
    ' Terminates the given processname.
    ' ------------------------------------------------------------------------------------------------
    
      Set colProcess = fn_objWMIService.ExecQuery ("Select * From Win32_Process")
      For Each objProcess In colProcess
        If Instr(LCase(objProcess.Name),LCase(strProcessName)) > 0 Then
           objShell.Run "TASKKILL /F /T /IM " & objProcess.Name, 0, False
           objProcess.Terminate()
           WriteToLog("Terminating application: " & objProcess.Name)
        End If
      Next
    
    End Function
    
    Function GenereteSCCM2012DetectionRule
    
    ' ------------------------------------------------------------------------------------------------
    ' Function: GenereteSCCM2012DetectionRule
    ' Creates the SCCM 2012 Detection Rule to detect a successfull installation.
    ' ------------------------------------------------------------------------------------------------
    
      WriteToLog ("Creates the SCCM 2012 Detection Rule.")
      strKeyPath   = "SOFTWARE\VroomSoft\SCCM2012DetectionRules"
      strValueName = "Install_OneDriveSyncClient_17.3.6386.0412"
      strValue     = "true"
      objReg.CreateKey      HKEY_LOCAL_MACHINE, strKeyPath  
      objReg.SetStringValue HKEY_LOCAL_MACHINE, strKeyPath, strValueName, strValue
      
    End Function
    
    Function RemoveOneDriveSyncClient()
    
    ' ------------------------------------------------------------------------------------------------
    ' Function: RemoveOneDriveSyncClient()
    ' Removes OneDrive Next Gen Sync Client.
    ' ------------------------------------------------------------------------------------------------
    
      strCommand = chr(34) & CurrentDir & "\OneDriveSetup.exe" & chr(34)
      strCommand = strCommand & " /uninstall"
      WriteToLog ("Running command: " & strCommand)
      valResult = objShell.Run (strCommand,6,True)
      WriteToLog("Result: " & valResult)
      if valResult = 0 Then
         strKeyPath   = "SOFTWARE\VroomSoft\SCCM2012DetectionRules"
         strValueName = "Install_OneDriveSyncClient_17.3.6386.0412"
         objReg.DeleteValue HKEY_LOCAL_MACHINE, strKeyPath, strValueName
      end if 
      
    End Function
    
  • This is the detection rule that will be used in SCCM 2012:
    Detection rule set in the vbscript.

    Detection rule set in the vbscript.

  • Implementation in SCCM 2012

    1. Create a ‘installation program’ and ‘uninstall program’:
      New application.

      New application.

    2. The detection rule:
      Detection rule.

      Detection rule.

    3. The detection method:
      Detection method.

      Detection method.

    4. The application is installed with admin rights:
      Installation with admin rights.

      Installation with admin rights.

    5. And deploy the application to a deviced based collection:
      Deployment to a device based collection.

      Deployment to a device based collection.


      Deploy as available.

      Deploy as available.


      Show all notifications.

      Show all notifications.

    Check on a client

    1. Log on to a client and go to the Software Catalog:
      The application is visible in the software center

      The application is visible in the software center


      You can find the new application under installed applications.

      You can find the new application under installed applications.

    2. You can use regedit to find the detection rule which is set via vbscript:
      The detection rule is found in the registry

      The detection rule is found in the registry

    3. The installation log file (found in c:\windows\system32\logfiles):
      Installation log file

      Installation log file

    4. Now you can select the folders you want to synchronize or want to exclude from synchronization:
      You can select the folders you want so synchronize.

      You can select the folders you want so synchronize.


      Unselect the folders you do not want to synchronize.

      Unselect the folders you do not want to synchronize.

    Uninstall the application

    You can uninstall the OneDrive NextGen Sync Client if you do not want to use it anymore. All the data will remain on the computer:

    You can uninstall the client.

    You can uninstall the client.


    The uninstall in the log file.

    The uninstall in the log file.




Upgrading and deploying AppV 5.0 client via SCCM 2012

If you want to deploy AppV 5.0 packages, a client is needed. In this article I will describe all the steps that are needed to install the latest AppV 5.0 client. When writing this article it is AppV 5.0 SP3 with hotfix 2.

This version is not available as a ‘complete’ version. First you will have to install AppV 5.0 SP3 client, followed by the hotfix. Fortunately you can ‘merge’ this 2 versions in one installer.

The technique

If you extract the AppV 5.0 SP3 installer, you will find 2 MSI files: one for 86 bits and one for 64 bits operating systems. These versions will be patched with the hotfix.

Step 1:: download the software

Download the AppV 5.0 SP3 (MDOP 2014 R2)

You can download MDOP 2014 R2 – which includes AppV 5.0 SP3 – from the Microsoft Volume Licensing Service Center.

Download AppV 5.0 SP3 hotfix 2

You can download Hotfix Package 2 for Microsoft Application Virtualization 5.0 Service Pack 3.
Once completed all the required steps, save the file 484280_intl_x64_zip.exe in the folder C:\AppV50SP3.

Step 2: extract the software

Extract the main installer

  1. Create the folder C:\AppV50SP3
  2. Copy appv_client_setup.exe to C:\AppV50SP3
  3. Extract the content via the command:
    C:\AppV50SP3\appv_client_setup.exe /layout C:\AppV50SP3
    
  4. There are 2 additionals MSI files in C:\AppV50SP3: for each platform one MSI installer.

Extract the hotfix 2

  1. Dubbleclick on C:\AppV50SP3\484280_intl_x64_zip.exe and extract to C:\AppV50SP3

    Extract the hotfix.

    Extract the hotfix.

  2. Extract the patches with the command:
    C:\AppV50SP3\AppV5.0SP3_Client_KB3060458.exe /layout C:\AppV50SP3
    

Create the patched MSI file for both x86 and x64 operating systems

This is done via an old technique: create an administrative installation point and patch that administrative installation point with the patch.

  1. Create an administrative installation point for the x86 bits client:
    msiexec /a "C:\AppV50SP3\appv_client_MSI_x86.msi" TARGETDIR=c:\AppV50SP3HF2_aip_x86
    

    The MSI (x86) after administrative install

    The MSI (x86) after administrative install.

  2. Create an administrative installation point for the x64 bits client:
    msiexec /a "C:\AppV50SP3\appv_client_MSI_x64.msi" TARGETDIR=c:\AppV50SP3HF2_aip_x64
    

    The MSI (x64) after administrative install.

    The MSI (x64) after administrative install.

  3. Patch the msi in the administrative installation point with the patch for the x86 bits client:
    msiexec /a "C:\AppV50SP3HF2_aip_x86\appv_client_MSI_x86.msi" /p "C:\AppV50SP3\appv_client_kb3060458_x86.msp"
    

    The MSI (x86) after patching the administrative install.

    The MSI (x86) after patching the administrative install.

  4. Patch the msi in the administrative installation point with the patch for the x64 bits client:
    msiexec /a "C:\AppV50SP3HF2_aip_x64\appv_client_MSI_x64.msi" /p "C:\AppV50SP3\appv_client_kb3060458_x64.msp"
    

    The MSI (x64) after patching the administrative install.

    The MSI (x64) after patching the administrative install.

  5. An overview of the commands:

    Summary of all the commands.

    Summary of all the commands.

Step 3: implement in SCCM 2012

Place the installation files on the software library

  1. Copy the content from C:\AppV50SP3HF2_aip_x86 to the share with all the packages: \\demo-sccm\SCCMPackages\All Applications\Microsoft_AppVClient_5.0SP3HF2_ENG_W7_1.0.0\x86

    Copy the installation files (x86) to the software library.

    Copy the installation files (x86) to the software library.

  2. Copy the content from C:\AppV50SP3HF2_aip_x64 to the share with all the packages: \\demo-sccm\SCCMPackages\All Applications\Microsoft_AppVClient_5.0SP3HF2_ENG_W7_1.0.0\x64

    Copy the installation files (x64) to the software library.

    Copy the installation files (x64) to the software library.

Create the software package

Create the SCCM 2012 applications as per screen prints below. The installation command line should at least contain ACCEPTEULA=1. I always add ENABLEPACKAGESCRIPTS=1 so you can add virtualized Click 2 Run packages later.Then there is no need to set this via GPO later.

Upg After creating the application and deployment types.

Upg After creating the application and deployment types.

Installation program (x86).

Installation program (x86).

Detection rule (x86)

Detection rule (x86)

Requirements (x86).

Requirements (x86).

Dependencies (x86).

Dependencies (x86).

Installation program (x64).

Installation program (x64).

Detection rule (x64).

Detection rule (x64).

Requirements (x64).

Requirements (x64).

Dependencies (x64).

Dependencies (x64).

Deployment of the application.

Deployment of the application.

The deployment collection.

The deployment collection.

The deployment is set as required.

The deployment is set as required.

Ignore the maintenance window for installations.

Ignore the maintenance window for installations.

Confirmation.

Confirmation.

Deploy the software and check on the client

Force the computers in the collection to download the computer policies.

Force the computers in the collection to download the computer policies.

The policy is applied on the client.

The policy is applied on the client.

And the new AppV client is installed.

And the new AppV client is installed.

The new client version.

The new client version.

The deep dive: more info about the upgrade

For whatever reason, Microsoft has decided not to change the ProductCode property. Thus all the AppV 5.0 SP3 packages share the same ProductCode.

If you open C:\AppV50SP3\appv_client_MSI_x86.msi and apply C:\AppV50SP3\appv_client_kb3060458_x86.msp you will see that only the ProductVersion is updated:

The patch does only update the ProductVersion (x86).

The patch does only update the ProductVersion (x86).

Although:

The PATCHNEWPACKAGECODE property is ignored (x86).

The PATCHNEWPACKAGECODE property is ignored (x86).

So if you want to update the AppV50SP3 client to AppV50SP3 with HF2 then you can do 2 thinks:

  • Uninstall the old AppV50SP3 client and install the new AppV50SP3HF2 client.
  • Apply only the patch. I have not tested this scenario.



Load all AppV 5.0 packages into the AppV Cache

After the migration from SCCM 2007 to SCCM 2012 some users had an issue with accessing their AppV 5.0 packages: the virtual package could not be found in the ccmcache. That was true, as the package had not been used before the SCCM 2007 to SCCM 2012 migration and after the migration the ccmcache was deleted. So the only solution was to mount all AppV 5.0 packages. That will download the virtual package from the ccmcache into the AppV 5.0 cache.

To do this, I created a vbscript that is run just before the SCCM 2007 to SCCM 2012 migration task sequence.

' ================================================================================================
' Mount all AppV packages
' Created by Willem-Jan Vroom
' Version history:
'
' 0.0.1
'    Initial version
'
' 
' ================================================================================================

' ------------------------------------------------------------------------------------------------
' Declare the most variables. 
' ------------------------------------------------------------------------------------------------

  Option Explicit

  Dim objShell                          : set objShell                      = WScript.CreateObject("WScript.Shell")
  Dim objLogFileFSO                     : Set objLogFileFSO                 = CreateObject("Scripting.FileSystemObject")
  Dim objFSO                            : Set objFSO                        = CreateObject("Scripting.FileSystemObject")
  Dim objProcessEnv                     : Set objProcessEnv                 = objShell.Environment("PROCESS")
  Dim objWMIService                     : Set objWMIService                 = GetObject("winmgmts:\\.\root\cimv2")
  Dim fn_objWMIService                  : Set fn_objWMIService              = GetObject("winmgmts:\\.\root\cimv2")
  Dim objReg                            : Set objReg                        = GetObject("winmgmts:\\.\root\default:StdRegProv")
  Dim objLogFile
  Dim CurrentDir                        : CurrentDir                        = Left(Wscript.ScriptFullname, InstrRev(Wscript.ScriptFullname, "\"))
                                          CurrentDir                        = Left(CurrentDir,len(CurrentDir)-1)
  Dim strcomputerName                   : strcomputerName                   = objProcessEnv("COMPUTERNAME")
  Dim strLogLocation                    : strLogLocation                    = "C:\WINDOWS\system32\Logfiles"
  Dim strOutputFile                     : strOutputFile                     = strLogLocation & "\" & strcomputerName & "_Mount_All_AppV_Packages_" & Replace(FormatDateTime(Now(), 2),"/","-") & ".log"
  Dim strArchitecture                   : strArchitecture                   = ""
  Dim strCurrentUser                    : strCurrentUser                    = ""
  Dim strCurrentUserSID                 : strCurrentUserSID                 = "" 
  Dim strOS                             : strOS                             = ""
  Dim strCommand                        : strCommand                        = ""
  Dim strLine                           : strLine                           = ""
  Dim strQuery                          : strQuery                          = ""
  Dim strCommonDesktop                  : strCommonDesktop                  = ""
  Dim strArray                          : strArray                          = ""  
  Dim strAppDataFolder                  : strAppDataFolder                  = ""
  Dim strProcess                        : strProcess                        = ""
  Dim strLanguage                       : strLanguage                       = ""
  Dim strInstallLanguage                : strInstallLanguage                = ""
  Dim strDetectUserUILanguage           : strDetectUserUILanguage           = ""
  Dim strValue                          : strValue                          = ""
  Dim strKeyPath                        : strKeyPath                        = ""
  Dim strValueName                      : strValueName                      = ""
  Dim dwValue                           : dwValue                           = 0

  Dim arrFileNamesToKill                : arrFileNamesToKill                = Array("doesnotexists.exe")
  Dim objFileNameToKill

  Dim arrArguments
  Dim arrLanguage
  Dim strDNSDomain                      : strDNSDomain                      = CreateObject("ADSystemInfo").DomainShortName
  Dim valCounter
  Dim valResult 
  Dim valReturnCode

  Dim colProcess, objProcess 
  Dim valOSBuildNumber
  Dim objSubfolder
  Dim colItems, objItem, Subfolder
  Dim colSoftware, objSoftware
  Dim arrValues


  Const ForWriting           = 2
  Const ForReading           = 1
  Const ForAppending         = 8
  Const OverWriteFiles       = True
  const HKEY_CURRENT_USER    = &H80000001
  const HKEY_LOCAL_MACHINE   = &H80000002
  Const HKEY_USERS           = &H80000003 

' ------------------------------------------------------------------------------------------------
' Create the log location (if not exists)
' Open the logfile.  
' ------------------------------------------------------------------------------------------------

  CreateFolderStructure(strLogLocation)
  OpenLogFile()
  WriteToLog "- ACTION: script started."
  WriteToLog " "

' ------------------------------------------------------------------------------------------------
' Detect the current OS.  
' ------------------------------------------------------------------------------------------------

  Set colItems = objWMIService.ExecQuery("Select Caption,BuildNumber from Win32_OperatingSystem")
  For Each objItem in colItems
      strOS            = objItem.Caption
      valOSBuildNumber = objItem.BuildNumber
  next

' ------------------------------------------------------------------------------------------------
' Find the currently logged on userid.
' And translate it to the users SID.
' ------------------------------------------------------------------------------------------------

  Set colProcess = objWMIService.ExecQuery("Select * from Win32_Process where Name = 'explorer.exe'")
  For Each objProcess in colProcess
      valReturnCode = objProcess.GetOwner(strCurrentUser)
      if valReturnCode <> 0 Then strCurrentUser = "Unable to find the username!"
  Next

  strCurrentUser = Ucase(strDNSDomain) & "\" & strCurrentUser

  strCurrentUserSID = GetSIDFromUser(strCurrentUser)

' ------------------------------------------------------------------------------------------------
' Detect the current processor architecture.  
' ------------------------------------------------------------------------------------------------

  if objFSO.FolderExists(objShell.ExpandEnvironmentStrings("%windir%") & "\SysWOW64\Config") then  
     strArchitecture = "x64"
       else
     strArchitecture = "x86"
  end if

' ------------------------------------------------------------------------------------------------
' Detect the current User Interface MUI.  
' ------------------------------------------------------------------------------------------------

  strDetectUserUILanguage = DetectUserUILanguage

  strInstallLanguage = DetectUserUILanguage


  WriteToLog("########## Details regarding operating system and logged on user     ##########")
  WriteToLog("Found Operating System:       " & strOS)
  WriteToLog("Found architecture:           " & strArchitecture)
  WriteToLog("Logged on userid:             " & strCurrentUser)
  WriteToLog("Logged on user SID:           " & strCurrentUserSID)
  WriteToLog("User MUI Language:            " & DetectUserUILanguage)
  WriteToLog("########## End details regarding operating system and logged on user ##########")
  WriteToLog(" ")

  WriteToLog("########## Mount all AppV 5.0 packages into the AppV Cache and       ##########")

' ------------------------------------------------------------------------------------------------
' Start the installation
' ------------------------------------------------------------------------------------------------ 

  EnablePackageScriptsViaRegistry

  strCommand = "powershell -ExecutionPolicy Bypass import-module 'C:\Program Files\Microsoft Application Virtualization\Client\AppvClient\AppvClient.psd1'; Mount-AppvClientPackage -Name * -verbose"
  WriteToLog("Running command: " & strCommand)
  valReturnCode = objShell.Run(strCommand,6,True)
  WriteToLog("Result: " & valReturnCode)

  WriteToLog("########## End Mounting all AppV 5.0 packages.                       ##########")
  WriteToLog(" ")
  CloseLogFile()
  wscript.quit 0


Sub OpenLogFile() 

' ------------------------------------------------------------------------------------------------
' Subroutine: OpenLogFile() 
' The name of the logfile is mentinoed in the variabele strOutputFile.
' ------------------------------------------------------------------------------------------------

  If objLogFileFSO.FileExists(strOutputFile) Then
     Set objLogFile = objLogFileFSO.OpenTextFile(strOutputFile, ForWriting)
         Else
     Set objLogFile = objLogFileFSO.CreateTextFile(strOutputFile)
  End If

End Sub

Sub CloseLogFile()

' ------------------------------------------------------------------------------------------------
' Subroutine: CloseLogFile()
' Close the log file.
' ------------------------------------------------------------------------------------------------

  WriteToLog "- ACTION: script ended."
  objLogFile.Close
  Set objLogfileFSO = Nothing

End Sub

Function WriteToLog(sLogMessage)

' ------------------------------------------------------------------------------------------------
' Function: WriteToLog(sLogMessage)
' Writes an entry 'sLogMessage' in the logfile.
' ------------------------------------------------------------------------------------------------

  if instr(sLogMessage, "- ACTION: ") = 0 then sLogMessage = "          " & sLogMessage
  objLogFile.WriteLine("Time: " & now & "  " & sLogMessage)

End Function

Function GetSIDFromUser(UserName)

' ------------------------------------------------------------------------------------------------
' Function: GetSIDFromUser(UserName)
' Gets the SID from the give username.
' We use the registry to avoid an empty result if run under the SYSTEM account.
' ------------------------------------------------------------------------------------------------

  Dim DomainName, Result, WMIUser

  If InStr(UserName, "\") > 0 Then
    DomainName = Mid(UserName, 1, InStr(UserName, "\") - 1)
    UserName = Mid(UserName, InStr(UserName, "\") + 1)
  Else
    DomainName = CreateObject("WScript.Network").UserDomain
  End If

  On Error Resume Next
  Set WMIUser = GetObject("winmgmts:{impersonationlevel=impersonate}!" _
    & "/root/cimv2:Win32_UserAccount.Domain='" & DomainName & "'" _
    & ",Name='" & UserName & "'")
  If Err.Number = 0 Then
    Result = WMIUser.SID
  Else
    Result = ""
  End If
  On Error GoTo 0

  GetSIDFromUser = Result

End Function

Sub CreateFolderStructure(strFolderNameToBeCreated)

' ------------------------------------------------------------------------------------------------
' Subroutine: CreateFolderStructure(strFolderNameToBeCreated)
' Creates the map as mentioned in strFolderNameToBeCreated.
' ------------------------------------------------------------------------------------------------

  Dim arrFoldersTMP : arrFoldersTMP = split (strFolderNameToBeCreated,"\")
  Dim strFolder  : strFolder  = ""
  Dim objFolderTMP
 
  For Each objFolderTMP in arrFoldersTMP
      strFolder = strFolder & objFolderTMP
      If NOT objFSO.FolderExists(strFolder) Then
             objFSO.CreateFolder(strFolder)
      end If
      strFolder = strFolder & "\"
  Next
 
End Sub

Function DetectUserUILanguage

' ------------------------------------------------------------------------------------------------
' Function: DetectUserUILanguage
' Detects the User Interface Language 
' Modified by Willem-Jan Vroom d.d 12 Feb 14: also detects Czech, Finnish, Hungarian, Polish,
' Russian, Slovak, Slovian and Turkish languages on Windows 7.
' ------------------------------------------------------------------------------------------------
  
  if instr(lcase(strOS),"windows xp") > 0 then
     strKeyPath               = strCurrentUserSID & "\Control Panel\Desktop"
     strValueName             = "MultiUILanguageId"
     strLanguage              = "eng"
     objReg.GetStringvalue HKEY_USERS,strKeyPath,strValueName,strValue
     if NOT IsEmpty(strValue) Then
        strValue = UCase(right(strValue,4))
        if strValue = "040C" then strLanguage = "fra"
        if strValue = "0410" then strLanguage = "ita"
        if strValue = "0407" then strLanguage = "ger"
        if strValue = "040A" then strLanguage = "spa"
        if strValue = "0C0A" then strLanguage = "spa"
     end if
      else
     strKeyPath               = strCurrentUserSID & "\Control Panel\Desktop"
     strValueName             = "PreferredUILanguages"
     strLanguage              = "eng"
     objReg.GetMultiStringValue HKEY_USERS,strKeyPath,strValueName,arrValues
     if not IsNull(arrValues) Then
        strValue=arrValues(0)
        if NOT IsEmpty(strValue) then
           strValue = lcase(left(strValue,2))
           if strValue = "fr" then strLanguage = "fra"
           if strValue = "it" then strLanguage = "ita"
           if strValue = "de" then strLanguage = "ger"
           if strValue = "es" then strLanguage = "spa"
           if strValue = "cs" then strLanguage = "cze"
           if strValue = "fi" then strLanguage = "fin"
           if strValue = "hu" then strLanguage = "hun"
           if strValue = "pl" then strLanguage = "pol"
           if strValue = "ru" then strLanguage = "rus"
           if strValue = "sk" then strLanguage = "sky"
           if strValue = "sl" then strLanguage = "slv"
           if strValue = "sv" then strLanguage = "swe"
           if strValue = "tr" then strLanguage = "tur"
        end if
     end if
     if strLanguage = "eng" then
        strKeyPath               = ".DEFAULT\Control Panel\Desktop\MuiCached"
        strValueName             = "MachinePreferredUILanguages"
        objReg.GetMultiStringValue HKEY_USERS,strKeyPath,strValueName,arrValues
        if not IsNull(arrValues) Then
           strValue=arrValues(0)
           if NOT IsEmpty(strValue) then
              strValue = lcase(left(strValue,2))
              if strValue = "fr" then strLanguage = "fra"
              if strValue = "it" then strLanguage = "ita"
              if strValue = "de" then strLanguage = "ger"
              if strValue = "es" then strLanguage = "spa"
              if strValue = "cs" then strLanguage = "cze"
              if strValue = "fi" then strLanguage = "fin"
              if strValue = "hu" then strLanguage = "hun"
              if strValue = "pl" then strLanguage = "pol"
              if strValue = "ru" then strLanguage = "rus"
              if strValue = "sk" then strLanguage = "sky"
              if strValue = "sl" then strLanguage = "slv"
              if strValue = "sv" then strLanguage = "swe"
              if strValue = "tr" then strLanguage = "tur"
           end if
        end if
     end if     
  end if

  DetectUserUILanguage = strLanguage
End Function

Function fnKillProcess(strProcessName)

' ------------------------------------------------------------------------------------------------
' Function: fnKillProcess(strProcessName)
' Terminates the given processname.
' ------------------------------------------------------------------------------------------------

  Set colProcess = fn_objWMIService.ExecQuery ("Select * From Win32_Process")
  For Each objProcess In colProcess
    If Instr(LCase(objProcess.Name),LCase(strProcessName)) > 0 Then
       objShell.Run "TASKKILL /F /T /IM " & objProcess.Name, 0, False
       objProcess.Terminate()
       WriteToLog("Terminating application: " & objProcess.Name)
    End If
  Next

End Function

Function fnCheckIsMSIInstalled(strMSIName)

  ' -----------------------------------------------------------------------------------------------------------------
  ' Function: fnCheckIsMSIInstalled(strMSIName)
  ' Checks if the MSI given in strMSIName has been installed.
  ' strMSIName = "\\server\share\path\to\install.msi"
  ' This script is based on the example in 
  ' http://www.pcreview.co.uk/forums/getting-property-info-via-vbscript-text-file-t1544137.html
  ' -----------------------------------------------------------------------------------------------------------------

  Dim fn_objFSO    : Set fn_objFSO = CreateObject("Scripting.FileSystemObject")
  Dim fn_objWI     : Set fn_objWI  = CreateObject("WindowsInstaller.Installer")
 
  Dim fn_objReg    : Set fn_objReg = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\default:StdRegProv")
  Dim strKeyPath   : strKeyPath    = "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\"
  Dim strValueName : strValueName  = "InstallDate"

  ' -----------------------------------------------------------------------------------------------------------------
  ' check if the file exists and that the given file is a MSI
  ' -----------------------------------------------------------------------------------------------------------------

  If NOT fn_objFSO.FileExists(strMSIName) then
     ' --------------------------------------------------------------------------------------------------------------
     ' Leave the function and clean up the mess.
     ' --------------------------------------------------------------------------------------------------------------
     fnCheckIsMSIInstalled = False
     Set fn_objFSO     = Nothing
     Set fn_objWI      = Nothing
     Set fn_objReg     = Nothing
     Exit Function
  End If
  If lcase(right(strMSIName,3)) <> "msi" then
     ' --------------------------------------------------------------------------------------------------------------
     ' Leave the function and clean up the mess.
     ' --------------------------------------------------------------------------------------------------------------
     fnCheckIsMSIInstalled = False
     Set fn_objFSO     = Nothing
     Set fn_objWI      = Nothing
     Set fn_objReg     = Nothing
     Exit Function
  End If

  ' -----------------------------------------------------------------------------------------------------------------
  ' Find the property 'ProductCode' in the given MSI
  ' -----------------------------------------------------------------------------------------------------------------

  Dim fn_objDB     : Set fn_objDB   = fn_objWI.OpenDatabase(strMSIName,2)
  Dim fn_objView   : Set fn_objView = fn_objDB.OpenView("Select `Value` From Property WHERE `Property` ='ProductCode'")
  fn_objView.Execute
  Dim fn_objRec    : Set fn_objRec  = fn_objView.Fetch
  If fn_objRec is Nothing Then
     ' --------------------------------------------------------------------------------------------------------------
     ' Leave the function and clean up the mess.
     ' --------------------------------------------------------------------------------------------------------------
     fnCheckIsMSIInstalled = False
     Set fn_objFSO     = Nothing
     Set fn_objWI      = Nothing
     Set fn_objReg     = Nothing
     Set fn_objDB      = Nothing
     Set fn_objView    = Nothing
     Set fn_objRec     = Nothing
     Exit Function
  End If
  Dim strProductCode : strProductCode = fn_objRec.StringData(1)

  ' -----------------------------------------------------------------------------------------------------------------
  ' Check that the key InstallDate in the hive HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{ProductCode} 
  ' exists
  ' -----------------------------------------------------------------------------------------------------------------

  strKeyPath = strKeyPath & strProductCode
  fn_objReg.GetStringValue HKEY_LOCAL_MACHINE,strKeyPath,strValueName,strValue
  if IsNull(strValue) then 
     fnCheckIsMSIInstalled = False
       else
     fnCheckIsMSIInstalled = True
  end if
  
  Set fn_objFSO     = Nothing
  Set fn_objWI      = Nothing
  Set fn_objReg     = Nothing
  Set fn_objDB      = Nothing
  Set fn_objView    = Nothing
  Set fn_objRec     = Nothing
  
End Function

Function KeyExists(Key, KeyPath)

' ------------------------------------------------------------------------------------------------
' Function: KeyExists(Key, KeyPath)
' Checks to see if the 'Key' in 'KeyPath' exists.
' ------------------------------------------------------------------------------------------------

  Dim arrSubKeys
  Dim i
  Dim regReadResult : regReadResult = objReg.EnumKey(HKEY_LOCAL_MACHINE, KeyPath, arrSubKeys)
  
  	
  KeyExists = False
    
  If regReadResult = 0 And IsArray(arrSubKeys) Then
     For i = 0 To UBound(arrSubKeys)
         If InStr(arrSubKeys(i), Key) <> 0 Then
       	    KeyExists = True
    	    i = UBound(arrSubKeys)
	 End If
     Next	
  End If
    
End Function

Function ValueExists(Key, KeyPath)

' ------------------------------------------------------------------------------------------------
' Function: ValueExists(Key, KeyPath)
' Checks to see if the value 'Key' in 'KeyPath' exists.
' ------------------------------------------------------------------------------------------------

  Dim arrSubValues
  Dim i
  Dim regReadResult : regReadResult = objReg.EnumValues(HKEY_LOCAL_MACHINE, KeyPath, arrSubValues)

  	
  ValueExists = False
    
  If regReadResult = 0 And IsArray(arrSubValues) Then
     For i = 0 To UBound(arrSubValues)
         If InStr(arrSubValues(i), Key) <> 0 Then
    	    ValueExists = True
    	    i = UBound(arrSubValues)
         End If
     Next	
  End If

End Function


Sub EnablePackageScriptsViaRegistry

' ------------------------------------------------------------------------------------------------
' Set 'EnablePackageScripts' via the registry. 
' ------------------------------------------------------------------------------------------------

  WriteToLog("########## Modify 'EnablePackageScripts' via the registry.            ##########")

  strKeyPath   = "SOFTWARE\Microsoft\AppV\Client\Scripting"
  strValueName = "EnablePackageScripts"
  objReg.SetDWordValue HKEY_LOCAL_MACHINE,strKeyPath,strValueName,1
  wscript.sleep 1000
  objReg.GetDWordValue HKEY_LOCAL_MACHINE,strKeyPath,strValueName,dwValue
  WriteToLog("The key HKEY_LOCAL_MACHINE\" & strKeyPath)
  WriteToLog("   Value: " & strValueName & " has the value: " & dwValue)

  WriteToLog("########## End modifying 'EnablePackageScripts' via the registry.     ##########")

End Sub

After the




Load all AppV 4.6 packages into the AppV Cache

After the migration from SCCM 2007 to SCCM 2012 some users had an issue with accessing their AppV 4.6 packages. They received the following error:

Error message that AppV 46 cannot be found in ccmcache.

Error message that AppV 46 cannot be found in ccmcache.


What happens. During the client migration the ccmcache is cleared. So if an AppV 4.6 package has not been started before the migration, the content could not be found after starting the package after the migration.
That has been solved by creating a vbscript that is run before the SCCM 2007 client is migrated to SCCM 2012. In short: for each AppV 4.6 application the virtual package is loaded into the AppV Cache.

' ================================================================================================
' Load all the AppV 4.6 Packages again
' Created by Willem-Jan Vroom
' (c) Canon Europe B.V.
' Version history:
'
' v0.0.1:
'   Initial version
'
' v0.0.2:
'   Add functionality:
'     - Unlocks the AppV 4.6 package if it is in use
'     - For each entry a log file is created. 
' ================================================================================================

' ------------------------------------------------------------------------------------------------
' Declare the most variables. 
' ------------------------------------------------------------------------------------------------

  Option Explicit

  Dim objShell               : set objShell         = WScript.CreateObject("WScript.Shell")
  Dim objNetWork             : set objNetWork       = Wscript.CreateObject("WScript.Network")
  Dim objLogFileFSO          : Set objLogFileFSO    = CreateObject("Scripting.FileSystemObject")
  Dim objFSO                 : Set objFSO           = CreateObject("Scripting.FileSystemObject")
  Dim objProcessEnv          : Set objProcessEnv    = objShell.Environment("PROCESS")
  Dim objWMIService          : Set objWMIService    = GetObject("winmgmts:\\.\root\cimv2")
  Dim objReg                 : Set objReg           = GetObject("winmgmts:\\.\root\default:StdRegProv")
  Dim objLogFile
  Dim colListOfServices
  Dim objService                
  Dim strCurrentDir          : strCurrentDir        = Left(Wscript.ScriptFullname, InstrRev(Wscript.ScriptFullname, "\"))
                               strCurrentDir        = Left(strCurrentDir,len(strCurrentDir)-1)
  Dim strCommand             : strCommand           = ""
  Dim strLogLocation         : strLogLocation       = "C:\WINDOWS\system32\Logfiles"
  Dim strcomputerName        : strcomputerName      = objProcessEnv("COMPUTERNAME")
  Dim strOutputFile          : strOutputFile        = strLogLocation & "\" & strcomputerName & "_ReinstallAllAppV46Packages_" & Replace(FormatDateTime(Now(), 2),"/","-") & ".log"
  Dim strOS                  : strOS                = ""
  Dim strKeyPath             : strKeyPath           = ""
  Dim strArchitecture        : strArchitecture      = "x86"
  Dim strWindir              : strWinDir            = objShell.ExpandEnvironmentStrings("%WinDir%")

  Dim valOSBuildNumber, valResult
  Dim objItem, colItems
  Dim arrSubKeys, objSubKey
  Dim strProcess

  Const ForWriting           = 2
  Const ForReading           = 1
  Const ForAppending         = 8
  Const OverWriteFiles       = True
  const HKEY_CURRENT_USER    = &H80000001
  const HKEY_LOCAL_MACHINE   = &H80000002
  Const HKEY_USERS           = &H80000003 

' ------------------------------------------------------------------------------------------------
' Detect the current OS architecture.  
' ------------------------------------------------------------------------------------------------

  if objFSO.FolderExists(strWindir & "\syswow64") Then
     strArchitecture = "x64"
  end if

' ------------------------------------------------------------------------------------------------
' Create the log location (if not exists)
' Open the logfile.  
' ------------------------------------------------------------------------------------------------

  CreateFolderStructure(strLogLocation)
  OpenLogFile()
  WriteToLog "- ACTION: script started."

' ------------------------------------------------------------------------------------------------
' Detect the current OS.  
' ------------------------------------------------------------------------------------------------

  Set colItems = objWMIService.ExecQuery("Select Caption,BuildNumber from Win32_OperatingSystem")
  For Each objItem in colItems
      strOS            = objItem.Caption
      valOSBuildNumber = objItem.BuildNumber
  Next

  WriteToLog("########## Details regarding operating system and logged on user        ##########")
  WriteToLog("Found Operating System:    " & strOS)
  WriteToLog("Found OS architecture:     " & strArchitecture)
  WriteToLog("Wscript engine:            " & wscript.FullName)
  WriteToLog("########## End details regarding operating system and logged on user    ##########")
  WriteToLog(" ")

' ------------------------------------------------------------------------------------------------
' Kill all the processes that belongs to the virtual applications
' The location of the packages are:
' on x86:
'  HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\SoftGrid\4.5\Client\Applications
' 
' on x64:
'  HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\SoftGrid\4.5\Client\Applications
' ------------------------------------------------------------------------------------------------

  if strArchitecture = "x64" then
     strKeyPath = "SOFTWARE\Wow6432Node\Microsoft\SoftGrid\4.5\Client\Applications"
      else
     strKeyPath = "SOFTWARE\Microsoft\SoftGrid\4.5\Client\Applications"
  end if

  WriteToLog ("---------------------------------------------------------------------------------")
  WriteToLog ("Closing all applications that belongs to an AppV 4.6 package.")

  objReg.EnumKey HKEY_LOCAL_MACHINE, strKeyPath, arrSubKeys
  if IsArray(arrSubKeys) Then
     For Each objSubKey in arrSubKeys
         KillProcessFromAllvirtualPackages(strKeyPath & "\" & objSubKey)
     Next
     else 
    WriteToLog("No AppV 4.6 packages have been found.")
  end if
  WriteToLog ("---------------------------------------------------------------------------------")
  WriteToLog (" ")


' ------------------------------------------------------------------------------------------------
' Load the App-V 4.6 packages. 
' The location of the packages are:
' on x86:
'  HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\SoftGrid\4.5\Client\Packages
' 
' on x64:
'  HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\SoftGrid\4.5\Client\Packages
' ------------------------------------------------------------------------------------------------

  if strArchitecture = "x64" then
     strKeyPath = "SOFTWARE\Wow6432Node\Microsoft\SoftGrid\4.5\Client\Packages"
      else
     strKeyPath = "SOFTWARE\Microsoft\SoftGrid\4.5\Client\Packages"
  end if

  objReg.EnumKey HKEY_LOCAL_MACHINE, strKeyPath, arrSubKeys
  if IsArray(arrSubKeys) Then
     For Each objSubKey in arrSubKeys
         LoadAppV46Package(strKeyPath & "\" & objSubKey)
     Next
     else 
    WriteToLog("No AppV 4.6 packages have been found. Thus quitting.")
  end if

  CloseLogFile()


Sub OpenLogFile() 

' ------------------------------------------------------------------------------------------------
' Subroutine: OpenLogFile() 
' The name of the logfile is mentinoed in the variabele strOutputFile.
' ------------------------------------------------------------------------------------------------

  If objLogFileFSO.FileExists(strOutputFile) Then
     Set objLogFile = objLogFileFSO.OpenTextFile(strOutputFile, ForWriting)
         Else
     Set objLogFile = objLogFileFSO.CreateTextFile(strOutputFile)
  End If

End Sub


Function WriteToLog(sLogMessage)

' ------------------------------------------------------------------------------------------------
' Function: WriteToLog(sLogMessage)
' Writes an entry 'sLogMessage' in the logfile.
' ------------------------------------------------------------------------------------------------

  if instr(sLogMessage, "- ACTION: ") = 0 then sLogMessage = "          " & sLogMessage
  objLogFile.WriteLine("Time: " & now & "  " & sLogMessage)

End Function

Sub CloseLogFile()

' ------------------------------------------------------------------------------------------------
' Subroutine: CloseLogFile()
' Close the log file.
' ------------------------------------------------------------------------------------------------
  WriteToLog("########## End repairing all the AppV Packages.                         ##########")
  WriteToLog("- ACTION: script ended.")           
  objLogFile.Close
  Set objLogfileFSO = Nothing

End Sub


Sub CreateFolderStructure(strFolderNameToBeCreated)

' ------------------------------------------------------------------------------------------------
' Subroutine: CreateFolderStructure(strFolderNameToBeCreated)
' Creates the map as mentioned in strFolderNameToBeCreated.
' ------------------------------------------------------------------------------------------------

  Dim arrFoldersTMP : arrFoldersTMP = split (strFolderNameToBeCreated,"\")
  Dim strFolder     : strFolder     = ""
  Dim objFolderTMP

  For Each objFolderTMP in arrFoldersTMP
      strFolder = strFolder & objFolderTMP
      If NOT objFSO.FolderExists(strFolder) Then
             objFSO.CreateFolder(strFolder)
      end If
      strFolder = strFolder & "\"
  Next

End Sub

Sub KillProcessFromAllvirtualPackages(key)

' ------------------------------------------------------------------------------------------------
' Subroutine: LoadAppV46Package(key)
' ------------------------------------------------------------------------------------------------

  Dim strProcessNameToKill : objReg.GetStringValue HKEY_LOCAL_MACHINE, key, "AppPath", strProcessNameToKill

  if not IsNull(strProcessNameToKill) Then

     Dim numFirstChar : numFirstChar = len(left(strProcessNameToKill, InstrRev(strProcessNameToKill, "\")))
     Dim numLastChar  : numLastChar  = len(strProcessNameToKill)

     call fnKillProcess(mid(strProcessNameToKill,numFirstChar+1,numLastChar))
  end if

End Sub



Sub LoadAppV46Package(key)

' ------------------------------------------------------------------------------------------------
' Subroutine: LoadAppV46Package(key)
' ------------------------------------------------------------------------------------------------

  Dim strSoftGridPackageName  : objReg.GetStringValue HKEY_LOCAL_MACHINE, key, "Name",        strSoftGridPackageName
  Dim strSoftGridSFTName      : objReg.GetStringValue HKEY_LOCAL_MACHINE, key, "OverrideURL", strSoftGridSFTName
  Dim strCommand


  WriteToLog ("---------------------------------------------------------------------------------")
  WriteToLog ("Processing SCCM AppV Package: " & strSoftGridPackageName & ".")

  

  strCommand = "sftmime unlock package:" & strSoftGridPackageName & " /LOG c:\windows\system32\logfiles\Unlock_AppV46Package_" & strSoftGridPackageName & ".log"
  WriteToLog ("Running command: " & ucase(strCommand))

  On Error Resume Next
  objShell.Run strCommand,6,True
  If Err.Number <> 0 Then
     WriteToLog("The following error occurred: " & Err.Description)
  end if
  On Error Goto 0
  
  On Error Resume Next
  strCommand = "sftmime unload package:" & strSoftGridPackageName & " /LOG c:\windows\system32\logfiles\Unload_AppV46Package_" & strSoftGridPackageName & ".log"
  WriteToLog ("Running command: " & ucase(strCommand))
  objShell.Run strCommand,6,True
  If Err.Number <> 0 Then
     WriteToLog("The following error occurred: " & Err.Description)
  end if
  On Error Goto 0

  On Error Resume Next
  strCommand = "sftmime load package:" & strSoftGridPackageName & " /sftpath "
  strCommand = strCommand & chr(34) & strSoftGridSFTName & chr(34) & " /LOG c:\windows\system32\logfiles\Load_AppV46Package_" & strSoftGridPackageName & ".log"
  WriteToLog ("Running command: " & ucase(strCommand))
  objShell.Run strCommand,6,True
  If Err.Number <> 0 Then
     WriteToLog("The following error occurred: " & Err.Description)
  end if
  On Error Goto 0


  On Error Goto 0

  WriteToLog ("End processing SCCM AppV Package: " & strSoftGridPackageName & ".")
  WriteToLog ("---------------------------------------------------------------------------------")
  WriteToLog (" ")
  

End Sub

Function fnKillProcess(strProcessName)

' ------------------------------------------------------------------------------------------------
' Function: fnKillProcess(strProcessName)
' Terminates the given processname.
' ------------------------------------------------------------------------------------------------

  Dim colProcess
  Dim objProcess

  Set colProcess = objWMIService.ExecQuery ("Select * From Win32_Process")
  For Each objProcess In colProcess
    If Instr(LCase(objProcess.Name),LCase(strProcessName)) > 0 Then
       objShell.Run "TASKKILL /F /T /IM " & objProcess.Name, 0, False
       objProcess.Terminate()
       WriteToLog("Terminating application: " & objProcess.Name)
    End If
  Next

End Function

Sub CheckProcess(strProcess)
' ------------------------------------------------------------------------------------------------
' Subroutine: CheckProcess(strProces)
' checks if a process is running. The subroutine is left when the process is not running
' anymore.
' ------------------------------------------------------------------------------------------------

  Dim blnProcessFound    : blnProcessFound = True
  Dim intLoopNotFound    : intLoopNotFound = 0
  Dim MINLoopCount       : MINLoopCount    = 3
  Dim objSWbemServices
  Dim colSWbemObjectSet
  Dim objSWbemObject
  Dim strCmdLine
  
  strProcess = ucase(strProcess)
  
  Do While (blnProcessFound And intLoopNotFound < 3)
    blnProcessFound = False
    WScript.Sleep 3000
    Set objSWbemServices = GetObject("winmgmts:\\.\root\cimv2")
    Set colSWbemObjectSet = objSWbemServices.InstancesOf("Win32_Process")
    For Each objSWbemObject In colSWbemObjectSet
        If (ucase(objSWbemObject.Name) = strProcess) And  (strCmdLine = "" Or strCmdLine = objSWbemObject.CommandLine) then
            blnProcessFound = true
                    intLoopNotFound = 0
        End If
    Next
    If Not blnProcessFound Then intLoopNotFound = intLoopNotFound + 1
    Set colSWbemObjectSet = Nothing
    Set objSWbemServices  = Nothing
  Loop

End Sub

Now the user can start the application without issues:

 Successfull start.

Successfull start.




Uninstall AppV 5.0 package automatically if a computer is removed from a device based collection in SCCM 2012

In my article Uninstall AppV 5.0 package automatically if an user is removed from a user based collection in SCCM 2012 I described how an application is removed if an user is removed from an user based collection.
In this article I will describe how an application is uninstalled if a computer is removed from a device based collection.

Create the computer based uninstall query

On page 30 in the document Managing AppV5 with Configuration Manager 2012SP1 it is described how a computer based uninstall collection is made.

The uninstall query.

The uninstall query.

The query is:

select SMS_R_SYSTEM.ResourceID,SMS_R_SYSTEM.ResourceType,SMS_R_SYSTEM.Name,SMS_R_SYSTEM.SMSUniqueIdentifier,SMS_R_SYSTEM.ResourceDomainORWorkgroup,SMS_R_SYSTEM.Client
from
SMS_R_System inner join SMS_G_System_AppClientState on SMS_G_System_AppClientState.MachineName = SMS_R_System.Name
where 
SMS_G_System_AppClientState.ComplianceState = "1"
and 
SMS_G_System_AppClientState.AppName ="Mozilla_Firefox_24_DUT_WIN7"

Create the uninstall deployment

The application ‘Mozilla_Firefox_24_DUT_WIN7’ has both an install and uninstall deployment.

The application has both an install and uninstall deployment.

The application has both an install and uninstall deployment.