Skip to content

Instantly share code, notes, and snippets.

@WinSe7en
Last active December 19, 2016 07:55
Show Gist options
  • Save WinSe7en/da6f4035bcabd1587ed0 to your computer and use it in GitHub Desktop.
Save WinSe7en/da6f4035bcabd1587ed0 to your computer and use it in GitHub Desktop.
@ECHO ON
REM This script is used to remove Office 2007, Office 2010 and Office 2013.
set WorkingDir=%~dp0
set ProductName03={90110409-6000-11D3-8CFE-0150048383C9}
set ProductName07={90120000-0030-0000-0000-0000000FF1CE}
set ProductName14=Office14.PROPLUS
set ProductNameVIS=Office14.VISIO
set ProductNamePRJ=Office14.PRJPRO
set ProductNameLync={90150000-012C-0000-0000-0000000FF1CE}
set ProductName15=Office15.PROPLUS
IF NOT "%ProgramFiles(x86)%"=="" (goto ARP64) else (goto ARP86)
:ARP64
reg query HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432NODE\Microsoft\Windows\CurrentVersion\Uninstall\%ProductName03%
if %errorlevel%==1 (set Installed03=no) else (set Installed03=yes)
reg query HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432NODE\Microsoft\Windows\CurrentVersion\Uninstall\%ProductName07%
if %errorlevel%==1 (set Installed07=no) else (set Installed07=yes)
reg query HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432NODE\Microsoft\Windows\CurrentVersion\Uninstall\%ProductName14%
if %errorlevel%==1 (set Installed14-32=no) else (set Installed14-32=yes)
reg query HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432NODE\Microsoft\Windows\CurrentVersion\Uninstall\%ProductNameLync%
if %errorlevel%==1 (set LyncInstalled-32=no) else (set LyncInstalled-32=yes)
reg query HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432NODE\Microsoft\Windows\CurrentVersion\Uninstall\%ProductName15%
if %errorlevel%==1 (set Installed15-32=no) else (set Installed15-32=yes)
reg query HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\%ProductName03%
if %errorlevel%==1 (set Installed03=no) else (set Installed03=yes)
reg query HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\%ProductName07%
if %errorlevel%==1 (set Installed07=no) else (set Installed07=yes)
reg query HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\%ProductName14%
if %errorlevel%==1 (set Installed14=no) else (set Installed14=yes)
reg query HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\%ProductNameLync%
if %errorlevel%==1 (set LyncInstalled=no) else (set LyncInstalled=yes)
reg query HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\%ProductName15%
if %errorlevel%==1 (set Installed15=no) else (set Installed15=yes)
goto OfficeScrub
goto OfficeScrub
REM Check for 32 and 64 bit versions of Office 2010. (Office 64bit would also appear here on a 64bit OS)
:ARP86
reg query HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\%ProductName03%
if %errorlevel%==1 (set Installed03=no) else (set Installed03=yes)
reg query HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\%ProductName07%
if %errorlevel%==1 (set Installed07=no) else (set Installed07=yes)
reg query HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\%ProductName14%
if %errorlevel%==1 (set Installed14=no) else (set Installed14=yes)
reg query HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\%ProductName15%
if %errorlevel%==1 (set Installed15=no) else (set Installed15=yes)
goto OfficeScrub
:OfficeScrub
if "%Installed03%" == "yes" goto Remove2003
if "%Installed07%" == "yes" goto Remove2007
if "%Installed14%" == "yes" goto Remove2014
if "%Installed14-32%" == "yes" goto Remove2014
if "%Installed15%" == "yes" goto Remove2015
if "%Installed15-32%" == "yes" goto Remove2015
goto OfficeInstall
:Remove2003
call cscript.exe %WorkingDir%\OfficeScrubbers\OffScrub03.vbs ProPlus /Force /Log %temp% /s
call cscript.exe %WorkingDir%\OfficeScrubbers\OffScrub03.vbs /OCE /Force /Log %temp% /s
set Installed03=no
goto OfficeScrub
:Remove2007
call cscript.exe %WorkingDir%\OfficeScrubbers\OffScrub07.vbs ProPlus /Force /s /log %temp%
call cscript.exe %WorkingDir%\OfficeScrubbers\OffScrub07.vbs ENTERPRISE /Force /s /log %temp%
call cscript.exe %WorkingDir%\OfficeScrubbers\OffScrub07.vbs /OSE /Force /s /log %temp%
set Installed07=no
goto OfficeScrub
:Remove2014
call cscript.exe %WorkingDir%\OfficeScrubbers\OffScrub14.vbs ALL /log %temp%
set Installed14=no
set Installed14-32=no
goto OfficeScrub
:Remove2015
call cscript.exe %WorkingDir%\OfficeScrubbers\OffScrub_O15msi.vbs PROPLUS /log %temp%
set Installed15=no
set Installed15-32=no
goto OfficeScrub
:OfficeInstall
%WorkingDir%setup.exe /adminfile %WorkingDir%Office2016x86v2.MSP
IF NOT "%ProgramFiles(x86)%"=="" (goto ACTOFFICE64) else (goto ACTOFFICE32)
:ACTOFFICE64
cscript "%ProgramFiles(x86)%\Microsoft Office\Office16\ospp.vbs" /act
exit /b 0
:ACTOFFICE32
cscript "%ProgramFiles%\Microsoft Office\Office16\ospp.vbs" /act
exit /b 0
'=======================================================================================================
' Name: OffScrub03.vbs
' Author: Microsoft Customer Support Services
' Copyright (c) 2010, Microsoft Corporation
' Script to remove (scrub) Office 2003 products
'=======================================================================================================
Option Explicit
Const SCRIPTVERSION = "1.31"
Const SCRIPTFILE = "OffScrub03.vbs"
Const SCRIPTNAME = "OffScrub03"
Const OVERSION = "11.0"
Const OVERSIONMAJOR = "11"
Const OREF = "Office11"
Const OREGREF = ""
Const ONAME = "Office 2003"
Const OPACKAGE = ""
Const OFFICEID = "6000-11D3-8CFE-0150048383C9}"
Const SQOFFICEID = "Vn-}f(ZXfeAR6.ji"
Const HKCR = &H80000000
Const HKCU = &H80000001
Const HKLM = &H80000002
Const HKU = &H80000003
Const FOR_WRITING = 2
Const PRODLEN = 28
Const SQPRODLEN = 16
Const COMPPERMANENT = "00000000000000000000000000000000"
Const UNCOMPRESSED = 38
Const SQUISHED = 20
Const COMPRESSED = 32
Const REG_ARP = "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\"
Const VB_YES = 6
Const MSIOPENDATABASEREADONLY = 0
'=======================================================================================================
Dim oFso, oMsi, oReg, oWShell, oWmiLocal
Dim ComputerItem, Item, LogStream, TmpKey
Dim arrTmpSKUs, arrDeleteFiles, arrDeleteFolders, arrMseFolders
Dim dicKeepProd, dicKeepLis, dicApps, dicKeepFolder, dicDelRegKey, dicKeepReg
Dim dicInstalledSku, dicRemoveSku, dicKeepSku, dicSrv, dicCSuite, dicCSingle
Dim f64, fLegacyProductFound
Dim sErr, sTmp, sSkuRemoveList, sDefault, sWinDir, sWICacheDir, sMode
Dim sAppData, sTemp, sScrubDir, sProgramFiles, sProgramFilesX86, sCommonProgramFiles, sCommonProgramFilesX86
Dim sAllusersProfile
Dim sProgramData, sLocalAppData, sOInstallRoot
'=======================================================================================================
'Main
'=======================================================================================================
'Configure defaults
Dim sLogDir : sLogDir = ""
Dim sMoveMessage: sMoveMessage = ""
Dim fRemoveOse : fRemoveOse = False
Dim fRemoveOspp : fRemoveOspp = False
Dim fRemoveAll : fRemoveAll = False
Dim fRemoveC2R : fRemoveC2R = False
Dim fRemoveAppV : fRemoveAppV = False
Dim fRemoveCSuites : fRemoveCSuites = False
Dim fRemoveCSingle : fRemoveCSingle = False
Dim fRemoveSrv : fRemoveSrv = False
Dim fKeepUser : fKeepUser = True 'Default to keep per user settings
Dim fSkipSD : fSkipSD = False 'Default to not Skip the Shortcut Detection
Dim fDetectOnly : fDetectOnly = False
Dim fQuiet : fQuiet = False
Dim fNoCancel : fNoCancel = False
Dim fElevated : fElevated = False
Dim fTryReconcile : fTryReconcile = False
'CAUTION! -> "fForce" will kill running applications which can result in data loss! <- CAUTION
Dim fForce : fForce = False
'CAUTION! -> "fForce" will kill running applications which can result in data loss! <- CAUTION
Dim fLogInitialized : fLogInitialized = False
Dim fBypass_Stage1 : fBypass_Stage1 = False 'Component Detection
Dim fBypass_Stage2 : fBypass_Stage2 = False 'Msiexec
Dim fBypass_Stage3 : fBypass_Stage3 = False 'CleanUp
Dim fRebootRequired : fRebootRequired = False
'Create required objects
Set oWmiLocal = GetObject("winmgmts:\\.\root\cimv2")
Set oWShell = CreateObject("Wscript.Shell")
Set oFso = CreateObject("Scripting.FileSystemObject")
Set oMsi = CreateObject("WindowsInstaller.Installer")
Set oReg = GetObject("winmgmts:\\.\root\default:StdRegProv")
'Get environment path info
sAppData = oWShell.ExpandEnvironmentStrings("%appdata%")
sLocalAppData = oWShell.ExpandEnvironmentStrings("%localappdata%")
sTemp = oWShell.ExpandEnvironmentStrings("%temp%")
sAllUsersProfile = oWShell.ExpandEnvironmentStrings("%allusersprofile%")
sProgramFiles = oWShell.ExpandEnvironmentStrings("%programfiles%")
'Deferred until after architecture check
'sProgramFilesX86 = oWShell.ExpandEnvironmentStrings("%programfiles(x86)%")
sCommonProgramFiles = oWShell.ExpandEnvironmentStrings("%commonprogramfiles%")
'Deferred until after architecture check
'sCommonProgramFilesX86 = oWShell.ExpandEnvironmentStrings("%CommonProgramFiles(x86)%")
sProgramData = oWSHell.ExpandEnvironmentStrings("%programdata%")
sWinDir = oWShell.ExpandEnvironmentStrings("%windir%")
sWICacheDir = sWinDir & "\" & "Installer"
sScrubDir = sTemp & "\" & SCRIPTNAME
'Detect if we're running on a 64 bit OS
Set ComputerItem = oWmiLocal.ExecQuery("Select * from Win32_ComputerSystem")
For Each Item In ComputerItem
f64 = Instr(Left(Item.SystemType,3),"64") > 0
If f64 Then Exit For
Next
If f64 Then sProgramFilesX86 = oWShell.ExpandEnvironmentStrings("%programfiles(x86)%")
If f64 Then sCommonProgramFilesX86 = oWShell.ExpandEnvironmentStrings("%CommonProgramFiles(x86)%")
'Ensure CScript as engine
If Not UCase(Mid(Wscript.FullName, Len(Wscript.Path) + 2, 1)) = "C" Then RelaunchAsCScript
'Create Dictionaries
Set dicKeepProd = CreateObject("Scripting.Dictionary")
Set dicInstalledSku = CreateObject("Scripting.Dictionary")
Set dicRemoveSku = CreateObject("Scripting.Dictionary")
Set dicKeepSku = CreateObject("Scripting.Dictionary")
Set dicKeepLis = CreateObject("Scripting.Dictionary")
Set dicKeepFolder = CreateObject("Scripting.Dictionary")
Set dicApps = CreateObject("Scripting.Dictionary")
Set dicDelRegKey = CreateObject("Scripting.Dictionary")
Set dicKeepReg = CreateObject("Scripting.Dictionary")
Set dicSrv = CreateObject("Scripting.Dictionary")
Set dicCSuite = CreateObject("Scripting.Dictionary")
Set dicCSingle = CreateObject("Scripting.Dictionary")
'Create the temp folder
If Not oFso.FolderExists(sScrubDir) Then oFso.CreateFolder sScrubDir
'Set the default logging directory
sLogDir = sScrubDir
'Call the command line parser
ParseCmdLine
If Not CheckRegPermissions Then
'Try to relaunch elevated
If NOT fQuiet Then RelaunchElevated
'Can't relaunch. Exit out
Log "Insufficient registry access permissions - exiting"
'Undo temporary entries created in ARP
TmpKeyCleanUp
wscript.quit
End If
'Get Office Install Folder
If NOT RegReadValue(HKLM,"SOFTWARE\Microsoft\Office\"&OVERSION&"\Common\InstallRoot","Path",sOInstallRoot,"REG_SZ") Then
sOInstallRoot = sProgramFiles & "\Microsoft Office\"&OREF
End If
'Ensure integrity of WI metadata which could fail used APIs otherwise
EnsureValidWIMetadata HKCU,"Software\Classes\Installer\Products",COMPRESSED
EnsureValidWIMetadata HKCR,"Installer\Products",COMPRESSED
EnsureValidWIMetadata HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products",COMPRESSED
EnsureValidWIMetadata HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components",COMPRESSED
EnsureValidWIMetadata HKCR,"Installer\Components",COMPRESSED
'Add initial known .exe files that might need to be closed
dicApps.Add "communicator.exe","communicator.exe"
Select Case OVERSIONMAJOR
Case "12"
Case "14"
dicApps.Add "bcssync.exe","bcssync.exe"
dicApps.Add "officesas.exe","officesas.exe"
dicApps.Add "officesasscheduler.exe","officesasscheduler.exe"
dicApps.Add "msosync.exe","msosync.exe"
dicApps.Add "onenotem.exe","onenotem.exe"
Case Else
End Select
'-------------------
'Stage # 0 - Basics |
'-------------------
'Build a list with installed/registered Office products
sTmp = "Stage # 0 " & chr(34) & "Basics" & chr(34) & " (" & Time & ")"
Log vbCrLf & sTmp & vbCrLf & String(Len(sTmp),"=") & vbCrLf
FindInstalledOProducts
If dicInstalledSku.Count > 0 Then Log "Found registered product(s): " & Join(RemoveDuplicates(dicInstalledSku.Items),",") &vbCrLf
'Validate the list of products we got from the command line if applicable
ValidateRemoveSkuList
'Log detection results
If dicRemoveSku.Count > 0 Then Log "Product(s) to be removed: " & Join(RemoveDuplicates(dicRemoveSku.Items),",")
sMode = "Selected " & ONAME & " products"
If Not dicRemoveSku.Count > 0 Then sMode = "Orphaned " & ONAME & " products"
If fRemoveAll Then sMode = "All " & ONAME & " products"
Log "Final removal mode: " & sMode
Log "Remove OSE service: " & fRemoveOse &vbCrLf
'Log preview mode if applicable
If fDetectOnly Then Log "*************************************************************************"
If fDetectOnly Then Log "* PREVIEW MODE *"
If fDetectOnly Then Log "* All uninstall and delete operations will only be logged not executed! *"
If fDetectOnly Then Log "*************************************************************************" & vbCrLf
'Check if there are legacy products installed
CheckForLegacyProducts
If fLegacyProductFound Then Log "Found legacy Office products that will not be removed." Else Log "No legacy Office products found."
'Cache .msi files
If dicRemoveSku.Count > 0 Then CacheMsiFiles
'--------------------------------
'Stage # 1 - Component Detection |
'--------------------------------
sTmp = "Stage # 1 " & chr(34) & "Component Detection" & chr(34) & " (" & Time & ")"
Log vbCrLf & sTmp & vbCrLf & String(Len(sTmp),"=") & vbCrLf
If Not fBypass_Stage1 Then
'Build a list with files which are installed/registered to a product that's going to be removed
Log "Prepare for CleanUp stages."
Log "Identifying removable elements. This can take several minutes."
ScanComponents
Else
Log "Skipping Component Detection because bypass was requested."
End If
'End all running Office applications
If fForce OR fQuiet Then CloseOfficeApps
'------------------------
'Stage # 2 - Msiexec.exe |
'------------------------
sTmp = "Stage # 2 " & chr(34) & "Msiexec.exe" & chr(34) & " (" & Time & ")"
Log vbCrLf & sTmp & vbCrLf & String(Len(sTmp),"=") & vbCrLf
If Not fBypass_Stage2 Then
MsiexecRemoval
Else
Log "Skipping Msiexec.exe because bypass was requested."
End If
'--------------------
'Stage # 3 - CleanUp |
'--------------------
'Removal of files and registry settings
sTmp = "Stage # 3 " & chr(34) & "CleanUp" & chr(34) & " (" & Time & ")"
Log vbCrLf & sTmp & vbCrLf & String(Len(sTmp),"=") & vbCrLf
If Not fBypass_Stage3 Then
'Office Source Engine
If fRemoveOse Then RemoveOSE
'Local Installation Source (MSOCache)
WipeLIS
'Obsolete files
If fRemoveAll Then
FileWipeAll
Else
FileWipeIndividual
End If
'Empty Folders
DeleteEmptyFolders
'Restore Explorer if needed
If fForce Then RestoreExplorer
'Registry data
RegWipe
'Wipe orphaned files from Windows Installer cache
MsiClearOrphanedFiles
'Temporary .msi files in scrubcache
DeleteMsiScrubCache
'Temporary files
DelScrubTmp
Else
Log "Skipping CleanUp because bypass was requested."
End If
If Not sMoveMessage = "" Then Log vbCrLf & "Please remove this folder after next reboot: " & sMoveMessage
'THE END
Log vbCrLf & "End removal: " & Now & vbCrLf
Log vbCrLf & "For detailed logging please refer to the log in folder " &chr(34)&sScrubDir&chr(34)&vbCrLf
If fRebootRequired Then
Log vbCrLf & "A restart is required to complete the operation!"
If NOT fQuiet Then
If MsgBox("Do you want to reboot now?",vbYesNo,"Reboot Required") = VB_YES Then
Dim colOS, oOS
Dim oWmiReboot
Set oWmiReboot = GetObject("winmgmts:{impersonationLevel=impersonate,(Shutdown)}!\\.\root\cimv2")
Set colOS = oWmiReboot.ExecQuery ("Select * from Win32_OperatingSystem")
For Each oOS in colOS
oOS.Reboot()
Next
End If
End If
End If
'=======================================================================================================
'=======================================================================================================
'Stage 0 - 4 Subroutines
'=======================================================================================================
'Office products are listed with their ProductCode in the "Uninstall" key
Sub FindInstalledOProducts
Dim ArpItem, prod, Item, File
Dim sConfigName
Dim arrKeys
'Query msi to get a list of Office products
For Each prod in oMsi.Products
If Len(prod) = 38 Then
If UCase(Right(prod,PRODLEN)) = OFFICEID Then
sConfigName = ""
sConfigName = UCase(GetProductID(Mid(prod,4,2)))
If Not dicInstalledSku.Exists(prod) Then dicInstalledSku.Add UCase(prod),sConfigName
End If 'OFFICEID
End If '38
Next 'prod
'Locate Office products from ARP
If RegEnumKey(HKLM,REG_ARP,arrKeys) Then
For Each ArpItem in arrKeys
If Len(ArpItem) = 38 Then
If UCase(Right(ArpItem,PRODLEN)) = OFFICEID Then
sConfigName = ""
sConfigName = UCase(GetProductID(Mid(ArpItem,4,2)))
If Not dicInstalledSku.Exists(ArpItem) Then dicInstalledSku.Add UCase(ArpItem),sConfigName
End If
End If '38
Next 'ArpItem
End If 'RegEnumKey
If fTryReconcile Then
For Each File in oFso.GetFolder(sWICacheDir).Files
If Len(File.Name)>3 Then
Select Case LCase(Right(File.Name,4))
Case ".msi"
prod = ""
prod = GetMsiProductCode(File.Path)
If Len(prod) = 38 Then
If UCase(Right(prod,PRODLEN)) = OFFICEID Then
sConfigName = ""
sConfigName = UCase(GetProductID(Mid(prod,4,2)))
If Not dicInstalledSku.Exists(prod) Then
dicInstalledSku.Add UCase(prod),sConfigName
oFso.CopyFile File.Path,sScrubDir & "\" & prod & ".msi",True
MsiRegisterProduct(File.Path)
End If
End If 'OFFICEID
End If '38
Case Else
End Select
End If '>3
Next 'File
End If ' fTryReconcile
'Categorize the SKU
'Three categories are available: ClientSuite, ClientSingleProduct, Server
If dicInstalledSku.Count > 0 Then
For Each prod in dicInstalledSku.Keys
Select Case UCase(Mid(prod,4,2))
'Client
Case "11","12","13","15","16","17","18","1A","1B","1E","1F","23","33","44","A1","CA","E0","E3","FD","FF"
If NOT dicCSuite.Exists(UCase(prod)) Then dicCSuite.Add UCase(prod),dicInstalledSku.Item(prod)
'Server
Case "14","2E","32","92","A5"
If NOT dicSrv.Exists(UCase(prod)) Then dicSrv.Add UCase(prod),dicInstalledSku.Item(prod)
Case Else
'Standalone
If NOT dicCSingle.Exists(UCase(prod)) Then dicCSingle.Add UCase(prod),dicInstalledSku.Item(prod)
End Select
Next 'prod
End If 'dicInstalledSku.Count > 0
End Sub 'FindInstalledOProducts
'=======================================================================================================
'Check if there are Office products from previous versions on the computer
Sub CheckForLegacyProducts
Const OLEGACY = "78E1-11D2-B60F-006097C998E7}.6000-11D3-8CFE-0050048383C9}.BDCA-11D1-B7AE-00C04FB92F3D}.6D54-11D4-BEE3-00C04F990354}"
Dim Product
'Set safe default
fLegacyProductFound = True
For Each Product in oMsi.Products
If Len(Product) = 38 Then
'Handle O09 - O10 Products
If InStr(OLEGACY, UCase(Right(Product, 28)))>0 Then
'Found legacy Office product. Keep flag in default and exit
Exit Sub
End If
End If '38
Next 'Product
fLegacyProductFound = False
End Sub 'CheckForLegacyProducts
'=======================================================================================================
'Create clean list of Products to remove.
'Strip off bad & empty contents
Sub ValidateRemoveSkuList
Dim Sku, Key, sProductCode, sProductCodeList
Dim arrRemoveSKUs
If fRemoveAll Then
'Remove all mode
For Each Key in dicInstalledSku.Keys
dicRemoveSku.Add Key,dicInstalledSku.Item(Key)
Next 'Key
Else
'Remove individual products or preconfigured configurations mode
'Ensure to have a string with no unexpected contents
sSkuRemoveList = Replace(sSkuRemoveList,";",",")
sSkuRemoveList = Replace(sSkuRemoveList," ","")
sSkuRemoveList = Replace(sSkuRemoveList,Chr(34),"")
While InStr(sSkuRemoveList,",,")>0
sSkuRemoveList = Replace(sSkuRemoveList,",,",",")
Wend
'Prepare 'remove' and 'keep' dictionaries to determine what has to be removed
'Initial pre-fill of 'keep' dic
For Each Key in dicInstalledSku.Keys
dicKeepProd.Add Key,dicInstalledSku.Item(Key)
Next 'Key
'Determine contents of keep and remove dic
'Individual products
arrRemoveSKUs = Split(UCase(sSkuRemoveList),",")
For Each Sku in arrRemoveSKUs
If Sku = "OSE" Then fRemoveOse = True
If dicKeepProd.Exists(Sku) Then
'A productcode has been passed in
'remove the item from the keep dic
dicKeepProd.Remove(Sku)
'Now add it to the remove dic
If NOT dicRemoveSku.Exists(Sku) Then dicRemoveSku.Add Sku,Sku
End If
'Check the Sku based entries
For Each Key in dicKeepProd.Keys
If dicKeepProd.Item(Key) = Sku Then
If NOT dicRemoveSku.Exists(Sku) Then dicRemoveSku.Add Sku,Sku
dicKeepProd.Remove(Key)
End If
Next 'Key
Next 'Sku
'Client Suite Category
If fRemoveCSuites Then
For Each Key in dicInstalledSku.Keys
If dicCSuite.Exists(Key) Then
If dicKeepProd.Exists(Key) Then dicKeepProd.Remove(Key)
If NOT dicRemoveSku.Exists(Key) Then dicRemoveSku.Add Key,Key
End If
Next 'Key
End If 'fRemoveCSuites
'Client Single/Standalone Category
If fRemoveCSingle Then
For Each Key in dicInstalledSku.Keys
If dicCSingle.Exists(Key) Then
If dicKeepProd.Exists(Key) Then dicKeepProd.Remove(Key)
If NOT dicRemoveSku.Exists(Key) Then dicRemoveSku.Add Key,Key
End If
Next 'Key
End If 'fRemoveCSingle
'Server Category
If fRemoveSrv Then
For Each Key in dicInstalledSku.Keys
If dicSrv.Exists(Key) Then
If dicKeepProd.Exists(Key) Then dicKeepProd.Remove(Key)
If NOT dicRemoveSku.Exists(Key) Then dicRemoveSku.Add Key,Key
End If
Next 'Key
End If 'fRemoveSrv
If NOT dicKeepProd.Count > 0 Then fRemoveAll = True
End If 'fRemoveAll
If fRemoveAll OR fRemoveOse Then CheckRemoveOSE
End Sub 'ValidateRemoveSkuList
'=======================================================================================================
'Check if OSE service can be scrubbed
Sub CheckRemoveOSE
Dim Product
If fRemoveOse Then Exit Sub
For Each Product in oMsi.Products
If Len(Product) = 38 Then
If UCase(Right(Product,13))="0000000FF1CE}" Then
Select Case Mid(Product,4,2)
Case "12","14","15","16","17"
'Found another Office product. Set flag to keep the OSE service
If NOT Mid(Product,4,2) = OVERSIONMAJOR Then
fRemoveOse = False
Exit Sub
End If
Case Else
End Select
End If
End If '38
Next 'Product
fRemoveOse = True
End Sub 'CheckRemoveOSE
'=======================================================================================================
'Cache .msi files for products that will be removed in case they are needed for later file detection
Sub CacheMsiFiles
Dim Product
Dim sMsiFile
'Non critical routine for failures.
'Errors will be logged but must not fail the execution
On Error Resume Next
Log " Cache .msi files to temporary Scrub folder"
'Cache the files
For Each Product in oMsi.Products
'Ensure valid GUID length
If Len(Product) = 38 Then
If (Right(Product,PRODLEN) = OFFICEID) AND (fRemoveAll OR CheckDelete(Product))Then
CheckError "CacheMsiFiles"
sMsiFile = oMsi.ProductInfo(Product,"LocalPackage") : CheckError "CacheMsiFiles"
LogOnly " - " & Product & ".msi"
If oFso.FileExists(sMsiFile) Then oFso.CopyFile sMsiFile,sScrubDir & "\" & Product & ".msi",True
CheckError "CacheMsiFiles"
End If 'OFFICEID
End If '38
Next 'Product
Err.Clear
End Sub 'CacheMsiFiles
'=======================================================================================================
'Build a list of all files that will be deleted
Sub ScanComponents
Const MSIINSTALLSTATE_LOCAL = 3
Dim FileList, RegList, ComponentID, CompClient, Record, qView, MsiDb
Dim Processes, Process, Prop, prod
Dim sQuery, sSubKeyName, sPath, sFile, sMsiFile, sCompClient, sComponent, sCompReg
Dim fRemoveComponent, fAffectedComponent, fIsPermanent
Dim i, iProgress, iCompCnt, iRemCnt
Dim dicFLError, oDic, oFolderDic, dicCompPath
Dim hDefKey
'Logfile
Set FileList = oFso.OpenTextFile(sScrubDir & "\FileList.txt",FOR_WRITING,True,True)
Set RegList = oFso.OpenTextFile(sScrubDir & "\RegList.txt",FOR_WRITING,True,True)
'FileListError dic
Set dicFLError = CreateObject("Scripting.Dictionary")
Set oDic = CreateObject("Scripting.Dictionary")
Set oFolderDic = CreateObject("Scripting.Dictionary")
Set dicCompPath = CreateObject("Scripting.Dictionary")
'Prevent that API errors fail script execution
On Error Resume Next
iCompCnt = oMsi.Components.Count
If NOT Err = 0 Then
'API failure
Log "Error during components detection. Cannot complete this task."
Err.Clear
Exit Sub
End If
'Ensure to not divide by zero
If iCompCnt = 0 Then iCompCnt = 1
LogOnly " Scanning " & iCompCnt & " components"
'Enum all Components
For Each ComponentID In oMsi.Components
'Progress bar
i = i + 1
If iProgress < (i / iCompCnt) * 100 Then
wscript.stdout.write "." : LogStream.Write "."
iProgress = iProgress + 1
If iProgress = 35 OR iProgress = 70 Then Log ""
End If
'Check if all ComponentClients will be removed
sCompClient = ""
iRemCnt = 0
fIsPermanent = False
fRemoveComponent = False 'Flag to track if the component will be completely removed
fAffectedComponent = False 'Flag to track if some clients remain installed who have a none shared location
dicCompPath.RemoveAll
For Each CompClient In oMsi.ComponentClients(ComponentID)
If Err = 0 Then
'Ensure valid guid length
If Len(CompClient) = 38 Then
sPath = ""
sPath = LCase(oMsi.ComponentPath(CompClient,ComponentID))
sPath = Replace(sPath,"?",":")
'Scan for msidbComponentAttributesPermanent flag
If CompClient = "{00000000-0000-0000-0000-000000000000}" Then
fIsPermanent = True
iRemCnt = iRemCnt + 1
End If
fRemoveComponent = Right(CompClient,PRODLEN)=OFFICEID
If fRemoveComponent Then fRemoveComponent = CheckDelete(CompClient)
If fRemoveComponent Then
iRemCnt = iRemCnt + 1
fAffectedComponent = True
'Since the scope remains within one Office family the keypath for the component
'is assumed to be identical
If sCompClient = "" Then sCompClient = CompClient
Else
If NOT dicCompPath.Exists(sPath) Then dicCompPath.Add sPath,CompClient
End If
Else
If NOT dicFLError.Exists("Error: Invalid metadata found. ComponentID: "&ComponentID &", ComponentClient: "&CompClient) Then _
dicFLError.Add "Error: Invalid metadata found. ComponentID: "&ComponentID &", ComponentClient: "&CompClient, ComponentID
End If '38
Else
Err.Clear
End If 'Err = 0
Next 'CompClient
'Determine if the component resources go away
sPath = ""
fRemoveComponent = fAffectedComponent AND (iRemCnt = oMsi.ComponentClients(ComponentID).Count)
If NOT fRemoveComponent AND fAffectedComponent Then
'Flag as removable if component has a unique keypath
sPath = LCase(oMsi.ComponentPath(sCompClient,ComponentID))
sPath = Replace(sPath,"?",":")
fRemoveComponent = NOT dicCompPath.Exists(sPath)
End If
If fRemoveComponent Then
'Check msidbComponentAttributesPermanent flag
If fIsPermanent AND NOT fForce Then fRemoveComponent = False
End If
If fRemoveComponent Then
'Component resources go away for this product
Err.Clear
'Add the component registration key to ensure removal
sCompReg = "Installer\Components\"&GetCompressedGuid(ComponentID)&"\"
If NOT dicDelRegKey.Exists(sCompReg) Then
dicDelRegKey.Add sCompReg,HKCR
RegList.WriteLine HiveString(HKCR)&"\"&sCompReg
End If
sCompReg = "SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components\"&GetCompressedGuid(ComponentID)&"\"
If NOT dicDelRegKey.Exists(sCompReg) Then
dicDelRegKey.Add sCompReg,HKLM
RegList.WriteLine HiveString(HKCR)&"\"&sCompReg
End If
'Get the component path
If sPath = "" Then
sPath = LCase(oMsi.ComponentPath(sCompClient,ComponentID))
sPath = Replace(sPath,"?",":")
End If
If Len(sPath) > 4 Then
If Left(sPath,1) = "0" Then
'Registry keypath
Select Case Left(sPath,2)
Case "00"
sPath = Mid(sPath,5)
hDefKey = HKCR
Case "01"
sPath = Mid(sPath,5)
hDefKey = HKCU
Case "02","22"
sPath = Mid(sPath,5)
hDefKey = HKLM
Case Else
'
End Select
If NOT dicDelRegKey.Exists(sPath) Then
dicDelRegKey.Add sPath,hDefKey
RegList.WriteLine HiveString(hDefKey)&"\"&sPath
End If
Else
'File
If oFso.FileExists(sPath) Then
sPath = oFso.GetFile(sPath).ParentFolder
If Not oFolderDic.Exists(sPath) Then oFolderDic.Add sPath,sPath
'Get the .msi file
If oFso.FileExists(sScrubDir & "\" & sCompClient & ".msi") Then
sMsiFile = sScrubDir & "\" & sCompClient & ".msi"
Else
sMsiFile = oMsi.ProductInfo(sCompClient,"LocalPackage")
End If
If Not Err = 0 Then
If NOT dicFLError.Exists("Failed to obtain .msi file for product "&sCompClient) Then _
dicFLError.Add "Failed to obtain .msi file for product "&sCompClient, ComponentID
Err.Clear
End If
Set MsiDb = oMsi.OpenDatabase(sMsiFile,MSIOPENDATABASEREADONLY)
If Err = 0 Then
'Get the component name from the 'Component' table
sQuery = "SELECT `Component`,`ComponentId` FROM Component WHERE `ComponentId` = '" & ComponentID &"'"
Set qView = MsiDb.OpenView(sQuery) : qView.Execute
Set Record = qView.Fetch()
If Not Record Is Nothing Then sComponent = Record.Stringdata(1)
'Get filenames from the 'File' table
sQuery = "SELECT `Component_`,`FileName` FROM File WHERE `Component_` = '" & sComponent &"'"
Set qView = MsiDb.OpenView(sQuery) : qView.Execute
Set Record = qView.Fetch()
Do Until Record Is Nothing
'Read the filename
sFile = Record.StringData(2)
If InStr(sFile,"|") > 0 Then sFile = Mid(sFile,InStr(sFile,"|")+1,Len(sFile))
'sFile = sPath & "\" & sFile
If Not oDic.Exists(sPath & "\" & sFile) Then
'Exception handler
fAdd = True
Select Case UCase(sFile)
Case "FPERSON.DLL"
'Catch exception caused by changed .msi keypath authoring logic for smart tags
For Each prod in oMsi.Products
If NOT Checkdelete(prod) Then
If oMsi.FeatureState(prod, "MSTagPluginNamesFiles") = MSIINSTALLSTATE_LOCAL Then
fAdd = False
Exit For
End If
End If
Next 'prod
Case Else
End Select
If fAdd Then
oDic.Add sPath & "\" & sFile,sFile
FileList.WriteLine sFile
If Len(sFile)>4 Then
sFile = LCase(sFile)
If Right(sFile,4) = ".exe" Then
If NOT dicApps.Exists(sFile) Then
Select Case sFile
Case "setup.exe","ose.exe","osppsvc.exe","explorer.exe","cvhsvc.exe","sftvsa.exe","sftlist.exe","sftplay.exe","sftvol.exe","sftfs.exe"
Case Else
dicApps.Add sFile,LCase(sPath) & "\" & sFile
End Select
End If 'dicApps.Exists
End If '.exe
End If 'Len > 4
End If 'fAdd
End If 'oDic.Exists
Set Record = qView.Fetch()
Loop
Set Record = Nothing
qView.Close
Set qView = Nothing
Else
If NOT dicFLError.Exists("Error: Could not read from .msi file: "&sMsiFile) Then _
dicFLError.Add "Error: Could not read from .msi file: "&sMsiFile, ComponentID
Err.Clear
End If 'Err = 0
End If 'FileExists(sPath)
End If
End If 'Len(sPath) > 4
Else
'Add the path to the 'Keep' dictionary
Err.Clear
For Each CompClient In oMsi.ComponentClients(ComponentID)
'Get the component path
sPath = "" : sPath = LCase(oMsi.ComponentPath(CompClient,ComponentID))
sPath = Replace(sPath,"?",":")
If Len(sPath) > 4 Then
If Left(sPath,1) = "0" Then
'Registry keypath
Select Case Left(sPath,2)
Case "00"
sPath = Mid(sPath,5)
hDefKey = HKCR
Case "01"
sPath = Mid(sPath,5)
hDefKey = HKCU
Case "02","22"
sPath = Mid(sPath,5)
hDefKey = HKLM
Case Else
'
End Select
If NOT dicKeepReg.Exists(sPath) Then
dicKeepReg.Add sPath,hDefKey
End If
Else
'File keypath
If oFso.FileExists(sPath) Then
sPath = LCase(oFso.GetFile(sPath).ParentFolder)
If Not dicKeepFolder.Exists(sPath) Then AddKeepFolder sPath
End If
End If 'Is Registry
End If 'sPath > 4
Next 'CompClient
End If 'fRemoveComponent
Next 'ComponentID
Err.Clear
On Error Goto 0
Log " Done" & vbCrLf
If dicFLError.Count > 0 Then LogOnly Join(dicFLError.Keys,vbCrLf)
If Not oFolderDic.Count = 0 Then arrDeleteFolders = oFolderDic.Keys Else Set arrDeleteFolders = Nothing
If Not oDic.Count = 0 Then arrDeleteFiles = oDic.Keys Else Set arrDeleteFiles = Nothing
End Sub 'ScanComponents
'=======================================================================================================
'Invoke msiexec to remove individual .MSI packages
Sub MsiexecRemoval
Dim Product
Dim i
Dim sCmd, sReturn
'Check MSI registered products
'Office System does only support per machine installation so it's sufficient to use Installer.Products
i = 0
For Each Product in oMsi.Products
If Len(Product) = 38 Then
If Right(Product,PRODLEN) = OFFICEID Then
If fRemoveAll OR CheckDelete(Product) Then
i = i + 1
Log " Calling msiexec.exe to remove " & Product
sCmd = "msiexec.exe /x" & Product & " REBOOT=ReallySuppress NOLOCALCACHEROLLBACK=1"
If fQuiet Then
sCmd = sCmd & " /q"
Else
sCmd = sCmd & " /qb-"
End If
sCmd = sCmd & " /l*v+ "&chr(34)&sLogDir&"\Uninstall_"&Product&".log"&chr(34)
If NOT fDetectOnly Then
Log " - Calling msiexec with '"&sCmd&"'"
'Execute the patch uninstall
sReturn = oWShell.Run(sCmd, 0, True)
Log " - msiexec returned: " & SetupRetVal(sReturn) & " (" & sReturn & ")" & vbCrLf
fRebootRequired = fRebootRequired OR (sReturn = "3010")
Else
Log " -> Removal suppressed in preview mode. Command: "&sCmd
End If
End If 'CheckDelete
End If 'OFFICEID
End If '38
Next 'Product
If i = 0 Then Log " Nothing to remove for msiexec"
End Sub 'MsiexecRemoval
'=======================================================================================================
'Remove the OSE (Office Source Engine) service
Sub RemoveOSE
On Error Resume Next
Log " OSE CleanUp"
DeleteService "ose"
'Delete the folder
DeleteFolder sCommonProgramFiles & "\Microsoft Shared\Source Engine"
'Delete the registration
RegDeleteKey HKLM,"SYSTEM\CurrentControlSet\Services\ose"
End Sub 'RemoveOSE
'=======================================================================================================
'Identify which parts of the LIS (MSOCache) will not be removed
Sub CheckLIS
Dim Prod
Dim sDownloadCode
If NOT dicKeepProd.Count > 0 Then Exit Sub
'Loop all products that remain installed
For Each Prod in dicKeepProd.Keys
If RegReadValue(HKLM,"SOFTWARE\Microsoft\Office\11.0\Delivery\"&Prod,"DownloadCode",sDownloadCode,"REG_SZ") Then
If dicKeepLis.Exists(UCase(sDownloadCode)) Then
dicKeepLis.Item(sDownloadCode) = dicKeepLis.Item(sDownloadCode)&","&UCase(Prod)
Else
dicKeepLis.Add UCase(sDownloadCode),UCase(Prod)
End If
End If
Next 'Prod
End Sub 'CheckLIS
'=======================================================================================================
'File cleanup operations for the Local Installation Source (MSOCache)
Sub WipeLIS
Const LISROOT = "MSOCache\All Users\"
Dim LogicalDisks, Disk, Folder, SubFolder, MseFolder, File, Files
Dim arrSubFolders
Dim sFolder
Dim fRemoveFolder
Log " LIS CleanUp"
'Check which parts of the local installation source has to remain
CheckLIS
'Search all hard disks
Set LogicalDisks = oWmiLocal.ExecQuery("Select * From Win32_LogicalDisk WHERE DriveType=3")
For Each Disk in LogicalDisks
If oFso.FolderExists(Disk.DeviceID & "\" & LISROOT) Then
Set Folder = oFso.GetFolder(Disk.DeviceID & "\" & LISROOT)
For Each Subfolder in Folder.Subfolders
If Len(Subfolder) > 37 Then
If InStr(UCase(Subfolder.Name)&"}",OFFICEID)>0 Then
If NOT dicKeepLis.Exists(UCase(Subfolder.Name)) Then DeleteFolder Subfolder.Path
ElseIf LCase(Subfolder.Name) = "microsoft.watson.alrtintl.data" OR _
LCase(Subfolder.Name) = "microsoft.watson.watsonrc.data" Then
If NOT dicKeepLis.Count > 0 Then DeleteFolder Subfolder.Path
End If
End If 'Len > 37
Next 'Subfolder
If (Folder.Subfolders.Count = 0) AND (Folder.Files.Count = 0) Then
sFolder = Folder.Path
Set Folder = Nothing
SmartDeleteFolder sFolder
End If
End If 'oFso.FolderExists
Next 'Disk
'MSECache
If EnumFolders(sProgramFiles,arrSubFolders) Then
For Each SubFolder in arrSubFolders
If UCase(Right(SubFolder,9))="\MSECACHE" Then
ReDim arrMseFolders(-1)
Set Folder = oFso.GetFolder(SubFolder)
GetMseFolderStructure Folder
For Each MseFolder in arrMseFolders
If oFso.FolderExists(MseFolder) Then
fRemoveFolder = False
Set Folder = oFso.GetFolder(MseFolder)
Set Files = Folder.Files
For Each File in Files
If (LCase(Right(File.Name,4))=".msi") Then
If CheckDelete(ProductCode(File.Path)) Then
fRemoveFolder = True
Exit For
End If 'CheckDelete
End If
Next 'File
Set Files = Nothing
Set Folder = Nothing
If fRemoveFolder Then SmartDeleteFolder MseFolder
End If 'oFso.FolderExists(MseFolder)
Next 'MseFolder
End If
Next 'SubFolder
End If 'oFso.FolderExists
End Sub 'WipeLis
'=======================================================================================================
'Wipe files and folders
Sub FileWipeAll
Dim sFolder
Dim Folder, Subfolder
If fForce OR fQuiet Then CloseOfficeApps
'Handle other services.
Select Case OVERSIONMAJOR
Case "11"
Case "12"
Case "14"
DeleteService "odserv"
DeleteService "Microsoft Office Groove Audit Service"
DeleteService "Microsoft SharePoint Workspace Audit Service"
Case Else
End Select
'User specific files
If NOT fKeepUser Then
'Delete files that should be backed up before deleting them
CopyAndDeleteFile sAppdata & "\Microsoft\Templates\Normal.dot"
End If
'Run the individual filewipe first
FileWipeIndividual
'Take care of the rest
DeleteFolder sOInstallRoot
DeleteFolder sCommonProgramFiles & "\Microsoft Shared\" & OREF
DeleteFile sAllUsersProfile & "\Application Data\Microsoft\Office\Data\opa"&OVERSIONMAJOR&".dat"
DeleteFile sAllUsersProfile & "\Microsoft\Office\Data\opa"&OVERSIONMAJOR&".dat"
If (fRemoveOspp OR fForce) AND CInt(OVERSIONMAJOR)>12 Then
DeleteService "osppsvc"
DeleteFolder sCommonProgramFiles & "\Microsoft Shared\OfficeSoftwareProtectionPlatform"
DeleteFolder sAllUsersProfile & "\Microsoft\OfficeSoftwareProtectionPlatform"
End If
Select Case OVERSIONMAJOR
Case "12"
Case "14"
DeleteFile oWShell.SpecialFolders("AllUsersStartup")&"\OfficeSAS.lnk"
DeleteFile oWShell.SpecialFolders("Startup")&"\OneNote 2010 Screen Clipper and Launcher.lnk"
Case Else
End Select
End Sub 'FileWipeAll
'=======================================================================================================
'Wipe individual files & folders related to SKU's that are no longer installed
Sub FileWipeIndividual
Dim LogicalDisks, Disk
Dim File, Files, XmlFile, scFiles, oFile, Folder, SubFolder, Processes, Process, item
Dim sFile, sFolder, sPath, sConfigName, sContents, sProductCode, sLocalDrives,sScQuery
Dim arrSubfolders
Dim fDeleteSC
Dim iRet
Log " File CleanUp"
If IsArray(arrDeleteFiles) Then
If fForce OR fQuiet Then
Log " Doing Action: StopOSE"
iRet = StopService("ose")
Set Processes = oWmiLocal.ExecQuery("Select * From Win32_Service Where Name like 'ose%.exe'")
For Each Process in Processes
LogOnly " - Running process : " & Process.Name
Log " -> Ending process: " & Process.Name
iRet = Process.Terminate()
Next 'Process
LogOnly " End Action: StopOSE"
CloseOfficeApps
End If
'Wipe individual files detected earlier
LogOnly " Removing left behind files"
For Each sFile in arrDeleteFiles
If oFso.FileExists(sFile) Then DeleteFile sFile
Next 'File
End If 'IsArray
'Wipe Shortcuts from local hard disks
If NOT fSkipSD Then
On Error Resume Next
Log " Searching for shortcuts. This can take some time ..."
Set LogicalDisks = oWmiLocal.ExecQuery("Select * From Win32_LogicalDisk WHERE DriveType=3")
For Each Disk in LogicalDisks
sLocalDrives = sLocalDrives & UCase(Disk.DeviceID) & "\;"
sScQuery = "Select * From Win32_ShortcutFile WHERE Drive='"&Disk.DeviceID&"'"
Set scFiles = oWmiLocal.ExecQuery(sScQuery)
For Each File in scFiles
fDeleteSC = False
'Compare if the shortcut target is in the list of executables that will be removed
If Len(File.Target)>0 Then
For Each item in dicApps.Items
If LCase(File.Target) = item Then
fDeleteSC = True
Exit For
End If
Next 'item
End If
'Handle Windows Installer shortcuts
If InStr(File.Target,"{")>0 Then
If Len(File.Target)>=InStr(File.Target,"{")+37 Then
If CheckDelete(Mid(File.Target,InStr(File.Target,"{"),38)) Then fDeleteSC = True
End If
End If
If fDeleteSC Then
If Not IsArray(arrDeleteFolders) Then ReDim arrDeleteFolders(0)
sFolder = Left(File.Description,InStrRev(File.Description,"\")-1)
If Not arrDeleteFolders(UBound(arrDeleteFolders)) = sFolder Then
ReDim Preserve arrDeleteFolders(UBound(arrDeleteFolders)+1)
arrDeleteFolders(UBound(arrDeleteFolders)) = sFolder
End If
DeleteFile File.Description
End If 'fDeleteSC
Next 'scFile
Next
On Error Goto 0
End If 'NOT SkipSD
Err.Clear
For Each Subfolder in oFso.GetFolder(sWICacheDir).SubFolders
If Len(Subfolder.Name) > 37 Then
If Right(Subfolder.Name,PRODLEN) = OFFICEID Then
If CheckDelete(Subfolder.Name) Then
DeleteFolder Subfolder.Path
End If
End If
End If 'Len > 37
Next 'Subfolder
End Sub 'FileWipeIndividual
'=======================================================================================================
Sub DelScrubTmp
On Error Resume Next
If oFso.FolderExists(sScrubDir & "\ScrubTmp") Then oFso.DeleteFolder sScrubDir & "\ScrubTmp",True
End Sub 'DelScrubTmp
'=======================================================================================================
'Ensure there are no unexpected .msi files in the scrub folder
Sub DeleteMsiScrubCache
Dim Folder, File, Files
Log " ScrubCache CleanUp"
Set Folder = oFso.GetFolder(sScrubDir) : CheckError "DeleteMsiScrubCache"
Set Files = Folder.Files
For Each File in Files
CheckError "DeleteMsiScrubCache"
If LCase(Right(File.Name,4))=".msi" Then
CheckError "DeleteMsiScrubCache"
DeleteFile File.Path : CheckError "DeleteMsiScrubCache"
End If
Next 'File
End Sub 'DeleteMsiScrubCache
'=======================================================================================================
Sub MsiClearOrphanedFiles
Const USERSIDEVERYONE = "s-1-1-0"
Const MSIINSTALLCONTEXT_ALL = 7
Const MSIPATCHSTATE_ALL = 15
'Error handling inlined
On Error Resume Next
Dim Patch, AllPatches, Product, AllProducts
Dim File, Files, Folder
Dim sFName, sLocalMsp, sLocalMsi, sPatchList, sMsiList
Set Folder = oFso.GetFolder(sWinDir & "\Installer")
Set Files = Folder.Files
Log " Windows Installer cache CleanUp"
'Get a complete list of patches
Err.Clear
Set AllPatches = oMsi.PatchesEx("",USERSIDEVERYONE,MSIINSTALLCONTEXT_ALL,MSIPATCHSTATE_ALL)
If Err <> 0 Then
CheckError "MsiClearOrphanedFiles (msp)"
Else
'Fill a comma separated stringlist with all .msp patchfiles
For Each Patch in AllPatches
sLocalMsp = "" : sLocalMsp = LCase(Patch.Patchproperty("LocalPackage")) : CheckError "MsiClearOrphanedFiles (msp)"
sPatchList = sPatchList & sLocalMsp & ","
Next 'Patch
'Delete all non referenced .msp files from %windir%\installer
For Each File in Files
sFName = "" : sFName = LCase(File.Path)
If LCase(Right(sFName,4)) = ".msp" Then
If Not InStr(sPatchList,sFName) > 0 Then
'While this is an orphaned file keep the scope of Office only
If InStr(UCase(MspTargets(File.Path)),OFFICEID)>0 Then DeleteFile File.Path
End If
End If 'LCase(Right(sFName,4))
Next 'File
End If 'Err=0
'Get a complete list products
Err.Clear
Set AllProducts = oMsi.ProductsEx("",USERSIDEVERYONE,MSIINSTALLCONTEXT_ALL)
If Err <> 0 Then
CheckError "MsiClearOrphanedFiles (msi)"
Else
'Fill a comma separated stringlist with all .msi files
For Each Product in AllProducts
sLocalMsi = "" : sLocalMsi = LCase(Product.InstallProperty("LocalPackage")) : CheckError "MsiClearOrphanedFiles (msi)"
sMsiList = sMsiList & sLocalMsi & ","
Next 'Product
'Delete all non referenced .msi files from %windir%\installer
For Each File in Files
sFName = "" : sFName = LCase(File.Path)
If LCase(Right(sFName,4)) = ".msi" Then
If Not InStr(sMsiList,sFName) > 0 Then
'While this is an orphaned file keep the scope of Office only
If UCase(Right(ProductCode(File.Path),PRODLEN))=OFFICEID Then DeleteFile File.Path
End If
End If 'LCase(Right(sFName,4)) = ".msi"
Next 'File
End If 'Err=0
End Sub 'MsiClearOrphanedFiles
'=======================================================================================================
Sub RegWipe
Dim Item, Name, Sku, key
Dim hDefKey, sSubKeyName, sCurKey, value, sValue, sGuid
Dim fkeep, fSystemComponent0, fPackages, fDisplayVersion
Dim arrKeys, arrNames, arrTypes, arrMultiSzValues, arrMultiSzNewValues
Dim i, iLoopCnt, iPos
Dim fDelReg
Log " Registry CleanUp"
'Wipe registry data
'User Profile settings
RegDeleteKey HKCU,"Software\Policies\Microsoft\Office\" & OVERSION
If NOT fKeepUser Then
RegDeleteKey HKCU,"Software\Microsoft\Office\" & OVERSION
End If 'fKeepUser
'Computer specific settings
If fRemoveAll Then
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Office\" & OVERSION
If fRemoveOse OR fForce Then
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Office Test"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Office\Common","LastAccessInstall"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Office\Common","MID"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Office\Excel\Addins\Microsoft.PerformancePoint.Planning.Client.Excel"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Office\InfoPath\Converters\Import\InfoPath.DesignerExcelImport\Versions",OVERSION
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Office\InfoPath\Converters\Import\InfoPath.DesignerWordImport\Versions",OVERSION
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Office\Outlook"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Shared Tools\Text Converters\Export\MEWord12"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Shared Tools\Text Converters\Export\Word12"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Shared Tools\Text Converters\Export\Word97"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Shared Tools\Text Converters\Import\MEWord12"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Shared Tools\Text Converters\Import\Word12"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Shared Tools\Text Converters\Import\Word97"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Run","GrooveMonitor"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Run","LobiServer"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Run","BCSSync"
RegDeleteKey HKLM,"SYSTEM\CurrentControlSet\Services\Outlook"
End If
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Office\Common\OffDiag\Location",OVERSIONMAJOR
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Terminal Server\Install\Software\Microsoft\Office\" & OVERSION
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Office\Common\OffDiag\Location",OVERSIONMAJOR
RegDeleteKey HKLM,"SOFTWARE\Microsoft\OfficeCustomizeWizard\" & OVERSION
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Terminal Server\Install\SOFTWARE\Microsoft\OfficeCustomizeWizard\" & OVERSION
Select Case OVERSIONMAJOR
Case "11"
'Jet_Replication
sValue = ""
If RegReadValue(HKCR,"CLSID\{CC2C83A6-9BE4-11D0-98E7-00C04FC2CAF5}\InprocServer32","SystemDB",sValue,"REG_SZ") Then
If Len(sValue) > Len(sOInstallRoot) Then
If LCase(Left(sValue,Len(sOInstallRoot))) = LCase(sOInstallRoot) Then RegDeleteKey HKCR,"CLSID\{CC2C83A6-9BE4-11D0-98E7-00C04FC2CAF5}\InprocServer32"
End If
End If
Case "12"
Case "14"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\OfficeSoftwareProtectionPlatform"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\OfficeSoftwareProtectionPlatform_Test"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Office\Common\ActiveX Compatibility\{00024512-0000-0000-C000-000000000046}"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Office\OneNote\Adapters","{456B0D0E-49DD-4C95-8DB6-175F54DE69A3}"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved","{42042206-2D85-11D3-8CFF-005004838597}"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved","{993BE281-6695-4BA5-8A2A-7AACBFAAB69E}"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved","{0006F045-0000-0000-C000-000000000046}"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved","{C41662BB-1FA0-4CE0-8DC5-9B7F8279FF97}"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved","{7CCA70DB-DE7A-4FB7-9B2B-52E2335A3B5A}"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved","{506F4668-F13E-4AA1-BB04-B43203AB3CC0}"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved","{D66DC78C-4F61-447F-942B-3FB6980118CF}"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Browser Helper Objects\{B4F3A835-0E21-4959-BA22-42B3008E02FF}"
'Groove Extensions
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellExecuteHooks","{B5A7F190-DDA6-4420-B3BA-52453494E6CD}"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved","{99FD978C-D287-4F50-827F-B2C658EDA8E7}"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved","{AB5C5600-7E6E-4B06-9197-9ECEF74D31CC}"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved","{920E6DB1-9907-4370-B3A0-BAFC03D81399}"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved","{16F3DD56-1AF5-4347-846D-7C10C4192619}"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved","{2916C86E-86A6-43FE-8112-43ABE6BF8DCC}"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved","{72853161-30C5-4D22-B7F9-0BBC1D38A37E}"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved","{6C467336-8281-4E60-8204-430CED96822D}"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved","{2A541AE1-5BF6-4665-A8A3-CFA9672E4291}"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved","{B5A7F190-DDA6-4420-B3BA-52453494E6CD}"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved","{A449600E-1DC6-4232-B948-9BD794D62056}"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved","{3D60EDA7-9AB4-4DA8-864C-D9B5F2E7281D}"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved","{387E725D-DC16-4D76-B310-2C93ED4752A0}"
RegDeleteKey HKLM,"SOFTWARE\Classes\*\shellex\ContextMenuHandlers\XXX Groove GFS Context Menu Handler XXX"
RegDeleteKey HKLM,"SOFTWARE\Classes\AllFilesystemObjects\shellex\ContextMenuHandlers\XXX Groove GFS Context Menu Handler XXX"
RegDeleteKey HKLM,"SOFTWARE\Classes\Directory\shellex\ContextMenuHandlers\XXX Groove GFS Context Menu Handler XXX"
RegDeleteKey HKLM,"SOFTWARE\Classes\Folder\ShellEx\ContextMenuHandlers\XXX Groove GFS Context Menu Handler XXX"
RegDeleteKey HKLM,"SOFTWARE\Classes\Directory\Background\shellex\ContextMenuHandlers\XXX Groove GFS Context Menu Handler XXX"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers\Groove Explorer Icon Overlay 1 (GFS Unread Stub)"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers\Groove Explorer Icon Overlay 2 (GFS Stub)"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers\Groove Explorer Icon Overlay 2.5 (GFS Unread Folder)"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers\Groove Explorer Icon Overlay 3 (GFS Folder)"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers\Groove Explorer Icon Overlay 4 (GFS Unread Mark)"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Browser Helper Objects\{72853161-30C5-4D22-B7F9-0BBC1D38A37E}"
Case Else
End Select
'Win32Assemblies
If RegEnumKey(HKCR,"Installer\Win32Assemblies\",arrKeys) Then
For Each Item in arrKeys
If InStr(UCase(Item),OREF)>0 Then RegDeleteKey HKCR,"Installer\Win32Assemblies\"&Item
Next 'Item
End If 'RegEnumKey
'Groove blocks reinstall if it locates groove.exe over this key
If RegKeyExists(HKCR,"GrooveFile\Shell\Open\Command\") Then
sValue = ""
RegReadValue HKCR,"GrooveFile\Shell\Open\Command\","",sValue,"REG_SZ"
If InStr(sValue,"\"&OREF&"\")>0 Then RegDeleteKey HKCR,"GrooveFile"
End If 'RegKeyExists
End If 'fRemoveAll
'Known Keypath settings
For Each key in dicDelRegKey.Keys
If Right(key,1) = "\" Then
RegDeleteKey dicDelRegKey.Item(key),key
Else
iPos = InStrRev(Key,"\")
If iPos > 0 Then RegDeleteValue dicDelRegKey.Item(key), Left(key,iPos - 1), Mid(key,iPos+1)
End If
Next
For iLoopCnt = 1 to 3
Select Case iLoopCnt
Case 1
'CIW - HKCU
sSubKeyName = "Software\Microsoft\OfficeCustomizeWizard\" & OVERSION & "\RegKeyPaths\"
hDefKey = HKCU
Case 2
'CIW - HKLM
sSubKeyName = "SOFTWARE\Microsoft\OfficeCustomizeWizard\" & OVERSION & "\RegKeyPaths\"
hDefKey = HKLM
Case 3
'Add/Remove Programs
sSubKeyName = REG_ARP
hDefKey = HKLM
End Select
If RegEnumKey(hDefKey,sSubKeyName,arrKeys) Then
For Each Item in arrKeys
'OFFICEID id
If Len(Item)>37 Then
sGuid = UCase(Left(Item,38))
If Right(sGuid,PRODLEN)=OFFICEID Then
If CheckDelete(sGuid) Then
RegDeleteKey hDefKey, sSubKeyName & Item
End If
End If 'Right(Item,PRODLEN)=OFFICEID
End If 'Len(Item)>37
Next 'Item
If iLoopCnt < 3 Then
If RegEnumValues(hDefKey,sSubKeyName,arrNames,arrTypes) Then
i = 0
For Each Name in arrNames
If RegReadValue(hDefKey,sSubKeyName,Name,sValue,arrTypes(i)) Then
If sValue = sGuid Then RegDeleteValue hDefKey,sSubKeyName,Name
End If
i = i + 1
Next
End If
End If
End If
If NOT RegEnumKey(hDefKey,sSubKeyName,arrKeys) Then RegDeleteKey hDefKey,"Software\Microsoft\OfficeCustomizeWizard\11.0\"
If NOT RegEnumKey(hDefKey,"Software\Microsoft\OfficeCustomizeWizard\11.0\",arrKeys) Then RegDeleteKey hDefKey,"Software\Microsoft\OfficeCustomizeWizard\"
Next 'iLoopCnt
'UpgradeCodes, WI config, WI global config
For iLoopCnt = 1 to 5
Select Case iLoopCnt
Case 1
sSubKeyName = "SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UpgradeCodes\"
hDefKey = HKLM
Case 2
sSubKeyName = "Installer\UpgradeCodes\"
hDefKey = HKCR
Case 3
sSubKeyName = "SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\"
hDefKey = HKLM
Case 4
sSubKeyName = "Installer\Features\"
hDefKey = HKCR
Case 5
sSubKeyName = "Installer\Products\"
hDefKey = HKCR
Case Else
sSubKeyName = ""
hDefKey = ""
End Select
If RegEnumKey(hDefKey,sSubKeyName,arrKeys) Then
For Each Item in arrKeys
'Ensure we have the expected length for a compressed GUID
If Len(Item)=32 Then
'Expand the GUID
sGuid = GetExpandedGuid(Item)
'Check if it's an Office key
If Right(sGuid,PRODLEN) = OFFICEID Then
If fRemoveAll Then
RegDeleteKey hDefKey,sSubKeyName & Item
Else
If iLoopCnt < 3 Then
'Enum all entries
RegEnumValues hDefKey,sSubKeyName & Item,arrNames,arrTypes
If IsArray(arrNames) Then
'Delete entries within removal scope
For Each Name in arrNames
If Len(Name)=32 Then
sGuid = GetExpandedGuid(Name)
If CheckDelete(sGuid) Then RegDeleteValue hDefKey, sSubKeyName & Item, Name
Else
'Invalid data -> delete the value
RegDeleteValue hDefKey, sSubKeyName & Item, Name
End If
Next 'Name
End If 'IsArray(arrNames)
'If all entries were removed - delete the key
RegEnumValues hDefKey,sSubKeyName & Item,arrNames,arrTypes
If Not IsArray(arrNames) Then RegDeleteKey hDefKey, sSubKeyName & Item
Else 'iLoopCnt >= 3
If CheckDelete(sGuid) Then RegDeleteKey hDefKey, sSubKeyName & Item
End If 'iLoopCnt < 3
End If 'fRemoveAll
End If 'Right(Item,PRODLEN)=OFFICEID
End If 'Len(Item)=32
Next 'Item
End If 'RegEnumKey
Next 'iLoopCnt
'Components
sSubKeyName = "SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components\"
If RegEnumKey(HKLM,sSubKeyName,arrKeys) Then
For Each Item in arrKeys
'Ensure we have the expected length for a compressed GUID
If Len(Item)=32 Then
If RegEnumValues(HKLM,sSubKeyName & Item,arrNames,arrTypes) Then
If IsArray(arrNames) Then
For Each Name in arrNames
If Len(Name)=32 Then
sGuid = GetExpandedGuid(Name)
If CheckDelete(sGuid) Then RegDeleteValue HKLM, sSubKeyName & Item, Name
End If '32
Next 'Name
End If 'IsArray
End If 'RegEnumValues
End If '32
Next 'Item
End If 'RegEnumKey
'Published Components
If fRemoveAll Then
sSubKeyName = "Installer\Components\"
If RegEnumKey(HKCR,sSubKeyName,arrKeys) Then
For Each Item in arrKeys
'Ensure we have the expected length for a compressed GUID
If Len(Item)=32 Then
If RegEnumValues(HKCR,sSubKeyName & Item,arrNames,arrTypes) Then
If IsArray(arrNames) Then
For Each Name in arrNames
If RegReadValue (HKCR,sSubKeyName & Item, Name, sValue,"REG_MULTI_SZ") Then
arrMultiSzValues = Split(sValue,chr(34))
If IsArray(arrMultiSzValues) Then
i = -1
ReDim arrMultiSzNewValues(-1)
For Each value in arrMultiSzValues
If Not InStr(value,SQOFFICEID)>0 Then
i = i + 1
ReDim Preserve arrMultiSzNewValues(i)
arrMultiSzNewValues(i) = value
End If
Next 'Value
If NOT (i = -1) Then
If NOT fDetectOnly Then
If NOT UBound(arrMultiSzValues) = i Then oReg.SetMultiStringValue HKCR,sSubKeyName & Item,Name,arrMultiSzNewValues
End If
Else
'Double check before we delete the entry
For Each value in arrMultiSzValues
fDelReg = InStr(value,SQOFFICEID)>0
If Not fDelReg Then Exit For
Next 'value
If fDelReg Then RegDeleteValue HKCR,sSubKeyName & Item, Name
End If
End If 'IsArray
End If
Next 'Name
End If 'IsArray
End If 'RegEnumValues
End If '32
Next 'Item
End If 'RegEnumKey
End If 'fRemoveAll
'Delivery
hDefKey = HKLM
sSubKeyName = "SOFTWARE\Microsoft\Office\Delivery\SourceEngine\Downloads\"
If RegEnumKey(HKLM,sSubKeyName,arrKeys) Then
For Each Item in arrKeys
If InStr(UCase(Item)&"}",OFFICEID)>0 Then
If NOT dicKeepLis.Exists(UCase(Item)) Then RegDeleteKey HKLM,sSubKeyName & Item
ElseIf LCase(Item) = "microsoft.watson.alrtintl.data" OR _
LCase(Item) = "microsoft.watson.watsonrc.data" Then
If NOT dicKeepLis.Count > 0 Then RegDeleteKey HKLM,sSubKeyName & Item
End If
Next 'Item
End If 'RegEnumKey
'Registration
hDefKey = HKLM
sSubKeyName = "SOFTWARE\Microsoft\Office\"&OVERSION&"\Registration\"
If RegEnumKey(HKLM,sSubKeyName,arrKeys) Then
For Each Item in arrKeys
If Len(Item)>37 Then
If CheckDelete(UCase(Left(Item,38))) Then RegDeleteKey HKLM,sSubKeyName & Item
End If
Next 'Item
End If 'RegEnumKey
End Sub 'RegWipeAll
'=======================================================================================================
'=======================================================================================================
' Helper Functions
'=======================================================================================================
'End all running instances of applications that will be removed
Sub CloseOfficeApps
Dim Processes, Process
Dim iRet
Log " Doing Action: CloseOfficeApps"
Set Processes = oWmiLocal.ExecQuery("Select * From Win32_Process")
For Each Process in Processes
If dicApps.Exists(LCase(Process.Name)) Then
Log " - End process " & Process.Name
iRet = Process.Terminate()
CheckError "CloseOfficeApps: " & "Process.Name"
End If
Next 'Process
LogOnly " End Action: CloseOfficeApps"
End Sub 'CloseOfficeApps
'=======================================================================================================
'Ensure Windows Explorer is restarted if needed
Sub RestoreExplorer
Dim Processes
'Non critical routine. Don't fail on error
On Error Resume Next
wscript.sleep 1000
Set Processes = oWmiLocal.ExecQuery("Select * From Win32_Process Where Name='explorer.exe'")
If Processes.Count < 1 Then oWShell.Run "explorer.exe"
End Sub 'RestoreExploer
'=======================================================================================================
'Check registry access permissions. Failure will terminate the script
Function CheckRegPermissions
Const KEY_QUERY_VALUE = &H0001
Const KEY_SET_VALUE = &H0002
Const KEY_CREATE_SUB_KEY = &H0004
Const DELETE = &H00010000
Dim sSubKeyName
Dim fReturn
CheckRegPermissions = True
sSubKeyName = "Software\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\"
oReg.CheckAccess HKLM, sSubKeyName, KEY_QUERY_VALUE, fReturn
If Not fReturn Then CheckRegPermissions = False
oReg.CheckAccess HKLM, sSubKeyName, KEY_SET_VALUE, fReturn
If Not fReturn Then CheckRegPermissions = False
oReg.CheckAccess HKLM, sSubKeyName, KEY_CREATE_SUB_KEY, fReturn
If Not fReturn Then CheckRegPermissions = False
oReg.CheckAccess HKLM, sSubKeyName, DELETE, fReturn
If Not fReturn Then CheckRegPermissions = False
End Function 'CheckRegPermissions
'=======================================================================================================
'Check if an Office product is still registered with a SKU that stays on the computer
Function CheckDelete(sProductCode)
'Ensure valid GUID length
If NOT Len(sProductCode) = 38 Then
CheckDelete = False
Exit Function
End If
'If it's a non Office ProductCode exit with false right away
CheckDelete = Right(sProductCode,PRODLEN) = OFFICEID
If Not CheckDelete Then Exit Function
If dicKeepProd.Exists(UCase(sProductCode)) Then CheckDelete = False
End Function 'CheckDelete
'=======================================================================================================
'Register an orphaned .msi product as installed for MSI
Sub MsiRegisterProduct (sMsiFile)
Dim sDisplayVersion, sCurKey, sDisplayName, sLang, sProductCode, sTmpKey
Dim iCnt
'Create a temporary keys to simulate an installed product
sProductCode = ""
sProductCode = GetMsiProductCode(sMsiFile)
sDisplayVersion = GetMsiProductVersion(sMsiFile)
If sDisplayVersion = "" Then sDisplayVersion = OVERSION & ".0000.0000"
sDisplayName = GetMsiProductName(sMsiFile)
If sDisplayName = "" Then sDisplayName = sProductCode
Select Case OVERSIONMAJOR
Case "9","10","11"
sLang = CInt("&h" & Mid(sProductCode,6,4))
Case "12","14"
sLang = CInt("&h" & Mid(sProductCode,16,4))
Case Else
End Select
For iCnt = 1 To 3
Select Case iCnt
Case 1
sCurKey = REG_ARP & sProductCode
oReg.CreateKey HKLM,sCurKey
Case 2
sCurKey = "SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\" & GetCompressedGuid(sProductCode)
oReg.CreateKey HKLM,sCurKey
oReg.CreateKey HKLM,sCurKey & "\Features"
oReg.CreateKey HKLM,sCurKey & "\InstallProperties"
oReg.CreateKey HKLM,sCurKey & "\Patches"
oReg.CreateKey HKLM,sCurKey & "\Usage"
sCurKey = sCurKey & "\InstallProperties"
oReg.SetStringValue HKLM,sCurKey,"LocalPackage",sMsiFile
Case 3
sCurKey = "Installer\Products\" & GetCompressedGuid(sProductCode)
sTmpKey = sCurKey
oReg.CreateKey HKCR,sCurKey
oReg.SetDWordValue HKCR,sCurKey,"AdvertiseFlags",388
oReg.SetDWordValue HKCR,sCurKey,"Assignment",1
oReg.SetDWordValue HKCR,sCurKey,"AuthorizedLUAApp",0
oReg.SetStringValue HKCR,sCurKey,"Clients",":"
oReg.SetDWordValue HKCR,sCurKey,"DeploymentFlags",3
oReg.SetDWordValue HKCR,sCurKey,"InstanceType",0
oReg.SetDWordValue HKCR,sCurKey,"Language",sLang
oReg.SetStringValue HKCR,sCurKey,"PackageCode",GetMsiPackageCode(sMsiFile)
oReg.SetStringValue HKCR,sCurKey,"ProductName",sDisplayName
oReg.SetDWordValue HKCR,sCurKey,"VersionMinor",0
sCurKey = sTmpKey & "\SourceList"
oReg.CreateKey HKCR,sCurKey
oReg.SetExpandedStringValue HKCR,sCurKey,"LastUsedSource",sScrubDir
oReg.SetStringValue HKCR,sCurKey,"PackageName",Mid(sMsiFile,InstrRev(sMsiFile,"\")+1)
sCurKey = sTmpKey & "\SourceList\Media"
oReg.CreateKey HKCR,sCurKey
oReg.SetStringValue HKCR,sCurKey,"1",OREF & ";1"
oReg.SetStringValue HKCR,sCurKey,"DiskPrompt",sDisplayName
sCurKey = sTmpKey & "\SourceList\Net"
oReg.CreateKey HKCR,sCurKey
oReg.SetExpandedStringValue HKCR,sCurKey,"1",sScrubDir
Case Else
End Select
If iCnt <3 Then
oReg.SetStringValue HKLM,sCurKey,"Comments",""
oReg.SetStringValue HKLM,sCurKey,"Contact",""
oReg.SetStringValue HKLM,sCurKey,"DisplayName",sDisplayName
oReg.SetStringValue HKLM,sCurKey,"DisplayVersion",sDisplayVersion
oReg.SetDWordValue HKLM,sCurKey,"EstimatedSize",0
oReg.SetStringValue HKLM,sCurKey,"HelpLink",""
oReg.SetStringValue HKLM,sCurKey,"HelpTelephone",""
oReg.SetStringValue HKLM,sCurKey,"InstallDate","20100101"
If f64 Then
oReg.SetStringValue HKLM,sCurKey,"InstallLocation",sProgramFilesX86
Else
oReg.SetStringValue HKLM,sCurKey,"InstallLocation",sProgramFiles
End If
oReg.SetStringValue HKLM,sCurKey,"InstallSource",sScrubDir
oReg.SetDWordValue HKLM,sCurKey,"Language",sLang
oReg.SetExpandedStringValue HKLM,sCurKey,"ModifyPath","MsiExec.exe /X" & sProductCode
oReg.SetDWordValue HKLM,sCurKey,"NoModify",1
oReg.SetStringValue HKLM,sCurKey,"Publisher","Microsoft Corporation"
oReg.SetStringValue HKLM,sCurKey,"Readme",""
oReg.SetStringValue HKLM,sCurKey,"Size",""
oReg.SetDWordValue HKLM,sCurKey,"SystemComponent",0
oReg.SetExpandedStringValue HKLM,sCurKey,"UninstallString","MsiExec.exe /X" & sProductCode
oReg.SetStringValue HKLM,sCurKey,"URLInfoAbout",""
oReg.SetStringValue HKLM,sCurKey,"URLUpdateInfo",""
oReg.SetDWordValue HKLM,sCurKey,"Version",0
oReg.SetDWordValue HKLM,sCurKey,"VersionMajor",OVERSIONMAJOR
oReg.SetDWordValue HKLM,sCurKey,"VersionMinor",0
oReg.SetDWordValue HKLM,sCurKey,"WindowsInstaller",1
End If '< 3
Next 'iCnt
End Sub 'MsiRegisterProduct
'=======================================================================================================
'Obtain the ProductCode (GUID) from a .msi package
'The function will open the .msi database and query the 'Property' table to retrieve the ProductCode
Function GetMsiProductCode(sMsiFile)
Dim MsiDb,Record
Dim qView
On Error Resume Next
GetMsiProductCode = ""
Set Record = Nothing
Set MsiDb = oMsi.OpenDatabase(sMsiFile,MSIOPENDATABASEREADONLY)
Set qView = MsiDb.OpenView("SELECT `Value` FROM Property WHERE `Property` = 'ProductCode'")
qView.Execute
Set Record = qView.Fetch
GetMsiProductCode = Record.StringData(1)
qView.Close
End Function 'GetMsiProductCode
'=======================================================================================================
'Obtain the ProductVersion from a .msi package
'The function will open the .msi database and query the 'Property' table to retrieve the ProductCode
Function GetMsiProductVersion(sMsiFile)
Dim MsiDb,Record
Dim qView
On Error Resume Next
GetMsiProductVersion = ""
Set Record = Nothing
Set MsiDb = oMsi.OpenDatabase(sMsiFile,MSIOPENDATABASEREADONLY)
Set qView = MsiDb.OpenView("SELECT `Value` FROM Property WHERE `Property` = 'ProductVersion'")
qView.Execute
Set Record = qView.Fetch
GetMsiProductVersion = Record.StringData(1)
qView.Close
End Function 'GetMsiProductVersion
'=======================================================================================================
'Obtain the ProductVersion from a .msi package
'The function will open the .msi database and query the 'Property' table to retrieve the ProductCode
Function GetMsiProductName(sMsiFile)
Dim MsiDb,Record
Dim qView
On Error Resume Next
GetMsiProductName = ""
Set Record = Nothing
Set MsiDb = oMsi.OpenDatabase(sMsiFile,MSIOPENDATABASEREADONLY)
Set qView = MsiDb.OpenView("SELECT `Value` FROM Property WHERE `Property` = 'ProductName'")
qView.Execute
Set Record = qView.Fetch
GetMsiProductName = Record.StringData(1)
qView.Close
End Function 'GetMsiProductVersion
'=======================================================================================================
'Obtain the PackageCode (GUID) from a .msi package
'The function will the .msi'S SummaryInformation stream
Function GetMsiPackageCode(sMsiFile)
On Error Resume Next
Const PID_REVNUMBER = 9
GetMsiPackageCode = ""
GetMsiPackageCode = GetCompressedGuid(oMsi.SummaryInformation(sMsiFile,MSIOPENDATABASEREADONLY).Property(PID_REVNUMBER))
End Function 'GetMsiPackageCode
'=======================================================================================================
'Returns a string with a list of ProductCodes from the summary information stream
Function MspTargets (sMspFile)
Const MSIOPENDATABASEMODE_PATCHFILE = 32
Const PID_TEMPLATE = 7
Dim Msp
'Non critical routine. Don't fail on error
On Error Resume Next
MspTargets = ""
If oFso.FileExists(sMspFile) Then
Set Msp = Msi.OpenDatabase(WScript.Arguments(0),MSIOPENDATABASEMODE_PATCHFILE)
If Err = 0 Then MspTargets = Msp.SummaryInformation.Property(PID_TEMPLATE)
End If 'oFso.FileExists(sMspFile)
End Function 'MspTargets
'=======================================================================================================
'Return the ProductCode {GUID} from a .MSI package
Function ProductCode(sMsi)
Const MSIUILEVELNONE = 2 'No UI
Dim MsiSession
On Error Resume Next
'Non critical routine. Don't fail on error
If oFso.FileExists(sMsi) Then
oMsi.UILevel = MSIUILEVELNONE
Set MsiSession = oMsi.OpenPackage(sMsi,1)
ProductCode = MsiSession.ProductProperty("ProductCode")
Set MsiSession = Nothing
Else
ProductCode = ""
End If 'oFso.FileExists(sMsi)
End Function 'ProductCode
'=======================================================================================================
Function GetExpandedGuid (sGuid)
Dim i
'Ensure valid length
If NOT Len(sGuid) = 32 Then Exit Function
GetExpandedGuid = "{" & StrReverse(Mid(sGuid,1,8)) & "-" & _
StrReverse(Mid(sGuid,9,4)) & "-" & _
StrReverse(Mid(sGuid,13,4))& "-"
For i = 17 To 20
If i Mod 2 Then
GetExpandedGuid = GetExpandedGuid & mid(sGuid,(i + 1),1)
Else
GetExpandedGuid = GetExpandedGuid & mid(sGuid,(i - 1),1)
End If
Next
GetExpandedGuid = GetExpandedGuid & "-"
For i = 21 To 32
If i Mod 2 Then
GetExpandedGuid = GetExpandedGuid & mid(sGuid,(i + 1),1)
Else
GetExpandedGuid = GetExpandedGuid & mid(sGuid,(i - 1),1)
End If
Next
GetExpandedGuid = GetExpandedGuid & "}"
End Function
'=======================================================================================================
'Converts a GUID into the compressed format
Function GetCompressedGuid (sGuid)
Dim sCompGUID
Dim i
'Ensure Valid Length
If NOT Len(sGuid) = 38 Then Exit Function
sCompGUID = StrReverse(Mid(sGuid,2,8)) & _
StrReverse(Mid(sGuid,11,4)) & _
StrReverse(Mid(sGuid,16,4))
For i = 21 To 24
If i Mod 2 Then
sCompGUID = sCompGUID & Mid(sGuid, (i + 1), 1)
Else
sCompGUID = sCompGUID & Mid(sGuid, (i - 1), 1)
End If
Next
For i = 26 To 37
If i Mod 2 Then
sCompGUID = sCompGUID & Mid(sGuid, (i - 1), 1)
Else
sCompGUID = sCompGUID & Mid(sGuid, (i + 1), 1)
End If
Next
GetCompressedGuid = sCompGUID
End Function
'=======================================================================================================
'Ensures that only valid metadata entries exist to avoid API failures
Sub EnsureValidWIMetadata (hDefKey,sKey,iValidLength)
Dim arrKeys
Dim SubKey
If Len(sKey) > 1 Then
If Right(sKey,1) = "\" Then sKey = Left(sKey,Len(sKey)-1)
End If
If RegEnumKey(hDefKey,sKey,arrKeys) Then
For Each SubKey in arrKeys
If NOT Len(SubKey) = iValidLength Then
RegDeleteKey hDefKey,sKey & "\" & SubKey
End If
Next 'SubKey
End If
End Sub 'EnsureValidWIMetadata
'=======================================================================================================
'Create a backup copy of the file in the ScrubDir then delete the file
Sub CopyAndDeleteFile(sFile)
Dim File
'Error handling inlined
On Error Resume Next
If oFso.FileExists(sFile) Then
Set File = oFso.GetFile(sFile)
If Not oFso.FolderExists(sScrubDir & "\" & File.ParentFolder.Name) Then oFso.CreateFolder sScrubDir & "\" & File.ParentFolder.Name
If Not fDetectOnly Then
LogOnly " - Backing up file: " & sFile
oFso.CopyFile sFile,sScrubDir & "\" & File.ParentFolder.Name & "\" & File.Name,True : CheckError "CopyAndDeleteFile"
Set File = Nothing
DeleteFile(sFile)
Else
LogOnly " - Simulate CopyAndDelete file: " & sFile
End If
End If 'oFso.FileExists
End Sub 'CopyAndDeleteFile
'=======================================================================================================
'Wrapper to delete a file
Sub DeleteFile(sFile)
Dim File
Dim sFileName, sNewPath
On Error Resume Next
If oFso.FileExists(sFile) Then
LogOnly " - Delete file: " & sFile
If Not fDetectOnly Then oFso.DeleteFile sFile,True
If Err <> 0 Then
CheckError "DeleteFile"
If fForce Then
'Try to move the file and delete from there
Set File = oFso.GetFile(sFile)
sFileName = File.Name
sNewPath = sScrubDir & "\ScrubTmp"
Set File = Nothing
If Not oFso.FolderExists(sNewPath) Then oFso.CreateFolder(sNewPath)
'Move the file
LogOnly " - Move file to: " & sNewPath & "\" & sFileName
oFso.MoveFile sFile,sNewPath & "\" & sFileName
If Err <> 0 Then
CheckError "DeleteFile (move)"
End If 'Err <> 0
End If 'fForce
End If 'Err <> 0
End If 'oFso.FileExists
End Sub 'DeleteFile
'=======================================================================================================
'64 bit aware wrapper to return the requested folder
Function GetFolderPath(sPath)
GetFolderPath = True
If oFso.FolderExists(sPath) Then Exit Function
If f64 AND oFso.FolderExists(Wow64Folder(sPath)) Then
sPath = Wow64Folder(sPath)
Exit Function
End If
GetFolderPath = False
End Function 'GetFolderPath
'=======================================================================================================
'Enumerates subfolder names of a folder and returns True if subfolders exist
Function EnumFolderNames (sFolder, arrSubFolders)
Dim Folder, Subfolder
Dim sSubFolders
If oFso.FolderExists(sFolder) Then
Set Folder = oFso.GetFolder(sFolder)
For Each Subfolder in Folder.Subfolders
sSubFolders = sSubFolders & Subfolder.Name & ","
Next 'Subfolder
End If
If f64 AND oFso.FolderExists(Wow64Folder(sFolder)) Then
Set Folder = oFso.GetFolder(Wow64Folder(sFolder))
For Each Subfolder in Folder.Subfolders
sSubFolders = sSubFolders & Subfolder.Name & ","
Next 'Subfolder
End If
If Len(sSubFolders)>0 Then arrSubFolders = RemoveDuplicates(Split(Left(sSubFolders,Len(sSubFolders)-1),","))
EnumFolderNames = Len(sSubFolders)>0
End Function 'EnumFolderNames
'=======================================================================================================
'Enumerates subfolders of a folder and returns True if subfolders exist
Function EnumFolders (sFolder, arrSubFolders)
Dim Folder, Subfolder
Dim sSubFolders
If oFso.FolderExists(sFolder) Then
Set Folder = oFso.GetFolder(sFolder)
For Each Subfolder in Folder.Subfolders
sSubFolders = sSubFolders & Subfolder.Path & ","
Next 'Subfolder
End If
If f64 AND oFso.FolderExists(Wow64Folder(sFolder)) Then
Set Folder = oFso.GetFolder(Wow64Folder(sFolder))
For Each Subfolder in Folder.Subfolders
sSubFolders = sSubFolders & Subfolder.Path & ","
Next 'Subfolder
End If
If Len(sSubFolders)>0 Then arrSubFolders = RemoveDuplicates(Split(Left(sSubFolders,Len(sSubFolders)-1),","))
EnumFolders = Len(sSubFolders)>0
End Function 'EnumFolders
'=======================================================================================================
Sub GetMseFolderStructure (Folder)
Dim SubFolder
For Each SubFolder in Folder.SubFolders
ReDim Preserve arrMseFolders(UBound(arrMseFolders)+1)
arrMseFolders(UBound(arrMseFolders)) = SubFolder.Path
GetMseFolderStructure SubFolder
Next 'SubFolder
End Sub 'GetMseFolderStructure
'=======================================================================================================
'Wrapper to delete a folder
Sub DeleteFolder(sFolder)
Dim Folder
Dim sDelFolder, sFolderName, sNewPath
If dicKeepFolder.Exists(LCase(sFolder)) Then Exit Sub
If f64 Then
If dicKeepFolder.Exists(LCase(Wow64Folder(sFolder))) Then Exit Sub
End If
If Len(sFolder) > 1 Then
If Right(sFolder,1) = "\" Then sFolder = Left(sFolder,Len(sFolder)-1)
End If
On Error Resume Next
If oFso.FolderExists(sFolder) Then
sDelFolder = sFolder
ElseIf f64 AND oFso.FolderExists(Wow64Folder(sFolder)) Then
sDelFolder = Wow64Folder(sFolder)
Else
Exit Sub
End If
If Not fDetectOnly Then
LogOnly " - Delete folder: " & sDelFolder
oFso.DeleteFolder sDelFolder,True
Else
LogOnly " - Simulate delete folder: " & sDelFolder
End If
If Err <> 0 Then
CheckError "DeleteFolder"
'Try to move the folder and delete from there
Set Folder = oFso.GetFolder(sDelFolder)
sFolderName = Folder.Name
sNewPath = sScrubDir & "\ScrubTmp"
Set Folder = Nothing
'Ensure we stay within the same drive
If Not oFso.FolderExists(sNewPath) Then oFso.CreateFolder(sNewPath)
'Move the folder
LogOnly " - Moving folder to: " & sNewPath & "\" & sFolderName
oFso.MoveFolder sFolder,sNewPath & "\" & sFolderName
If Err <> 0 Then
CheckError "DeleteFolder (move)"
End If 'Err <> 0
End If 'Err <> 0
End Sub 'DeleteFolder
'=======================================================================================================
'Delete empty folder structures
Sub DeleteEmptyFolders
Dim Folder
Dim sFolder
If Not IsArray(arrDeleteFolders) Then Exit Sub
Log " Empty Folder Cleanup"
For Each sFolder in arrDeleteFolders
If oFso.FolderExists(sFolder) Then
Set Folder = oFso.GetFolder(sFolder)
If (Folder.Subfolders.Count = 0) AND (Folder.Files.Count = 0) Then
Set Folder = Nothing
SmartDeleteFolder sFolder
End If
End If
Next 'sFolder
End Sub 'DeleteEmptyFolders
'=======================================================================================================
'Wrapper to delete a folder and remove the empty parent folder structure
Sub SmartDeleteFolder(sFolder)
If oFso.FolderExists(sFolder) Then
If Not fDetectOnly Then
LogOnly "Request SmartDelete for folder: " & sFolder
SmartDeleteFolderEx sFolder
Else
LogOnly "Simulate request SmartDelete for folder: " & sFolder
End If
End If
If f64 AND oFso.FolderExists(Wow64Folder(sFolder)) Then
If Not fDetectOnly Then
LogOnly "Request SmartDelete for folder: " & Wow64Folder(sFolder)
SmartDeleteFolderEx Wow64Folder(sFolder)
Else
LogOnly "Simulate request SmartDelete for folder: " & Wow64Folder(sFolder)
End If
End If
End Sub 'SmartDeleteFolder
'=======================================================================================================
'Executes the folder delete operation
Sub SmartDeleteFolderEx(sFolder)
Dim Folder
On Error Resume Next
DeleteFolder sFolder : CheckError "SmartDeleteFolderEx"
On Error Goto 0
Set Folder = oFso.GetFolder(oFso.GetParentFolderName(sFolder))
If (Folder.Subfolders.Count = 0) AND (Folder.Files.Count = 0) Then SmartDeleteFolderEx(Folder.Path)
End Sub 'SmartDeleteFolderEx
'=======================================================================================================
'Adds the folder structure to the 'KeepFolder' dictionary
Sub AddKeepFolder(sPath)
Dim Folder
If NOT dicKeepFolder.Exists (sPath) Then
dicKeepFolder.Add sPath,sPath
End If
sPath = LCase(oFso.GetParentFolderName(sPath))
If oFso.FolderExists(sPath) Then AddKeepFolder(sPath)
End Sub
'=======================================================================================================
'Handles additional folder-path operations on 64 bit environments
Function Wow64Folder(sFolder)
If LCase(Left(sFolder,Len(sWinDir & "\System32"))) = LCase(sWinDir & "\System32") Then
Wow64Folder = sWinDir & "\syswow64" & Right(sFolder,Len(sFolder)-Len(sSys32Dir))
ElseIf LCase(Left(sFolder,Len(sProgramFiles))) = LCase(sProgramFiles) Then
Wow64Folder = sProgramFilesX86 & Right(sFolder,Len(sFolder)-Len(sProgramFiles))
Else
Wow64Folder = "?" 'Return invalid string to ensure the folder cannot exist
End If
End Function 'Wow64Folder
'=======================================================================================================
Function HiveString(hDefKey)
On Error Resume Next
Select Case hDefKey
Case HKCR : HiveString = "HKEY_CLASSES_ROOT"
Case HKCU : HiveString = "HKEY_CURRENT_USER"
Case HKLM : HiveString = "HKEY_LOCAL_MACHINE"
Case HKU : HiveString = "HKEY_USERS"
Case Else : HiveString = hDefKey
End Select
End Function
'=======================================================================================================
Function RegKeyExists(hDefKey,sSubKeyName)
Dim arrKeys
RegKeyExists = False
If oReg.EnumKey(hDefKey,sSubKeyName,arrKeys) = 0 Then RegKeyExists = True
End Function
'=======================================================================================================
Function RegValExists(hDefKey,sSubKeyName,sName)
Dim arrValueTypes, arrValueNames
Dim i
RegValExists = False
If Not RegKeyExists(hDefKey,sSubKeyName) Then Exit Function
If oReg.EnumValues(hDefKey,sSubKeyName,arrValueNames,arrValueTypes) = 0 AND IsArray(arrValueNames) Then
For i = 0 To UBound(arrValueNames)
If LCase(arrValueNames(i)) = Trim(LCase(sName)) Then RegValExists = True
Next
End If 'oReg.EnumValues
End Function
'=======================================================================================================
'Read the value of a given registry entry
Function RegReadValue(hDefKey, sSubKeyName, sName, sValue, sType)
Dim RetVal
Dim Item
Dim arrValues
Select Case UCase(sType)
Case "1","REG_SZ"
RetVal = oReg.GetStringValue(hDefKey,sSubKeyName,sName,sValue)
If Not RetVal = 0 AND f64 Then RetVal = oReg.GetStringValue(hDefKey,Wow64Key(hDefKey, sSubKeyName),sName,sValue)
Case "2","REG_EXPAND_SZ"
RetVal = oReg.GetExpandedStringValue(hDefKey,sSubKeyName,sName,sValue)
If Not RetVal = 0 AND f64 Then RetVal = oReg.GetExpandedStringValue(hDefKey,Wow64Key(hDefKey, sSubKeyName),sName,sValue)
Case "7","REG_MULTI_SZ"
RetVal = oReg.GetMultiStringValue(hDefKey,sSubKeyName,sName,arrValues)
If Not RetVal = 0 AND f64 Then RetVal = oReg.GetMultiStringValue(hDefKey,Wow64Key(hDefKey, sSubKeyName),sName,arrValues)
If RetVal = 0 Then sValue = Join(arrValues,chr(34))
Case "4","REG_DWORD"
RetVal = oReg.GetDWORDValue(hDefKey,sSubKeyName,sName,sValue)
If Not RetVal = 0 AND f64 Then
RetVal = oReg.GetDWORDValue(hDefKey,Wow64Key(hDefKey, sSubKeyName),sName,sValue)
End If
Case "3","REG_BINARY"
RetVal = oReg.GetBinaryValue(hDefKey,sSubKeyName,sName,sValue)
If Not RetVal = 0 AND f64 Then RetVal = oReg.GetBinaryValue(hDefKey,Wow64Key(hDefKey, sSubKeyName),sName,sValue)
Case "11","REG_QWORD"
RetVal = oReg.GetQWORDValue(hDefKey,sSubKeyName,sName,sValue)
If Not RetVal = 0 AND f64 Then RetVal = oReg.GetQWORDValue(hDefKey,Wow64Key(hDefKey, sSubKeyName),sName,sValue)
Case Else
RetVal = -1
End Select 'sValue
RegReadValue = (RetVal = 0)
End Function 'RegReadValue
'=======================================================================================================
'Enumerate a registry key to return all values
Function RegEnumValues(hDefKey,sSubKeyName,arrNames, arrTypes)
Dim RetVal, RetVal64
Dim arrNames32, arrNames64, arrTypes32, arrTypes64
If f64 Then
RetVal = oReg.EnumValues(hDefKey,sSubKeyName,arrNames32,arrTypes32)
RetVal64 = oReg.EnumValues(hDefKey,Wow64Key(hDefKey, sSubKeyName),arrNames64,arrTypes64)
If (RetVal = 0) AND (Not RetVal64 = 0) AND IsArray(arrNames32) AND IsArray(arrTypes32) Then
arrNames = arrNames32
arrTypes = arrTypes32
End If
If (Not RetVal = 0) AND (RetVal64 = 0) AND IsArray(arrNames64) AND IsArray(arrTypes64) Then
arrNames = arrNames64
arrTypes = arrTypes64
End If
If (RetVal = 0) AND (RetVal64 = 0) AND IsArray(arrNames32) AND IsArray(arrNames64) AND IsArray(arrTypes32) AND IsArray(arrTypes64) Then
arrNames = RemoveDuplicates(Split((Join(arrNames32,"\") & "\" & Join(arrNames64,"\")),"\"))
arrTypes = RemoveDuplicates(Split((Join(arrTypes32,"\") & "\" & Join(arrTypes64,"\")),"\"))
End If
Else
RetVal = oReg.EnumValues(hDefKey,sSubKeyName,arrNames,arrTypes)
End If 'f64
RegEnumValues = ((RetVal = 0) OR (RetVal64 = 0)) AND IsArray(arrNames) AND IsArray(arrTypes)
End Function 'RegEnumValues
'=======================================================================================================
'Enumerate a registry key to return all subkeys
Function RegEnumKey(hDefKey,sSubKeyName,arrKeys)
Dim RetVal, RetVal64
Dim arrKeys32, arrKeys64
If f64 Then
RetVal = oReg.EnumKey(hDefKey,sSubKeyName,arrKeys32)
RetVal64 = oReg.EnumKey(hDefKey,Wow64Key(hDefKey, sSubKeyName),arrKeys64)
If (RetVal = 0) AND (Not RetVal64 = 0) AND IsArray(arrKeys32) Then arrKeys = arrKeys32
If (Not RetVal = 0) AND (RetVal64 = 0) AND IsArray(arrKeys64) Then arrKeys = arrKeys64
If (RetVal = 0) AND (RetVal64 = 0) Then
If IsArray(arrKeys32) AND IsArray (arrKeys64) Then
arrKeys = RemoveDuplicates(Split((Join(arrKeys32,"\") & "\" & Join(arrKeys64,"\")),"\"))
ElseIf IsArray(arrKeys64) Then
arrKeys = arrKeys64
Else
arrKeys = arrKeys32
End If
End If
Else
RetVal = oReg.EnumKey(hDefKey,sSubKeyName,arrKeys)
End If 'f64
RegEnumKey = ((RetVal = 0) OR (RetVal64 = 0)) AND IsArray(arrKeys)
End Function 'RegEnumKey
'=======================================================================================================
'Wrapper around oReg.DeleteValue to handle 64 bit
Sub RegDeleteValue(hDefKey, sSubKeyName, sName)
Dim sWow64Key
Dim iRetVal
If dicKeepReg.Exists(LCase(sSubKeyName & sName)) Then Exit Sub
If f64 Then
If dicKeepReg.Exists(LCase(Wow64Key(hDefKey, sSubKeyName) & sName)) Then Exit Sub
End If
If RegValExists(hDefKey,sSubKeyName,sName) Then
On Error Resume Next
If Not fDetectOnly Then
LogOnly " - Delete registry value: " & HiveString(hDefKey) & "\" & sSubKeyName & " -> " & sName
iRetVal = 0
iRetVal = oReg.DeleteValue(hDefKey, sSubKeyName, sName)
CheckError "RegDeleteValue"
If NOT (iRetVal=0) Then LogOnly " Delete failed. Return value: "&iRetVal
Else
LogOnly " - Simulate delete registry value: " & HiveString(hDefKey) & "\" & sSubKeyName & " -> " & sName
End If
On Error Goto 0
End If 'RegValExists
If f64 Then
sWow64Key = Wow64Key(hDefKey, sSubKeyName)
If RegValExists(hDefKey,sWow64Key,sName) Then
On Error Resume Next
If Not fDetectOnly Then
LogOnly " - Delete registry value: " & HiveString(hDefKey) & "\" & sWow64Key & " -> " & sName
iRetVal = 0
iRetVal = oReg.DeleteValue(hDefKey, sWow64Key, sName)
CheckError "RegDeleteValue"
If NOT (iRetVal=0) Then LogOnly " Delete failed. Return value: "&iRetVal
Else
LogOnly " - Simulate delete registry value: " & HiveString(hDefKey) & "\" & sWow64Key & " -> " & sName
End If
On Error Goto 0
End If 'RegKeyExists
End If
End Sub 'RegDeleteValue
'=======================================================================================================
'Wrappper around RegDeleteKeyEx to handle 64bit scenrios
Sub RegDeleteKey(hDefKey, sSubKeyName)
Dim sWow64Key
If dicKeepReg.Exists(LCase(sSubKeyName)) Then Exit Sub
If f64 Then
If dicKeepReg.Exists(LCase(Wow64Key(hDefKey, sSubKeyName))) Then Exit Sub
End If
If Len(sSubKeyName) > 1 Then
If Right(sSubKeyName,1) = "\" Then sSubKeyName = Left(sSubKeyName,Len(sSubKeyName)-1)
End If
If RegKeyExists(hDefKey, sSubKeyName) Then
If Not fDetectOnly Then
LogOnly " - Delete registry key: " & HiveString(hDefKey) & "\" & sSubKeyName
On Error Resume Next
RegDeleteKeyEx hDefKey, sSubKeyName
On Error Goto 0
Else
LogOnly " - Simulate delete registry key: " & HiveString(hDefKey) & "\" & sSubKeyName
End If
End If 'RegKeyExists
If f64 Then
sWow64Key = Wow64Key(hDefKey, sSubKeyName)
If RegKeyExists(hDefKey,sWow64Key) Then
If Not fDetectOnly Then
LogOnly " - Delete registry key: " & HiveString(hDefKey) & "\" & sWow64Key
On Error Resume Next
RegDeleteKeyEx hDefKey, sWow64Key
On Error Goto 0
Else
LogOnly " - Simulate delete registry key: " & HiveString(hDefKey) & "\" & sWow64Key
End If
End If 'RegKeyExists
End If
End Sub 'RegDeleteKey
'=======================================================================================================
'Recursively delete a registry structure
Sub RegDeleteKeyEx(hDefKey, sSubKeyName)
Dim arrSubkeys
Dim sSubkey
Dim iRetVal
On Error Resume Next
oReg.EnumKey hDefKey, sSubKeyName, arrSubkeys
If IsArray(arrSubkeys) Then
For Each sSubkey In arrSubkeys
RegDeleteKeyEx hDefKey, sSubKeyName & "\" & sSubkey
Next
End If
If Not fDetectOnly Then
iRetVal = 0
iRetVal = oReg.DeleteKey(hDefKey,sSubKeyName)
If NOT (iRetVal=0) Then LogOnly " Delete failed. Return value: "&iRetVal
End If
End Sub 'RegDeleteKeyEx
'=======================================================================================================
'Return the alternate regkey location on 64bit environment
Function Wow64Key(hDefKey, sSubKeyName)
Dim iPos
Select Case hDefKey
Case HKCU
If Left(sSubKeyName,17) = "Software\Classes\" Then
Wow64Key = Left(sSubKeyName,17) & "Wow6432Node\" & Right(sSubKeyName,Len(sSubKeyName)-17)
Else
iPos = InStr(sSubKeyName,"\")
Wow64Key = Left(sSubKeyName,iPos) & "Wow6432Node\" & Right(sSubKeyName,Len(sSubKeyName)-iPos)
End If
Case HKLM
If Left(sSubKeyName,17) = "Software\Classes\" Then
Wow64Key = Left(sSubKeyName,17) & "Wow6432Node\" & Right(sSubKeyName,Len(sSubKeyName)-17)
Else
iPos = InStr(sSubKeyName,"\")
Wow64Key = Left(sSubKeyName,iPos) & "Wow6432Node\" & Right(sSubKeyName,Len(sSubKeyName)-iPos)
End If
Case Else
Wow64Key = "Wow6432Node\" & sSubKeyName
End Select 'hDefKey
End Function 'Wow64Key
'=======================================================================================================
'Remove duplicate entries from a one dimensional array
Function RemoveDuplicates(Array)
Dim Item
Dim oDic
Set oDic = CreateObject("Scripting.Dictionary")
For Each Item in Array
If Not oDic.Exists(Item) Then oDic.Add Item,Item
Next 'Item
RemoveDuplicates = oDic.Keys
End Function 'RemoveDuplicates
'=======================================================================================================
'Uses WMI to stop a service
Function StopService(sService)
Dim Services, Service
Dim sQuery
Dim iRet
On Error Resume Next
iRet = 0
sQuery = "Select * From Win32_Service Where Name='" & sService & "'"
Set Services = oWmiLocal.Execquery(sQuery)
'Stop the service
For Each Service in Services
If UCase(Service.State) = "STARTED" Then iRet = Service.StopService
If UCase(Service.State) = "RUNNING" Then iRet = Service.StopService
Next 'Service
StopService = (iRet = 0)
End Function 'StopService
'=======================================================================================================
'Delete a service
Sub DeleteService(sService)
Dim Services, Service, Processes, Process
Dim sQuery, sStates
Dim iRet
On Error Resume Next
sStates = "STARTED;RUNNING"
sQuery = "Select * From Win32_Service Where Name='" & sService & "'"
Set Services = oWmiLocal.Execquery(sQuery)
'Stop and delete the service
For Each Service in Services
Log " Found service " & sService & " in state " & Service.State
If InStr(sStates,UCase(Service.State))>0 Then iRet = Service.StopService()
'Ensure no more instances of the service are running
Set Processes = oWmiLocal.ExecQuery("Select * From Win32_Process Where Name='" & sService & ".exe'")
For Each Process in Processes
iRet = Process.Terminate()
Next 'Process
If Not fDetectOnly Then
Log " - Deleting Service -> " & sService
iRet = Service.Delete()
Else
Log " - Simulate deleting Service -> " & sService
End If
Next 'Service
Set Services = Nothing
Err.Clear
End Sub 'DeleteService
'=======================================================================================================
'Translation for setup.exe error codes
Function SetupRetVal(RetVal)
Select Case RetVal
Case 0 : SetupRetVal = "Success"
Case 30001,1 : SetupRetVal = "AbstractMethod"
Case 30002,2 : SetupRetVal = "ApiProhibited"
Case 30003,3 : SetupRetVal = "AlreadyImpersonatingAUser"
Case 30004,4 : SetupRetVal = "AlreadyInitialized"
Case 30005,5 : SetupRetVal = "ArgumentNullException"
Case 30006,6 : SetupRetVal = "AssertionFailed"
Case 30007,7 : SetupRetVal = "CABFileAddFailed"
Case 30008,8 : SetupRetVal = "CommandFailed"
Case 30009,9 : SetupRetVal = "ConcatenationFailed"
Case 30010,10 : SetupRetVal = "CopyFailed"
Case 30011,11 : SetupRetVal = "CreateEventFailed"
Case 30012,12 : SetupRetVal = "CustomizationPatchNotFound"
Case 30013,13 : SetupRetVal = "CustomizationPatchNotApplicable"
Case 30014,14 : SetupRetVal = "DuplicateDefinition"
Case 30015,15 : SetupRetVal = "ErrorCodeOnly - Passthrough for Win32 error"
Case 30016,16 : SetupRetVal = "ExceptionNotThrown"
Case 30017,17 : SetupRetVal = "FailedToImpersonateUser"
Case 30018,18 : SetupRetVal = "FailedToInitializeFlexDataSource"
Case 30019,19 : SetupRetVal = "FailedToStartClassFactories"
Case 30020,20 : SetupRetVal = "FileNotFound"
Case 30021,21 : SetupRetVal = "FileNotOpen"
Case 30022,22 : SetupRetVal = "FlexDialogAlreadyInitialized"
Case 30023,23 : SetupRetVal = "HResultOnly - Passthrough for HRESULT errors"
Case 30024,24 : SetupRetVal = "HWNDNotFound"
Case 30025,25 : SetupRetVal = "IncompatibleCacheAction"
Case 30026,26 : SetupRetVal = "IncompleteProductAddOns"
Case 30027,27 : SetupRetVal = "InstalledProductStateCorrupt"
Case 30028,28 : SetupRetVal = "InsufficientBuffer"
Case 30029,29 : SetupRetVal = "InvalidArgument"
Case 30030,30 : SetupRetVal = "InvalidCDKey"
Case 30031,31 : SetupRetVal = "InvalidColumnType"
Case 30032,31 : SetupRetVal = "InvalidConfigAddLanguage"
Case 30033,33 : SetupRetVal = "InvalidData"
Case 30034,34 : SetupRetVal = "InvalidDirectory"
Case 30035,35 : SetupRetVal = "InvalidFormat"
Case 30036,36 : SetupRetVal = "InvalidInitialization"
Case 30037,37 : SetupRetVal = "InvalidMethod"
Case 30038,38 : SetupRetVal = "InvalidOperation"
Case 30039,39 : SetupRetVal = "InvalidParameter"
Case 30040,40 : SetupRetVal = "InvalidProductFromARP"
Case 30041,41 : SetupRetVal = "InvalidProductInConfigXml"
Case 30042,42 : SetupRetVal = "InvalidReference"
Case 30043,43 : SetupRetVal = "InvalidRegistryValueType"
Case 30044,44 : SetupRetVal = "InvalidXMLProperty"
Case 30045,45 : SetupRetVal = "InvalidMetadataFile"
Case 30046,46 : SetupRetVal = "LogNotInitialized"
Case 30047,47 : SetupRetVal = "LogAlreadyInitialized"
Case 30048,48 : SetupRetVal = "MissingXMLNode"
Case 30049,49 : SetupRetVal = "MsiTableNotFound"
Case 30050,50 : SetupRetVal = "MsiAPICallFailure"
Case 30051,51 : SetupRetVal = "NodeNotOfTypeElement"
Case 30052,52 : SetupRetVal = "NoMoreGraceBoots"
Case 30053,53 : SetupRetVal = "NoProductsFound"
Case 30054,54 : SetupRetVal = "NoSupportedCulture"
Case 30055,55 : SetupRetVal = "NotYetImplemented"
Case 30056,56 : SetupRetVal = "NotAvailableCulture"
Case 30057,57 : SetupRetVal = "NotCustomizationPatch"
Case 30058,58 : SetupRetVal = "NullReference"
Case 30059,59 : SetupRetVal = "OCTPatchForbidden"
Case 30060,60 : SetupRetVal = "OCTWrongMSIDll"
Case 30061,61 : SetupRetVal = "OutOfBoundsIndex"
Case 30062,62 : SetupRetVal = "OutOfDiskSpace"
Case 30063,63 : SetupRetVal = "OutOfMemory"
Case 30064,64 : SetupRetVal = "OutOfRange"
Case 30065,65 : SetupRetVal = "PatchApplicationFailure"
Case 30066,66 : SetupRetVal = "PreReqCheckFailure"
Case 30067,67 : SetupRetVal = "ProcessAlreadyStarted"
Case 30068,68 : SetupRetVal = "ProcessNotStarted"
Case 30069,69 : SetupRetVal = "ProcessNotFinished"
Case 30070,70 : SetupRetVal = "ProductAlreadyDefined"
Case 30071,71 : SetupRetVal = "ResourceAlreadyTracked"
Case 30072,72 : SetupRetVal = "ResourceNotFound"
Case 30073,73 : SetupRetVal = "ResourceNotTracked"
Case 30074,74 : SetupRetVal = "SQLAlreadyConnected"
Case 30075,75 : SetupRetVal = "SQLFailedToAllocateHandle"
Case 30076,76 : SetupRetVal = "SQLFailedToConnect"
Case 30077,77 : SetupRetVal = "SQLFailedToExecuteStatement"
Case 30078,78 : SetupRetVal = "SQLFailedToRetrieveData"
Case 30079,79 : SetupRetVal = "SQLFailedToSetAttribute"
Case 30080,80 : SetupRetVal = "StorageNotCreated"
Case 30081,81 : SetupRetVal = "StreamNameTooLong"
Case 30082,82 : SetupRetVal = "SystemError"
Case 30083,83 : SetupRetVal = "ThreadAlreadyStarted"
Case 30084,84 : SetupRetVal = "ThreadNotStarted"
Case 30085,85 : SetupRetVal = "ThreadNotFinished"
Case 30086,86 : SetupRetVal = "TooManyProducts"
Case 30087,87 : SetupRetVal = "UnexpectedXMLNodeType"
Case 30088,88 : SetupRetVal = "UnexpectedError"
Case 30089,89 : SetupRetVal = "Unitialized"
Case 30090,90 : SetupRetVal = "UserCancel"
Case 30091,91 : SetupRetVal = "ExternalCommandFailed"
Case 30092,92 : SetupRetVal = "SPDatabaseOverSize"
Case 30093,93 : SetupRetVal = "IntegerTruncation"
'msiexec return values
Case 1259 : SetupRetVal = "APPHELP_BLOCK"
Case 1601 : SetupRetVal = "INSTALL_SERVICE_FAILURE"
Case 1602 : SetupRetVal = "INSTALL_USEREXIT"
Case 1603 : SetupRetVal = "INSTALL_FAILURE"
Case 1604 : SetupRetVal = "INSTALL_SUSPEND"
Case 1605 : SetupRetVal = "UNKNOWN_PRODUCT"
Case 1606 : SetupRetVal = "UNKNOWN_FEATURE"
Case 1607 : SetupRetVal = "UNKNOWN_COMPONENT"
Case 1608 : SetupRetVal = "UNKNOWN_PROPERTY"
Case 1609 : SetupRetVal = "INVALID_HANDLE_STATE"
Case 1610 : SetupRetVal = "BAD_CONFIGURATION"
Case 1611 : SetupRetVal = "INDEX_ABSENT"
Case 1612 : SetupRetVal = "INSTALL_SOURCE_ABSENT"
Case 1613 : SetupRetVal = "INSTALL_PACKAGE_VERSION"
Case 1614 : SetupRetVal = "PRODUCT_UNINSTALLED"
Case 1615 : SetupRetVal = "BAD_QUERY_SYNTAX"
Case 1616 : SetupRetVal = "INVALID_FIELD"
Case 1618 : SetupRetVal = "INSTALL_ALREADY_RUNNING"
Case 1619 : SetupRetVal = "INSTALL_PACKAGE_OPEN_FAILED"
Case 1620 : SetupRetVal = "INSTALL_PACKAGE_INVALID"
Case 1621 : SetupRetVal = "INSTALL_UI_FAILURE"
Case 1622 : SetupRetVal = "INSTALL_LOG_FAILURE"
Case 1623 : SetupRetVal = "INSTALL_LANGUAGE_UNSUPPORTED"
Case 1624 : SetupRetVal = "INSTALL_TRANSFORM_FAILURE"
Case 1625 : SetupRetVal = "INSTALL_PACKAGE_REJECTED"
Case 1626 : SetupRetVal = "FUNCTION_NOT_CALLED"
Case 1627 : SetupRetVal = "FUNCTION_FAILED"
Case 1628 : SetupRetVal = "INVALID_TABLE"
Case 1629 : SetupRetVal = "DATATYPE_MISMATCH"
Case 1630 : SetupRetVal = "UNSUPPORTED_TYPE"
Case 1631 : SetupRetVal = "CREATE_FAILED"
Case 1632 : SetupRetVal = "INSTALL_TEMP_UNWRITABLE"
Case 1633 : SetupRetVal = "INSTALL_PLATFORM_UNSUPPORTED"
Case 1634 : SetupRetVal = "INSTALL_NOTUSED"
Case 1635 : SetupRetVal = "PATCH_PACKAGE_OPEN_FAILED"
Case 1636 : SetupRetVal = "PATCH_PACKAGE_INVALID"
Case 1637 : SetupRetVal = "PATCH_PACKAGE_UNSUPPORTED"
Case 1638 : SetupRetVal = "PRODUCT_VERSION"
Case 1639 : SetupRetVal = "INVALID_COMMAND_LINE"
Case 1640 : SetupRetVal = "INSTALL_REMOTE_DISALLOWED"
Case 1641 : SetupRetVal = "SUCCESS_REBOOT_INITIATED"
Case 1642 : SetupRetVal = "PATCH_TARGET_NOT_FOUND"
Case 1643 : SetupRetVal = "PATCH_PACKAGE_REJECTED"
Case 1644 : SetupRetVal = "INSTALL_TRANSFORM_REJECTED"
Case 1645 : SetupRetVal = "INSTALL_REMOTE_PROHIBITED"
Case 1646 : SetupRetVal = "PATCH_REMOVAL_UNSUPPORTED"
Case 1647 : SetupRetVal = "UNKNOWN_PATCH"
Case 1648 : SetupRetVal = "PATCH_NO_SEQUENCE"
Case 1649 : SetupRetVal = "PATCH_REMOVAL_DISALLOWED"
Case 1650 : SetupRetVal = "INVALID_PATCH_XML"
Case 3010 : SetupRetVal = "SUCCESS_REBOOT_REQUIRED"
Case Else : SetupRetVal = "Unknown Return Value"
End Select
End Function 'SetupRetVal
'=======================================================================================================
Function GetProductID(sProdID)
Dim sReturn
Select Case sProdId
Case "11" : sReturn = "PRO"
Case "12" : sReturn = "STANDARD"
Case "13" : sReturn = "BASIC"
Case "14" : sReturn = "WSS2"
Case "15" : sReturn = "Access"
Case "16" : sReturn = "Excel"
Case "17" : sReturn = "FrontPage"
Case "18" : sReturn = "PowerPoint"
Case "19" : sReturn = "Publisher"
Case "1A" : sReturn = "Outlook"
Case "1B" : sReturn = "Word"
Case "1C" : sReturn = "AccessRuntime"
Case "1E" : sReturn = "OfficeMUI"
Case "1F" : sReturn = "PTK"
Case "23" : sReturn = "OfficeMUI"
Case "24" : sReturn = "ORK"
Case "26" : sReturn = "XPWebComponents"
Case "2E" : sReturn = "OSSSDK"
Case "32" : sReturn = "PrjSrv"
Case "33" : sReturn = "PERSONAL"
Case "3A" : sReturn = "PrjStd"
Case "3B" : sReturn = "PrjPro"
Case "3C" : sReturn = "PrjMUI"
Case "44" : sReturn = "InfoPath"
Case "48" : sReturn = "InfoPathVSToolkit"
Case "49" : sReturn = "PIA"
Case "51" : sReturn = "VisPro"
Case "52" : sReturn = "VisView"
Case "53" : sReturn = "VisStd"
Case "55" : sReturn = "VisEA"
Case "5E" : sReturn = "VisMUI"
Case "83" : sReturn = "HtmlView"
Case "84" : sReturn = "XLView"
Case "85" : sReturn = "WDView"
Case "92" : sReturn = "WSS2Pack"
Case "93" : sReturn = "OWP&C"
Case "A1" : sReturn = "OneNote"
Case "A4" : sReturn = "OWC"
Case "A5" : sReturn = "WSSMig"
Case "A9" : sReturn = "InterConnect"
Case "AA" : sReturn = "PPTCast"
Case "AB" : sReturn = "PPTPack1"
Case "AC" : sReturn = "PPTPack2"
Case "AD" : sReturn = "PPTPack3"
Case "AE" : sReturn = "OrgChart"
Case "CA" : sReturn = "SmallBusiness"
Case "D0" : sReturn = "AccessDE"
Case "DC" : sReturn = "SmartDocSDK"
Case "E0" : sReturn = "Outlook"
Case "E3" : sReturn = "PROPLUS"
Case "F7" : sReturn = "InfoPathVST"
Case "F8" : sReturn = "RHDTool"
Case "FD" : sReturn = "Outlook"
Case "FF" : sReturn = "LIP"
Case Else : sReturn = sProdID
End Select 'sProdId
GetProductID = sReturn
End Function 'GetProductID
'=======================================================================================================
Sub Log (sLog)
wscript.echo sLog
LogStream.WriteLine sLog
End Sub 'Log
'=======================================================================================================
Sub LogOnly (sLog)
LogStream.WriteLine sLog
End Sub 'Log
'=======================================================================================================
Sub CheckError(sModule)
If Err <> 0 Then
LogOnly " " & Now & " - " & sModule & " - Source: " & Err.Source & "; Err# (Hex): " & Hex( Err ) & _
"; Err# (Dec): " & Err & "; Description : " & Err.Description
End If 'Err = 0
Err.Clear
End Sub
'=======================================================================================================
'Command line parser
Sub ParseCmdLine
Dim iCnt, iArgCnt
Dim arrArguments
Dim sArg0
iArgCnt = Wscript.Arguments.Count
If iArgCnt = 0 Then
Select Case UCase(wscript.ScriptName)
' Case UCase(SCRIPTNAME)&"_WAC.VBS"
' ReDim arrArguments(0)
Case Else
'Create the log
CreateLog
Log "No argument specified. Preparing user prompt" & vbCrLf
FindInstalledOProducts
If dicInstalledSku.Count > 0 Then sDefault = Join(RemoveDuplicates(dicInstalledSku.Items),",") Else sDefault = "CLIENTALL"
sDefault = InputBox("Enter a list of " & ONAME & " products to remove" & vbCrLf & vbCrLf & _
"Examples:" & vbCrLf & _
"CLIENTALL" & vbTab & "-> all Client products" & vbCrLf & _
"SERVER" & vbTab & "-> all Server products" & vbCrLf & _
"ALL" & vbTab & vbTab & "-> all Server & Client products" & vbCrLf & _
"ProPlus,PrjPro" & vbTab & "-> ProPlus and Project" & vbCrLf &_
"?" & vbTab & vbTab & "-> display Help", _
SCRIPTFILE & " - " & ONAME & " remover", _
sDefault)
If IsEmpty(sDefault) Then 'User cancelled
Log "User cancelled. CleanUp & Exit."
'Undo temporary entries created in ARP
TmpKeyCleanUp
wscript.quit 1602
End If 'IsEmpty(sDefault)
Log "Answer from prompt: " & sDefault & vbCrLf
sDefault = Trim(UCase(Trim(Replace(sDefault,Chr(34),""))))
arrArguments = Split(Trim(sDefault)," ")
If UBound(arrArguments) = -1 Then ReDim arrArguments(0)
End Select
Else
ReDim arrArguments(iArgCnt-1)
For iCnt = 0 To (iArgCnt-1)
arrArguments(iCnt) = UCase(Wscript.Arguments(iCnt))
Next 'iCnt
End If 'iArgCnt = 0
'Handle the SKU list
sArg0 = Replace(arrArguments(0),"/","")
sArg0 = Replace(sArg0,"-","")
Select Case UCase(sArg0)
Case "?"
ShowSyntax
Case "ALL"
fRemoveAll = True
fRemoveOse = False
Case "CLIENTSUITES"
fRemoveCSuites = True
fRemoveOse = False
Case "CLIENTSTANDALONE"
fRemoveCSingle = True
fRemoveOse = False
Case "CLIENTALL"
fRemoveCSuites = True
fRemoveCSingle = True
fRemoveOse = False
Case "SERVER"
fRemoveServer = True
fRemoveOse = False
Case "ALL,OSE"
fRemoveAll = True
fRemoveOse = True
Case Else
fRemoveAll = False
fRemoveOse = False
sSkuRemoveList = sArg0
End Select
For iCnt = 0 To UBound(arrArguments)
Select Case arrArguments(iCnt)
Case "?","/?","-?"
ShowSyntax
Case "/B","/BYPASS"
If UBound(arrArguments)>iCnt Then
If InStr(arrArguments(iCnt+1),"1")>0 Then fBypass_Stage1 = True
If InStr(arrArguments(iCnt+1),"2")>0 Then fBypass_Stage2 = True
If InStr(arrArguments(iCnt+1),"3")>0 Then fBypass_Stage3 = True
End If
Case "/D","/DELETEUSERSETTINGS"
fKeepUser = False
Case "/FR","/FASTREMOVE"
fBypass_Stage1 = True
fSkipSD = True
Case "/F","/FORCE"
fForce = True
Case "/K","/KEEPUSERSETTINGS"
fKeepUser = True
Case "/L","/LOG"
fLogInitialized = False
If UBound(arrArguments)>iCnt Then
If oFso.FolderExists(arrArguments(iCnt+1)) Then
sLogDir = arrArguments(iCnt+1)
Else
On Error Resume Next
oFso.CreateFolder(arrArguments(iCnt+1))
If Err <> 0 Then sLogDir = sScrubDir Else sLogDir = arrArguments(iCnt+1)
End If
End If
Case "/N","/NOCANCEL"
fNoCancel = True
Case "/O","/OSE"
fRemoveOse = True
Case "/P","/PREVIEW","/DETECTONLY"
fDetectOnly = True
Case "/Q","/QUIET"
fQuiet = True
Case "/S","/SKIPSD","/SKIPSHORTCUSTDETECTION"
fSkipSD = True
Case "/R","/RECONCILE"
fTryReconcile = True
Case Else
End Select
Next 'iCnt
If Not fLogInitialized Then CreateLog
End Sub 'ParseCmdLine
'=======================================================================================================
Sub CreateLog
Dim DateTime
Dim sLogName
On Error Resume Next
'Create the log file
Set DateTime = CreateObject("WbemScripting.SWbemDateTime")
DateTime.SetVarDate Now,True
sLogName = sLogDir & "\" & oWShell.ExpandEnvironmentStrings("%COMPUTERNAME%")
sLogName = sLogName & "_" & Left(DateTime.Value,14)
sLogName = sLogName & "_ScrubLog.txt"
Err.Clear
Set LogStream = oFso.CreateTextFile(sLogName,True,True)
If Err <> 0 Then
Err.Clear
sLogDir = sScrubDir
sLogName = sLogDir & "\" & oWShell.ExpandEnvironmentStrings("%COMPUTERNAME%")
sLogName = sLogName & "_" & Left(DateTime.Value,14)
sLogName = sLogName & "_ScrubLog.txt"
Set LogStream = oFso.CreateTextFile(sLogName,True,True)
End If
Log "Microsoft Customer Support Services - " & ONAME & " Removal Utility" & vbCrLf & vbCrLf & _
"Version: " & SCRIPTVERSION & vbCrLf & _
"64 bit OS: " & f64 & vbCrLf & _
"Start removal: " & Now & vbCrLf
fLogInitialized = True
End Sub 'CreateLog
'=======================================================================================================
Sub RelaunchAsCScript
Dim Argument
Dim sCmdLine
sCmdLine = "cmd.exe /k " & WScript.Path & "\cscript.exe //NOLOGO " & Chr(34) & WScript.scriptFullName & Chr(34)
If Wscript.Arguments.Count > 0 Then
For Each Argument in Wscript.Arguments
sCmdLine = sCmdLine & " " & chr(34) & Argument & chr(34)
Next 'Argument
End If
oWShell.Run sCmdLine,1,False
Wscript.Quit
End Sub 'RelaunchAsCScript
'=======================================================================================================
Sub RelaunchElevated
Dim Argument
Dim sCmdLine
Dim oShell
Set oShell = CreateObject("Shell.Application")
sCmdLine = Chr(34) & WScript.scriptFullName & Chr(34)
If Wscript.Arguments.Count > 0 Then
For Each Argument in Wscript.Arguments
If Argument = "UAC" Then Exit Sub
sCmdLine = sCmdLine & " " & chr(34) & Argument & chr(34)
Next 'Argument
End If
oShell.ShellExecute "cscript.exe", sCmdLine & " UAC", "", "runas", 1
Wscript.Quit
End Sub 'RelaunchElevated
'=======================================================================================================
'Show the expected syntax for the script usage
Sub ShowSyntax
Wscript.Echo sErr & vbCrLf & _
SCRIPTFILE & " V " & SCRIPTVERSION & vbCrLf & _
"Copyright (c) Microsoft Corporation. All Rights Reserved" & vbCrLf & vbCrLf & _
SCRIPTFILE & " helps to remove " & ONAME & " Server & Client products" & vbCrLf & _
"when a regular uninstall is no longer possible" & vbCrLf & vbCrLf & _
"Usage:" & vbTab & SCRIPTFILE & " [List of config ProductIDs] [Options]" & vbCrLf & vbCrLf & _
vbTab & "/? ' Displays this help"& vbCrLf &_
vbTab & "/Force ' Enforces file removal. May cause data loss!" & vbCrLf &_
vbTab & "/SkipShortcutDetection ' Does not search the local hard drives for shortcuts" & vbCrLf & _
vbTab & "/Log [LogfolderPath] ' Custom folder for log files" & vbCrLf & _
vbTab & "/NoCancel ' Setup.exe and Msiexec.exe have no Cancel button" & vbCrLf &_
vbTab & "/OSE ' Forces removal of the Office Source Engine service" & vbCrLf &_
vbTab & "/Quiet ' Setup.exe and Msiexec.exe run quiet with no UI" & vbCrLf &_
vbTab & "/Preview ' Run this script to preview what would get removed"& vbCrLf & vbCrLf & _
"Examples:"& vbCrLf & _
vbTab & SCRIPTFILE & " CLIENTALL ' Remove all " & ONAME & " Client products" & vbCrLf &_
vbTab & SCRIPTFILE & " SERVER ' Remove all " & ONAME & " Server products" & vbCrLf &_
vbTab & SCRIPTFILE & " ALL ' Remove all " & ONAME & " Server & Client products" & vbCrLf &_
vbTab & SCRIPTFILE & " ProPlus,PrjPro ' Remove ProPlus and Project" & vbCrLf
Wscript.Quit
End Sub 'ShowSyntax
'=======================================================================================================
'=======================================================================================================
' Name: OffScrub07.vbs
' Author: Microsoft Customer Support Services
' Copyright (c) 2008, Microsoft Corporation
' Script to remove (scrub) Office 2007 products
'=======================================================================================================
Option Explicit
Const VERSION = "1.17"
Const HKCR = &H80000000
Const HKCU = &H80000001
Const HKLM = &H80000002
Const HKU = &H80000003
Const FOR_WRITING = 2
Const PRODLEN = 13
Const OFFICEID = "0000000FF1CE}"
Const COMPPERMANENT = "00000000000000000000000000000000"
Const REG_ARP = "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\"
Dim oFso, oMsi, oReg, oWShell, oWmiLocal
Dim ComputerItem, Item, LogStream, TmpKey
Dim arrInstalledSKUs, arrRemoveSKUs, arrKeepSKUs, arrTmpSKUs
Dim arrDeleteFiles, arrDeleteFolders, arrMseFolders
Dim b64
Dim sErr, sSkuInstalledList, sSkuRemoveList, sDefault, sWinDir, sMode, sApps
Dim sAppData, sTemp, sScrubDir, sProgramFiles, sProgramFilesX86, sCommonProgramFiles, sAllusersProfile
'=======================================================================================================
'Main
'=======================================================================================================
'Configure defaults
Dim sLogDir : sLogDir = ""
Dim sMoveMessage: sMoveMessage = ""
Dim bRemoveOSE : bRemoveOSE = False
Dim bRemoveAll : bRemoveAll = False
Dim bSimulate : bSimulate = False
Dim bQuiet : bQuiet = False
Dim bNoCancel : bNoCancel = False
'CAUTION! -> "bForce" will kill running applications which can result in data loss! <- CAUTION
Dim bForce : bForce = False
'CAUTION! -> "bForce" will kill running applications which can result in data loss! <- CAUTION
Dim bLogInitialized : bLogInitialized = False
Dim bBypass_Stage1 : bBypass_Stage1 = False 'Component Detection
Dim bBypass_Stage2 : bBypass_Stage2 = False 'Setup
Dim bBypass_Stage3 : bBypass_Stage3 = False 'Msiexec
Dim bBypass_Stage4 : bBypass_Stage4 = False 'CleanUp
sApps = "\communicator.exe"
'Create required objects
Set oWmiLocal = GetObject("winmgmts:\\.\root\cimv2")
Set oWShell = CreateObject("Wscript.Shell")
Set oFso = CreateObject("Scripting.FileSystemObject")
Set oMsi = CreateObject("WindowsInstaller.Installer")
Set oReg = GetObject("winmgmts:\\.\root\default:StdRegProv")
'Ensure CScript as engine
If Not UCase(Mid(Wscript.FullName, Len(Wscript.Path) + 2, 1)) = "C" Then RelaunchAsCScript
'Get environment path info
sAppData = oWShell.ExpandEnvironmentStrings("%appdata%")
sTemp = oWShell.ExpandEnvironmentStrings("%temp%")
sAllUsersProfile = oWShell.ExpandEnvironmentStrings("%allusersprofile%")
sProgramFiles = oWShell.ExpandEnvironmentStrings("%programfiles%")
sCommonProgramFiles = oWShell.ExpandEnvironmentStrings("%commonprogramfiles%")
sWinDir = oWShell.ExpandEnvironmentStrings("%windir%")
sScrubDir = sTemp & "\OffScrub07"
'Create the temp folder
If Not oFso.FolderExists(sScrubDir) Then oFso.CreateFolder sScrubDir
'Set the default logging directory
sLogDir = sScrubDir
'Detect if we're running on a 64 bit OS
Set ComputerItem = oWmiLocal.ExecQuery("Select * from Win32_ComputerSystem")
For Each Item In ComputerItem
b64 = Instr(Left(Item.SystemType,3),"64") > 0
'Log "64 bit OS: " & b64 & " -> " & Item.SystemType
Next
If b64 Then sProgramFilesX86 = oWShell.ExpandEnvironmentStrings("%programfiles(x86)%")
'Call the command line parser
ParseCmdLine
If Not CheckRegPermissions Then
Log "Insufficient registry access permissions - exiting"
'Undo temporary entries created in ARP
TmpKeyCleanUp
wscript.quit
End If
'-------------------
'Stage # 0 - Basics |
'-------------------
'Build a list with installed/registered Office 2007 products
Log vbCrLf & Now & " - Stage # 0 " & chr(34) & "Basics" & chr(34)
FindInstalledO12Products
If Len(sSkuInstalledList)>0 Then Log "Found registered product(s): " & Left(sSkuInstalledList,Len(sSkuInstalledList)-1)
'Validate the list of products we got from the command line if applicable
ValidateRemoveSkuList
sMode = "Selected Office 2007 products"
If Not IsArray(arrRemoveSKUs) Then sMode = "Orphaned Office 2007 products"
If bRemoveAll Then sMode = "All Office 2007 products"
Log "Final removal mode: " & sMode
If IsArray(arrRemoveSKUs) Then Log "List of configuration product(s) to remove: " & Join(arrRemoveSKUs,",")
Log "Remove OSE service: " & bRemoveOSE
If bSimulate Then Log "*************************************************************************"
If bSimulate Then Log "* PREVIEW MODE *"
If bSimulate Then Log "* All uninstall and delete operations will only be logged not executed! *"
If bSimulate Then Log "*************************************************************************"
'Cache .msi files
If IsArray(arrRemoveSKUs) Then CacheMsiFiles
'--------------------------------
'Stage # 1 - Component Detection |
'--------------------------------
If Not bBypass_Stage1 Then
Log vbCrLf & Now & " - Stage # 1 " & chr(34) & "Component Detection" & chr(34)
'Build a list with files which are installed/registered to a product that's going to be removed
Log vbCrLf & "Prepare for CleanUp stages."
Log "Searching for removable files. This can take several minutes."
BuildFileList : Log "Done"
Else
Log vbCrLf & Now & " - Skipping Stage # 1 " & chr(34) & "Component Detection" & chr(34) & " because bypass was requested."
End If
'Kill all running Office applications
If bForce Then KillApps
'----------------------
'Stage # 2 - Setup.exe |
'----------------------
If Not bBypass_Stage2 Then
Log vbCrLf & Now & " - Stage # 2 " & chr(34) & "Setup.exe" & chr(34)
SetupExeRemoval
Else
Log vbCrLf & Now & " - Skipping Stage # 2 " & chr(34) & "Setup.exe" & chr(34) & " because bypass was requested."
End If
'------------------------
'Stage # 3 - Msiexec.exe |
'------------------------
If Not bBypass_Stage3 Then
Log vbCrLf & Now & " - Stage # 3 " & chr(34) & "Msiexec.exe" & chr(34)
MsiexecRemoval
Else
Log vbCrLf & Now & " - Skipping Stage # 3 " & chr(34) & "Msiexec.exe" & chr(34) & " because bypass was requested."
End If
'--------------------
'Stage # 4 - CleanUp |
'--------------------
'Removal of files and registry settings
If Not bBypass_Stage4 Then
Log vbCrLf & Now & " - Stage # 4 " & chr(34) & "CleanUp" & chr(34)
'Office Source Engine
If bRemoveOSE Then RemoveOSE
'Local Installation Source (MSOCache)
WipeLIS
'Obsolete files
If bRemoveAll Then
FileWipeAll
Else
FileWipeIndividual
End If
'Empty Folders
DeleteEmptyFolders
'Restore Explorer if needed
If bForce Then RestoreExplorer
'Registry data
RegWipe
'Wipe orphaned files from Windows Installer cache
MsiClearOrphanedFiles
'Temporary .msi files in scrubcache
DeleteMsiScrubCache
'Temporary files from file move operations
DelScrubTmp
Else
Log vbCrLf & Now & " - Skipping Stage # 4 " & chr(34) & "CleanUp" & chr(34) & " because bypass was requested."
End If
If Not sMoveMessage = "" Then Log vbCrLf & "Please remove this folder after next reboot: " & sMoveMessage
'THE END
Log vbCrLf & "End removal: " & Now & vbCrLf
'=======================================================================================================
'=======================================================================================================
'Stage 0 - 4 Subroutines
'=======================================================================================================
'Office 2007 configuration products are listed with their configuration product name in the "Uninstall" key
'To identify an Office 2007 configuration product all of these condiditions have to be met:
' - "SystemComponent" entry exists with a value of "0"
' - "PackageIds" entry exists and is not empty
' - "DisplayVersion" exists and the 3 leftmost digits are "12."
Sub FindInstalledO12Products
Dim ArpItem
Dim hDefKey, sSubKeyName, sCurKey, sName, sValue, sConfigName, sLcid
Dim arrKeys, arrValues, arrMultiSzValues
Dim bSystemComponent0, bPackageIDs, bDisplayVersion
If Len(sSkuInstalledList) > 0 Then Exit Sub 'Already done from InputBox prompt
sSubKeyName = REG_ARP
'Locate standalone Office 2007 products that have no configuration product entry and create a
'temporary configuration entry
ReDim arrTmpSKUs(-1)
If RegEnumKey(HKLM,sSubKeyName,arrKeys) Then
For Each ArpItem in arrKeys
If UCase(Right(ArpItem,PRODLEN))=OFFICEID AND Mid(ArpItem,4,2)="12" Then
sCurKey = sSubKeyName & ArpItem & "\"
bSystemComponent0 = RegReadValue(HKLM,sCurKey,"SystemComponent",sValue,"REG_DWORD") AND sValue = "0"
If bSystemComponent0 OR Not RegReadValue(HKLM,sCurKey,"SystemComponent",sValue,"REG_DWORD") Then
RegReadValue HKLM,sCurKey,"DisplayVersion",sValue,"REG_SZ"
Redim arrMultiSzValues(0)
sConfigName = GetProductID(Mid(ArpItem,11,4)) & "_" & CInt("&h" & Mid(ArpItem,16,4))
ReDim Preserve arrTmpSKUs(UBound(arrTmpSKUs)+1)
arrTmpSKUs(UBound(arrTmpSKUs)) = sConfigName
oReg.CreateKey HKLM,REG_ARP & "\" & sConfigName
arrMultiSzValues(0) = sConfigName
oReg.SetMultiStringValue HKLM,REG_ARP & "\" & sConfigName,"PackageIds",arrMultiSzValues
arrMultiSzValues(0) = ArpItem
oReg.SetMultiStringValue HKLM,REG_ARP & "\" & sConfigName,"ProductCodes",arrMultiSzValues
oReg.SetStringValue HKLM,REG_ARP & "\" & sConfigName,"DisplayVersion",sValue
oReg.SetDWordValue HKLM,REG_ARP & "\" & sConfigName,"SystemComponent",0
End If
End If
Next 'ArpItem
End If 'RegEnumKey
'Find the configuration products
If RegEnumKey(HKLM,sSubKeyName,arrKeys)Then
For Each ArpItem in arrKeys
sCurKey = sSubKeyName & ArpItem & "\"
bSystemComponent0 = RegReadValue(HKLM,sCurKey,"SystemComponent",sValue,"REG_DWORD") AND sValue = "0"
bPackageIDs = RegReadValue(HKLM,sCurKey,"PackageIds",sValue,"REG_MULTI_SZ")
bDisplayVersion = RegReadValue(HKLM,sCurKey,"DisplayVersion",sValue,"REG_SZ") AND (Left(sValue,3) = "12.")
If (bSystemComponent0 AND bPackageIDs AND bDisplayVersion) Then _
sSkuInstalledList = sSkuInstalledList & UCase(ArpItem) & ","
Next 'ArpItem
End If 'RegEnumKey
If Len(sSkuInstalledList) > 0 Then arrInstalledSKUs = Split(Left(sSkuInstalledList,Len(sSkuInstalledList)-1),",")
End Sub 'FindInstalledO12Products
'=======================================================================================================
'Create clean list of Products to remove.
'Strip of bad & empty contents
Sub ValidateRemoveSkuList
Dim Sku, sSkuKeepList
Dim iPos
If bRemoveAll Then
If Len(sSkuInstalledList) > 0 Then
sSkuInstalledList = Left(sSkuInstalledList,Len(sSkuInstalledList)-1)
arrRemoveSKUs = Split(sSkuInstalledList,",")
sSkuInstalledList = sSkuInstalledList & ","
Else
Set arrRemoveSKUs = Nothing
End If
Else
'Ensure to have a string with no unexpected contents
sSkuRemoveList = Replace(sSkuRemoveList," ","")
sSkuRemoveList = Replace(sSkuRemoveList,Chr(34),"")
While InStr(sSkuRemoveList,",,")>0
sSkuRemoveList = Replace(sSkuRemoveList,",,",",")
Wend
arrRemoveSKUs = Split(UCase(sSkuRemoveList),",")
sSkuKeepList = "," & sSkuInstalledList & "OSE,"
sSkuRemoveList = ""
'Compare the list from the Cmd against the actually installed list of Office 2007 products
For Each Sku in arrRemoveSKUs
If Len(Sku)>0 AND InStr(sSkuKeepList,"," & Sku & ",") > 0 Then
sSkuKeepList = Replace(sSkuKeepList,Sku & ",","")
sSkuRemoveList = sSkuRemoveList & Sku & ","
End If 'iPos > 0
Next 'Sku
If Right(sSkuKeepList,4)="OSE," Then sSkuKeepList = Left(sSkuKeepList,Len(sSkuKeepList)-4)
sSkuKeepList = Right(sSkuKeepList,Len(sSkuKeepList)-1)
bRemoveAll = (sSkuKeepList = "")
If Not bRemoveAll Then arrKeepSKUs = Split(Mid(sSkuKeepList,1,Len(sSkuKeepList)-1),",")
If Len(sSkuRemoveList) > 0 Then
sSkuRemoveList = "," & sSkuRemoveList
If InStr(sSkuRemoveList,",OSE,")>0 Then
sSkuRemoveList = Replace(sSkuRemoveList,",OSE,",",")
bRemoveOSE = True
End If
sSkuRemoveList = Right(sSkuRemoveList,Len(sSkuRemoveList)-1)
'Recheck if there are products to remove in the list after the OSE chcek
If Len(sSkuRemoveList) > 0 Then
arrRemoveSKUs = Split(Left(sSkuRemoveList,Len(sSkuRemoveList)-1),",")
Else
arrRemoveSKus = Nothing
End If
Else
Set arrRemoveSKus = Nothing
End If
End If 'bRemoveAll
If bRemoveAll AND Not bRemoveOSE Then CheckRemoveOSE
End Sub 'ValidateRemoveSkuList
'=======================================================================================================
'Check if OSE service can be scrubbed
Sub CheckRemoveOSE
Const O11 = "6000-11D3-8CFE-0150048383C9}"
Dim Product
For Each Product in oMsi.Products
If UCase(Right(Product,28)) = O11 Then
bRemoveOSE = False
Exit Sub
End If
Next 'Product
If UCase(Right(Product,PRODLEN))=OFFICEID AND Mid(Product,4,2)="14" Then
'Found Office 14 Product. Set flag to not remove the OSE service
bRemoveOSE = False
Exit Sub
End If
bRemoveOSE = True
End Sub 'CheckRemoveOSE
'=======================================================================================================
'Cache .msi files for products that will be removed in case they are needed for later file detection
Sub CacheMsiFiles
Dim Product
Dim sMsiFile
On Error Resume Next
Log "Cache .msi files to temporary Scrub folder:"
'Cache the files
For Each Product in oMsi.Products
If (Right(Product,PRODLEN) = OFFICEID AND Mid(Product,4,2)="12") AND (bRemoveAll OR CheckDelete(Product))Then
CheckError "CacheMsiFiles"
sMsiFile = oMsi.ProductInfo(Product,"LocalPackage") : CheckError "CacheMsiFiles"
Log "File backup: " & Product & ".msi"
If oFso.FileExists(sMsiFile) Then oFso.CopyFile sMsiFile,sScrubDir & "\" & Product & ".msi",True
CheckError "CacheMsiFiles"
End If 'Right(Product,PRODLEN) = OFFICEID ...
Next 'Product
End Sub 'CacheMsiFiles
'=======================================================================================================
'Build a list of all files that will be deleted
Sub BuildFileList
Const MSIOPENDATABASEREADONLY = 0
Dim FileList, oDic, oFolderDic, ComponentID, CompClient, Record, qView, MsiDb
Dim sQuery, sSubKeyName, sPath, sFile, sMsiFile, sCompClient, sComponent
Dim bRemoveComponent
Dim i, iProgress, iCompCnt
'Logfile
Set FileList = oFso.OpenTextFile(sScrubDir & "\FileList.txt",FOR_WRITING,True,True)
On Error Resume Next 'Not optional here. Required for inline error handler
Set oDic = CreateObject("Scripting.Dictionary")
Set oFolderDic = CreateObject("Scripting.Dictionary")
iCompCnt = oMsi.Components.Count
'Enum all Components
For Each ComponentID In oMsi.Components
'Progress bar
i = i + 1
If iProgress < (i / iCompCnt) * 100 Then
wscript.stdout.write "." : LogStream.Write "."
iProgress = iProgress + 1
End If
bRemoveComponent = False
'Check if all ComponentClients will be removed
For Each CompClient In oMsi.ComponentClients(ComponentID)
bRemoveComponent = Right(CompClient,PRODLEN)=OFFICEID AND Mid(CompClient,4,2)="12" AND CheckDelete(CompClient)
If Not bRemoveComponent Then Exit For
'In "force" mode all components will be removed regardless of msidbComponentAttributesPermanent flag.
'Default is to honour the msidbComponentAttributesPermanent attribute and keep the files
If Not bForce Then
sSubKeyName = "SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components\"
If RegValExists(HKLM,sSubKeyName & GetCompressedGuid(CompClient),COMPPERMANENT) Then
bRemoveComponent = False
Exit For
End If
End If 'bForce
sCompClient = CompClient
Next 'CompClient
If bRemoveComponent Then
Err.Clear
'Get the component path
sPath = oMsi.ComponentPath(sCompClient,ComponentID)
If oFso.FileExists(sPath) Then
sPath = Left(sPath,InStrRev(sPath,"\")-1)
If Not oFolderDic.Exists(sPath) Then oFolderDic.Add sPath,sPath
'Get the .msi file
If oFso.FileExists(sScrubDir & "\" & sCompClient & ".msi") Then
sMsiFile = sScrubDir & "\" & sCompClient & ".msi"
Else
sMsiFile = oMsi.ProductInfo(sCompClient,"LocalPackage")
End If
Set MsiDb = oMsi.OpenDatabase(sMsiFile,MSIOPENDATABASEREADONLY)
If Err = 0 Then
'Get the component name from the 'Component' table
sQuery = "SELECT `Component`,`ComponentId` FROM Component WHERE `ComponentId` = '" & ComponentID &"'"
Set qView = MsiDb.OpenView(sQuery) : qView.Execute
Set Record = qView.Fetch()
If Not Record Is Nothing Then sComponent = Record.Stringdata(1)
'Get filenames from the 'File' table
sQuery = "SELECT `Component_`,`FileName` FROM File WHERE `Component_` = '" & sComponent &"'"
Set qView = MsiDb.OpenView(sQuery) : qView.Execute
Set Record = qView.Fetch()
Do Until Record Is Nothing
'Read the filename
sFile = Record.StringData(2)
If InStr(sFile,"|") > 0 Then sFile = Mid(sFile,InStr(sFile,"|")+1,Len(sFile))
sFile = sPath & "\" & sFile
If Not oDic.Exists(sFile) Then
oDic.Add sFile,sFile
FileList.WriteLine sFile
If LCase(Right(sFile,4))=".exe" Then sApps = sApps & ";" & sFile
End If
Set Record = qView.Fetch()
Loop
Set Record = Nothing
qView.Close
Set qView = Nothing
End If 'Err = 0
End If 'FileExists(sPath)
End If 'bRemoveComponent
Next 'ComponentID
sApps = sApps & ";"
If Not oFolderDic.Count = 0 Then arrDeleteFolders = oFolderDic.Keys Else Set arrDeleteFolders = Nothing
If Not oDic.Count = 0 Then arrDeleteFiles = oDic.Keys Else Set arrDeleteFiles = Nothing
End Sub 'BuildFileList
'=======================================================================================================
'Try to remove the products by calling setup.exe
Sub SetupExeRemoval
Dim OseService, Service, TextStream
Dim iSetupCnt, RetVal
Dim Sku, sConfigFile, sUninstallCmd, sCatalyst, sDll, sDisplayLevel, sNoCancel
iSetupCnt = 0
If Not IsArray(arrRemoveSKUs) Then
Log "Nothing to remove for setup."
Exit Sub
End If
'Ensure that the OSE service is *installed, *not disabled, *running under System context.
'If validation fails exit out of this sub.
Set OseService = oWmiLocal.Execquery("Select * From Win32_Service Where Name='ose'")
If OseService.Count = 0 Then Exit Sub
For Each Service in OseService
If (Service.StartMode = "Disabled") AND (Not Service.ChangeStartMode("Manual")=0) Then Exit Sub
If (Not Service.StartName = "LocalSystem") AND (Service.Change( , , , , , , "LocalSystem", "")) Then Exit Sub
Next 'Service
For Each Sku in arrRemoveSKUs
'Create an "unattended" config.xml file for uninstall
If bQuiet Then sDisplayLevel = "None" Else sDisplayLevel="Basic"
If bNoCancel Then sNoCancel="Yes" Else sNoCancel="No"
Set TextStream = oFso.OpenTextFile(sScrubDir & "\config.xml",FOR_WRITING,True,True)
TextStream.Writeline "<Configuration Product=""" & Sku & """>"
TextStream.Writeline "<Display Level=""" & sDisplayLevel & """ CompletionNotice=""No"" SuppressModal=""Yes"" NoCancel=""" & sNoCancel & """ AcceptEula=""Yes"" />"
TextStream.Writeline "<Logging Type=""Verbose"" Path=""" & sLogDir & """ Template=""Microsoft Office " & Sku & " Setup(*).txt"" />"
TextStream.Writeline "<Setting Id=""SETUP_REBOOT"" Value=""Never"" />"
TextStream.Writeline "</Configuration>"
TextStream.Close
Set TextStream = Nothing
'Ensure path to setup.exe is valid to prevent errors
RetVal = RegReadValue(HKLM,REG_ARP & Sku,"UninstallString",sCatalyst,"REG_SZ")
If RetVal Then
If InStr(LCase(sCatalyst),"/dll")>0 Then sDll = Right(sCatalyst,Len(sCatalyst)-InStr(LCase(sCatalyst),"/dll")+2)
sCatalyst = Trim(Replace(Left(sCatalyst,InStr(sCatalyst,"/")-2),Chr(34),""))
If oFso.FileExists(sCatalyst) Then
sUninstallCmd = Chr(34) & sCatalyst & Chr(34) & " /uninstall " & Sku & " /config " & Chr(34) & sScrubDir & "\config.xml" & Chr(34) & sDll
iSetupCnt = iSetupCnt + 1
Log "Calling setup.exe to remove " & Sku '& vbCrLf & sUninstallCmd
On Error Resume Next
If Not bSimulate Then RetVal = oWShell.Run(sUninstallCmd,0,True) : CheckError "CacheMsiFiles"
On Error Goto 0
Log "Removal of " & Sku & " returned: " & SetupExeRetVal(Retval) & " (" & RetVal & ")"
Else
Log "Error: Office 2007 setup.exe appears to be missing"
End If 'RetVal = 0) AND oFso.FileExists
End If 'RetVal
Next 'Sku
If iSetupCnt = 0 Then Log "Nothing to remove for setup."
End Sub 'SetupExeRemoval
'=======================================================================================================
'Invoke msiexec to remove individual .MSI packages
Sub MsiexecRemoval
Const MSIINSTALLSTATEABSENT = 2
Const MSIUILEVELNONE = 2
Const MSIUILEVELBASIC = 3 'Simple progress and error handling.
Const MSIUILEVELHIDECANCEL = 32 ' shows progress dialog boxes but does not display a Cancel button
Const MSIUILEVELPROGRESSONLY = 64 'displays progress dialog boxes but does not display any modal dialog boxes or error dialog boxes.
Dim Product
Dim i
'Check registered products
'O12 does only support per machine installation so it's sufficient to use Installer.Products
i = 0
If bQuiet Then
oMsi.UILevel = MSIUILEVELNONE
Else
If bNoCancel Then
oMsi.UILevel = MSIUILEVELBASIC + MSIUILEVELHIDECANCEL + MSIUILEVELPROGRESSONLY
Else
oMsi.UILevel = MSIUILEVELBASIC + MSIUILEVELPROGRESSONLY
End If
End If
For Each Product in oMsi.Products
If (Right(Product,PRODLEN) = OFFICEID AND Mid(Product,4,2)="12") AND (bRemoveAll OR CheckDelete(Product))Then
i = i + 1
Log "Calling msiexec.exe to remove " & Product
oMsi.EnableLog "voicewarmup", sLogDir & "\Uninstall_" & Product & ".log"
On Error Resume Next
If Not bSimulate Then oMsi.ConfigureProduct Product,0,MSIINSTALLSTATEABSENT
On Error Goto 0
End If 'Right(Product,PRODLEN) = OFFICEID
Next 'Product
If i = 0 Then Log "Nothing to remove for msiexec"
End Sub 'MsiexecRemoval
'=======================================================================================================
'Remove the OSE (Office Source Engine) service
Sub RemoveOSE
On Error Resume Next
Log "OSE CleanUp:"
DeleteService ("ose")
'Delete the folder
DeleteFolder sCommonProgramFiles & "\Microsoft Shared\Source Engine"
'Delete the registration
RegDeleteKey HKLM,"SYSTEM\CurrentControlSet\Services\ose"
End Sub 'RemoveOSE
'=======================================================================================================
'File cleanup operations for the Local Installation Source (MSOCache)
Sub WipeLIS
Const LISROOT = "MSOCache\All Users\"
Dim LogicalDisks, Disk, Folder, SubFolder, MseFolder, File, Files
Dim arrSubFolders
Dim sFolder
Dim bRemoveFolder
'On Error Resume Next
Log "LIS CleanUp:"
'Search all hard disks
Set LogicalDisks = oWmiLocal.ExecQuery("Select * from Win32_LogicalDisk")
For Each Disk in LogicalDisks
If Disk.DriveType = 3 Then
If oFso.FolderExists(Disk.DeviceID & "\" & LISROOT)Then
If Err <> 0 Then
CheckError "WipeLIS"
Exit Sub
End If
Set Folder = oFso.GetFolder(Disk.DeviceID & "\" & LISROOT)
For Each Subfolder in Folder.Subfolders
If bRemoveAll Then
If (Mid(Subfolder.Name,26,PRODLEN) = OFFICEID AND Mid(SubFolder.Name,4,2)="12") OR _
LCase(Right(Subfolder.Name,7)) = "12.data" Then DeleteFolder Subfolder.Path
Else
If (Mid(Subfolder.Name,26,PRODLEN) = OFFICEID AND Mid(SubFolder.Name,4,2)="12") AND _
CheckDelete(UCase(Left(Subfolder.Name,38))) AND _
UCase(Right(Subfolder,1))= UCase(Left(Disk.DeviceID,1))Then DeleteFolder Subfolder.Path
End If
Next 'Subfolder
If (Folder.Subfolders.Count = 0) AND (Folder.Files.Count = 0) Then
sFolder = Folder.Path
Set Folder = Nothing
SmartDeleteFolder sFolder
End If
End If 'oFso.FolderExists
End If 'Disk.DriveType = 3
Next 'Disk
'MSECache
If EnumFolders(sProgramFiles,arrSubFolders) Then
For Each SubFolder in arrSubFolders
If UCase(Right(SubFolder,9))="\MSECACHE" Then
ReDim arrMseFolders(-1)
Set Folder = oFso.GetFolder(SubFolder)
GetFolderStructure Folder
For Each MseFolder in arrMseFolders
If oFso.FolderExists(MseFolder) Then
bRemoveFolder = False
Set Folder = oFso.GetFolder(MseFolder)
Set Files = Folder.Files
For Each File in Files
If (LCase(Right(File.Name,4))=".msi") Then
If CheckDelete(ProductCode(File.Path)) Then
bRemoveFolder = True
Exit For
End If 'CheckDelete
End If
Next 'File
Set Files = Nothing
Set Folder = Nothing
If bRemoveFolder Then SmartDeleteFolder MseFolder
End If 'oFso.FolderExists(MseFolder)
Next 'MseFolder
End If
Next 'SubFolder
End If 'oFso.FolderExists
End Sub 'WipeLis
'=======================================================================================================
'Wipe files and folders as documented in KB 928218
Sub FileWipeAll
Dim sFile, sAppdata, sFolder
Dim File, Files, Folder, Subfolder, OSPPsvc, Service
'On Error Resume Next
'Run the individual filewipe first
FileWipeIndividual
DeleteFolder sCommonProgramFiles & "\Microsoft Shared\Office12"
DeleteFolder sProgramFiles & "\Microsoft Office\Office12"
DeleteFile sAllUsersProfile & "\Application Data\Microsoft\Office\Data\opa12.dat"
'Delete files that should be backed up before deleting them
CopyAndDeleteFile sAppdata & "\Microsoft\Templates\Normal.dotm"
CopyAndDeleteFile sAppdata & "\Microsoft\Templates\Normalemail.dotm"
sFolder = sAppdata & "\microsoft\document building blocks"
If oFso.FolderExists(sFolder) Then
Set Folder = oFso.GetFolder(sFolder)
For Each Subfolder In Folder.Subfolders
If oFso.FileExists(Subfolder & "\blocks.dotx") Then CopyAndDeleteFile Subfolder & "\blocks.dotx"
Next 'Subfolder
Set Folder = Nothing
End If
'Cleanup %temp% folder
'Don't run this if the current log folder points to %temp%
If Not sLogDir = sTemp Then
Set Folder = oFso.GetFolder(sTemp)
Set Files = Folder.Files
For Each File in Files
DeleteFile File.Path
Next 'File
For Each Subfolder in Folder.Subfolders
If Not LCase(Subfolder.Name) = "offscrub07" Then DeleteFolder Subfolder.Path
Next 'Subfolder
End If 'Not sLogDir = sTemp
End Sub 'FileWipeAll
'=======================================================================================================
'Wipe individual files & folders related to SKU's that are no longer installed
Sub FileWipeIndividual
Dim LogicalDisks, Disk
Dim File, Files, XmlFile, scFiles, oFile, Folder, SubFolder, Processes, Process
Dim sFile, sFolder, sPath, sConfigName, sContents, sProductCode, sLocalDrives,sScQuery
Dim arrSubfolders
Dim bKeepFolder, bDeleteSC
Log "File CleanUp:"
'On Error Resume Next
If IsArray(arrDeleteFiles) Then
If bForce Then
Log "Doing Action: KillOSE"
Set Processes = oWmiLocal.ExecQuery("Select * From Win32_Process")
For Each Process in Processes
Log "Running process : " & Process.Name
If LCase(Left(Process.Name,3))="ose" Then
Log "Terminating process: " & Process.Name
Process.Terminate
End If
Next 'Process
Log "End Action: KillOSE"
KillApps
End If
'Wipe individual files detected earlier
For Each sFile in arrDeleteFiles
If oFso.FileExists(sFile) Then DeleteFile sFile
Next 'File
End If 'IsArray
'Wipe Catalyst in commonfiles
sFolder = sCommonProgramFiles & "\microsoft shared\OFFICE12\Office Setup Controller\"
If EnumFolderNames(sFolder,arrSubFolders) Then
For Each SubFolder in arrSubFolders
sPath = sFolder & SubFolder
If InStr(SubFolder,".")>0 Then sConfigName = UCase(Left(SubFolder,InStr(SubFolder,".")-1))Else sConfigName = UCase(Subfolder)
If GetFolderPath(sPath) Then
Set Folder = oFso.GetFolder(sPath)
Set Files = Folder.Files
bKeepFolder = False
For Each File In Files
If (LCase(Right(File.Name,4))=".xml") AND (UCase(Left(File.Name,Len(sConfigName)))=sConfigName) Then
Set XmlFile = oFso.OpenTextFile(File,1)
sContents = XmlFile.ReadAll
Set XmlFile = Nothing
sProductCode = Mid(sContents,InStr(sContents,"ProductCode=")+Len("ProductCode=")+1,38)
If CheckDelete(sProductCode) Then DeleteFile File.Path Else bKeepFolder = True
End If
Next 'File
Set Files = Nothing
Set Folder = Nothing
If Not bKeepFolder Then DeleteFolder sPath
End If 'GetFolderPath
Next 'SubFolder
End If 'EnumFolderNames
'Wipe Shortcuts from local hard disks
On Error Resume Next
Log "Searching for shortcuts. This can take a some time ..."
Set LogicalDisks = oWmiLocal.ExecQuery("Select * From Win32_LogicalDisk WHERE DriveType=3")
For Each Disk in LogicalDisks
sLocalDrives = sLocalDrives & UCase(Disk.DeviceID) & "\;"
sScQuery = "Select * From Win32_ShortcutFile WHERE Drive='"&Disk.DeviceID&"'"
Set scFiles = oWmiLocal.ExecQuery(sScQuery)
For Each File in scFiles
bDeleteSC = False
'Compare if the shortcut target is in the list of executables that will be removed
If Len(File.Target)>0 AND InStr(sApps,";" & File.Target & ";")>0 Then bDeleteSC = True
'Handle Windows Installer shortcuts
If InStr(File.Target,"{")>0 Then
If Len(File.Target)>=InStr(File.Target,"{")+37 Then
If CheckDelete(Mid(File.Target,InStr(File.Target,"{"),38)) Then bDeleteSC = True
End If
End If
If bDeleteSC Then
If Not IsArray(arrDeleteFolders) Then ReDim arrDeleteFolders(0)
sFolder = Left(File.Description,InStrRev(File.Description,"\")-1)
If Not arrDeleteFolders(UBound(arrDeleteFolders)) = sFolder Then
ReDim Preserve arrDeleteFolders(UBound(arrDeleteFolders)+1)
arrDeleteFolders(UBound(arrDeleteFolders)) = sFolder
End If
DeleteFile File.Description
End If 'bDeleteSC
Next 'scFile
Next
On Error Goto 0
End Sub 'FileWipeIndividual
'=======================================================================================================
Sub DelScrubTmp
Dim LogicalDisks, Disk
Dim sFolder
'Search all hard disks
Set LogicalDisks = oWmiLocal.ExecQuery("Select * from Win32_LogicalDisk")
For Each Disk in LogicalDisks
If Disk.DriveType = 3 Then
If oFso.FolderExists(Disk.DeviceID & "\ScrubTmp") Then
On Error Resume Next
oFso.DeleteFolder Disk.DeviceID & "\ScrubTmp",True
On Error Goto 0
End If
End If
Next 'Disk
End Sub 'DelScrubTmp
'=======================================================================================================
'Ensure there are no unexpected .msi files in the scrub folder
Sub DeleteMsiScrubCache
Dim Folder, File, Files
Log "ScrubCache CleanUp:"
Set Folder = oFso.GetFolder(sScrubDir) : CheckError "DeleteMsiScrubCache"
Set Files = Folder.Files
For Each File in Files
CheckError "DeleteMsiScrubCache"
If LCase(Right(File.Name,4))=".msi" Then
CheckError "DeleteMsiScrubCache"
DeleteFile File.Path : CheckError "DeleteMsiScrubCache"
End If
Next 'File
End Sub 'DeleteMsiScrubCache
'=======================================================================================================
Sub MsiClearOrphanedFiles
Const USERSIDEVERYONE = "s-1-1-0"
Const MSIINSTALLCONTEXT_ALL = 7
Const MSIPATCHSTATE_ALL = 15
On Error Resume Next
Dim Patch, AllPatches, Product, AllProducts
Dim File, Files, Folder
Dim sFName, sLocalMsp, sLocalMsi, sPatchList, sMsiList
Set Folder = oFso.GetFolder(sWinDir & "\Installer")
Set Files = Folder.Files
Log "Windows Installer cache CleanUp:"
'Get a complete list of patches
Err.Clear
Set AllPatches = oMsi.PatchesEx("",USERSIDEVERYONE,MSIINSTALLCONTEXT_ALL,MSIPATCHSTATE_ALL)
If Err <> 0 Then
CheckError "MsiClearOrphanedFiles (msp)"
Else
'Fill a comma separated stringlist with all .msp patchfiles
For Each Patch in AllPatches
sLocalMsp = "" : sLocalMsp = LCase(Patch.Patchproperty("LocalPackage")) : CheckError "MsiClearOrphanedFiles (msp)"
sPatchList = sPatchList & sLocalMsp & ","
Next 'Patch
'Delete all non referenced .msp files from %windir%\installer
For Each File in Files
sFName = "" : sFName = LCase(File.Path)
If LCase(Right(sFName,4)) = ".msp" Then
If Not InStr(sPatchList,sFName) > 0 Then
'While this is an orphaned file keep the scope of Office only
If InStr(UCase(MapTargets(File.Path)),OFFICEID)>0 Then DeleteFile File.Path
End If
End If 'LCase(Right(sFName,4))
Next 'File
End If 'Err=0
'Get a complete list products
Err.Clear
Set AllProducts = oMsi.ProductsEx("",USERSIDEVERYONE,MSIINSTALLCONTEXT_ALL)
If Err <> 0 Then
CheckError "MsiClearOrphanedFiles (msi)"
Else
'Fill a comma separated stringlist with all .msi files
For Each Product in AllProducts
sLocalMsi = "" : sLocalMsi = LCase(Product.InstallProperty("LocalPackage")) : CheckError "MsiClearOrphanedFiles (msi)"
sMsiList = sMsiList & sLocalMsi & ","
Next 'Product
'Delete all non referenced .msi files from %windir%\installer
For Each File in Files
sFName = "" : sFName = LCase(File.Path)
If LCase(Right(sFName,4)) = ".msi" Then
If Not InStr(sMsiList,sFName) > 0 Then
'While this is an orphaned file keep the scope of Office only
If UCase(Right(ProductCode(File.Path),PRODLEN))=OFFICEID Then DeleteFile File.Path
End If
End If 'LCase(Right(sFName,4)) = ".msi"
Next 'File
End If 'Err=0
End Sub 'MsiClearOrphanedFiles
'=======================================================================================================
Sub RegWipe
Dim Item, Name, Sku
Dim hDefKey, sSubKeyName, sCurKey, sValue, sGuid
Dim bKeep, bSystemComponent0, bPackageIDs, bDisplayVersion
Dim arrKeys, arrNames, arrTypes
Dim iLoopCnt
Log "Registry CleanUp:"
'Wipe registry data
If bRemoveAll Then
RegDeleteKey HKCU,"Software\Microsoft\Office\12.0"
RegDeleteKey HKCU,"Software\Policies\Microsoft\Office\12.0"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Office\12.0"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Terminal Server\Install\Software\Microsoft\Office\12.0"
'Win32Assemblies
hDefKey = HKCR
sSubKeyName = "Installer\Win32Assemblies\"
If RegEnumKey(hDefKey,sSubKeyName,arrKeys) Then
For Each Item in arrKeys
If InStr(UCase(Item),"OFFICE12")>0 Then RegDeleteKey hDefKey,sSubKeyName & Item
Next 'Item
End If 'RegEnumKey
End If 'bRemoveAll
'Add/Remove Programs
sSubKeyName = REG_ARP
If RegEnumKey(HKLM,sSubKeyName,arrKeys) Then
For Each Item in arrKeys
'*0FF1CE*
If Len(Item)>37 Then
sGuid = UCase(Left(Item,38))
If Right(sGuid,PRODLEN)=OFFICEID AND Mid(sGuid,4,2)="12" Then
If CheckDelete(sGuid) Then RegDeleteKey HKLM, sSubKeyName & Item
End If 'Right(Item,PRODLEN)=OFFICEID
End If 'Len(Item)>37
'Config entries
sCurKey = sSubKeyName & Item & "\"
bSystemComponent0 = RegReadValue(HKLM,sCurKey,"SystemComponent",sValue,"REG_DWORD") AND sValue = "0"
bPackageIDs = RegReadValue(HKLM,sCurKey,"PackageIds",sValue,"REG_MULTI_SZ")
bDisplayVersion = RegReadValue(HKLM,sCurKey,"DisplayVersion",sValue,"REG_SZ") AND (Left(sValue,3) = "12.")
If (bSystemComponent0 AND bPackageIDs AND bDisplayVersion) Then
bKeep = False
If Not bRemoveAll Then
For Each Sku in arrKeepSKUs
If UCase(Item) = Sku Then bKeep = True
Next 'Sku
End If
If Not bKeep Then RegDeleteKey HKLM, sSubKeyName & Item
End If
Next 'Item
End If 'RegEnumKey
'UpgradeCodes, WI config, WI global config
For iLoopCnt = 1 to 5
Select Case iLoopCnt
Case 1
sSubKeyName = "SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UpgradeCodes\"
hDefKey = HKLM
Case 2
sSubKeyName = "Installer\UpgradeCodes\"
hDefKey = HKCR
Case 3
sSubKeyName = "SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\"
hDefKey = HKLM
Case 4
sSubKeyName = "Installer\Features\"
hDefKey = HKCR
Case 5
sSubKeyName = "Installer\Products\"
hDefKey = HKCR
Case Else
sSubKeyName = ""
hDefKey = ""
End Select
If RegEnumKey(hDefKey,sSubKeyName,arrKeys) Then
For Each Item in arrKeys
'Ensure we have the expected length for a compressed GUID
If Len(Item)=32 Then
'Expand the GUID
sGuid = GetExpandedGuid(Item)
'Check if it's a Office 2007 key
If Right(sGuid,PRODLEN)=OFFICEID AND Mid(sGuid,4,2)="12" Then
If bRemoveAll Then
RegDeleteKey hDefKey,sSubKeyName & Item
Else
If iLoopCnt < 3 Then
'Enum all entries
RegEnumValues hDefKey,sSubKeyName & Item,arrNames,arrTypes
If IsArray(arrNames) Then
'Delete entries within removal scope
For Each Name in arrNames
If Len(Name)=32 Then
sGuid = GetExpandedGuid(Name)
If CheckDelete(sGuid) Then RegDeleteValue hDefKey, sSubKeyName & Item, Name
Else
'Invalid data -> delete the value
RegDeleteValue hDefKey, sSubKeyName & Item, Name
End If
Next 'Name
End If 'IsArray(arrNames)
'If all entries were removed - delete the key
RegEnumValues hDefKey,sSubKeyName & Item,arrNames,arrTypes
If Not IsArray(arrNames) Then RegDeleteKey hDefKey, sSubKeyName & Item
Else 'iLoopCnt >= 3
If CheckDelete(sGuid) Then RegDeleteKey hDefKey, sSubKeyName & Item
End If 'iLoopCnt < 3
End If 'bRemoveAll
End If 'Right(Item,PRODLEN)=OFFICEID
End If 'Len(Item)=32
Next 'Item
End If 'RegEnumKey
Next 'iLoopCnt
'Delivery
hDefKey = HKLM
sSubKeyName = "SOFTWARE\Microsoft\Office\Delivery\SourceEngine\Downloads\"
If RegEnumKey(HKLM,sSubKeyName,arrKeys) Then
For Each Item in arrKeys
If bRemoveAll Then
If (Mid(Item,26,PRODLEN)=OFFICEID AND Mid(Item,4,2)="12") OR _
LCase(Right(Item,7))="12.data" Then RegDeleteKey HKLM,sSubKeyName & Item
Else
If (Mid(Item,26,PRODLEN)=OFFICEID AND Mid(Item,4,2)="12") AND _
CheckDelete(UCase(Left(Item,38))) Then RegDeleteKey HKLM,sSubKeyName & Item
End If
Next 'Item
End If 'RegEnumKey
'Registration
hDefKey = HKLM
sSubKeyName = "SOFTWARE\Microsoft\Office\12.0\Registration\"
If RegEnumKey(HKLM,sSubKeyName,arrKeys) Then
For Each Item in arrKeys
If CheckDelete(UCase(Left(Item,38))) Then RegDeleteKey HKLM,sSubKeyName & Item
Next 'Item
End If 'RegEnumKey
'User Preconfigurations
hDefKey = HKLM
sSubKeyName = "SOFTWARE\Microsoft\Office\12.0\User Settings\"
If RegEnumKey(HKLM,sSubKeyName,arrKeys) Then
For Each Item in arrKeys
If CheckDelete(UCase(Left(Item,38))) Then RegDeleteKey HKLM,sSubKeyName & Item
Next 'Item
End If 'RegEnumKey
'Temporary entries in ARP
TmpKeyCleanUp
End Sub 'RegWipeAll
'=======================================================================================================
'Clean up temporary registry keys
Sub TmpKeyCleanUp
Dim TmpKey
If bLogInitialized Then Log "Remove temporary registry entries:"
If IsArray(arrTmpSKUs) Then
For Each TmpKey in arrTmpSKUs
'RegDeleteKey HKLM,REG_ARP & TmpKey
oReg.DeleteKey HKLM, REG_ARP & TmpKey
Next 'Item
End If 'IsArray
End Sub 'TmpKeyCleanUp
'=======================================================================================================
' Helper Functions
'=======================================================================================================
'Kill all running instances of applications that will be removed
Sub KillApps
Dim Processes, Process
'On Error Resume Next
Log "Doing Action: KillApps"
Set Processes = oWmiLocal.ExecQuery("Select * From Win32_Process")
For Each Process in Processes
If InStr(LCase(sApps),"\" & LCase(Process.Name) & ";")>0 Then
Log "Killing process " & Process.Name
Process.Terminate()
CheckError "KillApps: " & "Process.Name"
End If
Next 'Process
Log "End Action: KillApps"
End Sub 'KillApps
'=======================================================================================================
'Ensure Windows Explorer is restarted if needed
Sub RestoreExplorer
Dim Processes
On Error Resume Next
wscript.sleep 1000
Set Processes = oWmiLocal.ExecQuery("Select * From Win32_Process Where Name='explorer.exe'")
If Processes.Count < 1 Then oWShell.Run "explorer.exe"
End Sub 'RestoreExploer
'=======================================================================================================
'Check registry access permissions. Failure will terminate the script
Function CheckRegPermissions
Const KEY_QUERY_VALUE = &H0001
Const KEY_SET_VALUE = &H0002
Const KEY_CREATE_SUB_KEY = &H0004
Const DELETE = &H00010000
Dim sSubKeyName
Dim bReturn
CheckRegPermissions = True
sSubKeyName = "Software\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\"
oReg.CheckAccess HKLM, sSubKeyName, KEY_QUERY_VALUE, bReturn
If Not bReturn Then CheckRegPermissions = False
oReg.CheckAccess HKLM, sSubKeyName, KEY_SET_VALUE, bReturn
If Not bReturn Then CheckRegPermissions = False
oReg.CheckAccess HKLM, sSubKeyName, KEY_CREATE_SUB_KEY, bReturn
If Not bReturn Then CheckRegPermissions = False
oReg.CheckAccess HKLM, sSubKeyName, DELETE, bReturn
If Not bReturn Then CheckRegPermissions = False
End Function 'CheckRegPermissions
'=======================================================================================================
'Check if an Office 12 product is still registered with a SKU that stays on the computer
Function CheckDelete(sProductCode)
Dim Sku
Dim RetVal
Dim sProductCodeList
'If it's a non Office 12 ProductCode exit with false right away
CheckDelete = Right(sProductCode,PRODLEN) = OFFICEID
If CheckDelete Then CheckDelete = Mid(sProductCode,4,2) = "12"
If Not CheckDelete Then Exit Function
If Not IsArray(arrKeepSKUs) Then Exit Function
For Each Sku in arrKeepSKUs
RetVal = RegReadValue(HKLM,REG_ARP & Sku,"ProductCodes",sProductCodeList,"REG_MULTI_SZ")
If InStr(sProductCodeList,sProductCode) > 0 Then
CheckDelete = False
Exit Function
End If
Next 'Sku
End Function 'CheckDelete
'=======================================================================================================
'Returns a string with a list of ProductCodes from the summary information stream
Function MapTargets (sMspFile)
Const MSIOPENDATABASEMODE_PATCHFILE = 32
Const PID_TEMPLATE = 7
Dim Msp
On Error Resume Next
MapTargets = ""
If oFso.FileExists(sMspFile) Then
Set Msp = Msi.OpenDatabase(WScript.Arguments(0),MSIOPENDATABASEMODE_PATCHFILE)
If Err = 0 Then MapTargets = Msp.SummaryInformation.Property(PID_TEMPLATE)
End If 'oFso.FileExists(sMspFile)
End Function 'MspTargets
'=======================================================================================================
'Return the ProductCode {GUID} from a .MSI package
Function ProductCode(sMsi)
Const MSIUILEVELNONE = 2 'No UI
Dim MsiSession
On Error Resume Next
If oFso.FileExists(sMsi) Then
oMsi.UILevel = MSIUILEVELNONE
Set MsiSession = oMsi.OpenPackage(sMsi,1)
ProductCode = MsiSession.ProductProperty("ProductCode")
Set MsiSession = Nothing
Else
ProductCode = ""
End If 'oFso.FileExists(sMsi)
End Function 'ProductCode
'=======================================================================================================
Function GetExpandedGuid (sGuid)
Dim i
GetExpandedGuid = "{" & StrReverse(Mid(sGuid,1,8)) & "-" & _
StrReverse(Mid(sGuid,9,4)) & "-" & _
StrReverse(Mid(sGuid,13,4))& "-"
For i = 17 To 20
If i Mod 2 Then
GetExpandedGuid = GetExpandedGuid & mid(sGuid,(i + 1),1)
Else
GetExpandedGuid = GetExpandedGuid & mid(sGuid,(i - 1),1)
End If
Next
GetExpandedGuid = GetExpandedGuid & "-"
For i = 21 To 32
If i Mod 2 Then
GetExpandedGuid = GetExpandedGuid & mid(sGuid,(i + 1),1)
Else
GetExpandedGuid = GetExpandedGuid & mid(sGuid,(i - 1),1)
End If
Next
GetExpandedGuid = GetExpandedGuid & "}"
End Function
'=======================================================================================================
'Converts a GUID into the compressed format
Function GetCompressedGuid (sGuid)
Dim sCompGUID
Dim i
sCompGUID = StrReverse(Mid(sGuid,2,8)) & _
StrReverse(Mid(sGuid,11,4)) & _
StrReverse(Mid(sGuid,16,4))
For i = 21 To 24
If i Mod 2 Then
sCompGUID = sCompGUID & Mid(sGuid, (i + 1), 1)
Else
sCompGUID = sCompGUID & Mid(sGuid, (i - 1), 1)
End If
Next
For i = 26 To 37
If i Mod 2 Then
sCompGUID = sCompGUID & Mid(sGuid, (i - 1), 1)
Else
sCompGUID = sCompGUID & Mid(sGuid, (i + 1), 1)
End If
Next
GetCompressedGuid = sCompGUID
End Function
'=======================================================================================================
'Create a backup copy of the file in the ScrubDir then delete the file
Sub CopyAndDeleteFile(sFile)
Dim File
On Error Resume Next
If oFso.FileExists(sFile) Then
Set File = oFso.GetFile(sFile)
If Not oFso.FolderExists(sScrubDir & "\" & File.ParentFolder.Name) Then oFso.CreateFolder sScrubDir & "\" & File.ParentFolder.Name
oFso.CopyFile sFile,sScrubDir & "\" & File.ParentFolder.Name & "\" & File.Name,True : CheckError "CopyAndDeleteFile"
Set File = Nothing
DeleteFile(sFile)
End If 'oFso.FileExists
End Sub 'CopyAndDeleteFile
'=======================================================================================================
'Wrapper to delete a file
Sub DeleteFile(sFile)
Dim File, Process, Processes
Dim sFileName, sNewPath, sProcessList
On Error Resume Next
If oFso.FileExists(sFile) Then
Log " - Delete file: " & sFile
If Not bSimulate Then oFso.DeleteFile sFile,True ': CheckError "DeleteFile"
If Err <> 0 Then
CheckError "DeleteFile"
'Try to move the file and delete from there
Set File = oFso.GetFile(sFile)
sFileName = File.Name
sNewPath = File.Drive.Path & "\" & "ScrubTmp"
Set File = Nothing
'Ensure we stay within the same drive
If Not oFso.FolderExists(sNewPath) Then oFso.CreateFolder(sNewPath)
'Move the file
Log " - Move file to: " & sNewPath & "\" & sFileName
oFso.MoveFile sFile,sNewPath & "\" & sFileName
If Err <> 0 Then
CheckError "DeleteFile (move)"
Else
If Not InStr(sMoveMessage,sNewPath)>0 Then sMoveMessage = sMoveMessage & sNewPath & ";"
oFso.DeleteFile sNewPath & "\" & sFileName,True
If Err <> 0 And bForce Then
CheckError "DeleteFile (moved)"
End If
End If 'Err <> 0
End If 'Err <> 0
End If 'oFso.FileExists
End Sub 'DeleteFile
'=======================================================================================================
'64 bit aware wrapper to return the requested folder
Function GetFolderPath(sPath)
GetFolderPath = True
If oFso.FolderExists(sPath) Then Exit Function
If b64 AND oFso.FolderExists(Wow64Folder(sPath)) Then
sPath = Wow64Folder(sPath)
Exit Function
End If
GetFolderPath = False
End Function 'GetFolderPath
'=======================================================================================================
'Enumerates subfolder names of a folder and returns True if subfolders exist
Function EnumFolderNames (sFolder, arrSubFolders)
Dim Folder, Subfolder
Dim sSubFolders
If oFso.FolderExists(sFolder) Then
Set Folder = oFso.GetFolder(sFolder)
For Each Subfolder in Folder.Subfolders
sSubFolders = sSubFolders & Subfolder.Name & ","
Next 'Subfolder
End If
If b64 AND oFso.FolderExists(Wow64Folder(sFolder)) Then
Set Folder = oFso.GetFolder(Wow64Folder(sFolder))
For Each Subfolder in Folder.Subfolders
sSubFolders = sSubFolders & Subfolder.Name & ","
Next 'Subfolder
End If
If Len(sSubFolders)>0 Then arrSubFolders = RemoveDuplicates(Split(Left(sSubFolders,Len(sSubFolders)-1),","))
EnumFolderNames = Len(sSubFolders)>0
End Function 'EnumFolderNames
'=======================================================================================================
'Enumerates subfolders of a folder and returns True if subfolders exist
Function EnumFolders (sFolder, arrSubFolders)
Dim Folder, Subfolder
Dim sSubFolders
If oFso.FolderExists(sFolder) Then
Set Folder = oFso.GetFolder(sFolder)
For Each Subfolder in Folder.Subfolders
sSubFolders = sSubFolders & Subfolder.Path & ","
Next 'Subfolder
End If
If b64 AND oFso.FolderExists(Wow64Folder(sFolder)) Then
Set Folder = oFso.GetFolder(Wow64Folder(sFolder))
For Each Subfolder in Folder.Subfolders
sSubFolders = sSubFolders & Subfolder.Path & ","
Next 'Subfolder
End If
If Len(sSubFolders)>0 Then arrSubFolders = RemoveDuplicates(Split(Left(sSubFolders,Len(sSubFolders)-1),","))
EnumFolders = Len(sSubFolders)>0
End Function 'EnumFolders
'=======================================================================================================
Sub GetFolderStructure (Folder)
Dim SubFolder
For Each SubFolder in Folder.SubFolders
ReDim Preserve arrMseFolders(UBound(arrMseFolders)+1)
arrMseFolders(UBound(arrMseFolders)) = SubFolder.Path
GetFolderStructure SubFolder
Next 'SubFolder
End Sub 'GetFolderStructure
'=======================================================================================================
'Wrapper to delete a folder
Sub DeleteFolder(sFolder)
Dim Folder
Dim sDelFolder, sFolderName, sNewPath
On Error Resume Next
If oFso.FolderExists(sFolder) Then
sDelFolder = sFolder
ElseIf b64 AND oFso.FolderExists(Wow64Folder(sFolder)) Then
sDelFolder = Wow64Folder(sFolder)
Else
Exit Sub
End If
Log " - Delete folder: " & sDelFolder
If Not bSimulate Then oFso.DeleteFolder sDelFolder,True
If Err <> 0 Then
CheckError "DeleteFolder"
stop
'Try to move the folder and delete from there
Set Folder = oFso.GetFolder(sDelFolder)
sFolderName = Folder.Name
sNewPath = Folder.Drive.Path & "\" & "ScrubTmp"
Set Folder = Nothing
'Ensure we stay within the same drive
If Not oFso.FolderExists(sNewPath) Then oFso.CreateFolder(sNewPath)
'Move the folder
Log " - Moving folder to: " & sNewPath & "\" & sFolderName
oFso.MoveFolder sFolder,sNewPath & "\" & sFolderName
If Err <> 0 Then
CheckError "DeleteFolder (move)"
Else
oFso.DeleteFolder sNewPath & "\" & sFolderName,True
If Err <> 0 And bForce Then
CheckError "DeleteFolder (moved)"
End If
End If 'Err <> 0
End If 'Err <> 0
End Sub 'DeleteFolder
'=======================================================================================================
'Delete empty folder structures
Sub DeleteEmptyFolders
Dim Folder
Dim sFolder
If Not IsArray(arrDeleteFolders) Then Exit Sub
Log "Empty Folder Cleanup"
For Each sFolder in arrDeleteFolders
If oFso.FolderExists(sFolder) Then
Set Folder = oFso.GetFolder(sFolder)
If (Folder.Subfolders.Count = 0) AND (Folder.Files.Count = 0) Then
Set Folder = Nothing
SmartDeleteFolder sFolder
End If
End If
Next 'sFolder
End Sub 'DeleteEmptyFolders
'=======================================================================================================
'Wrapper to delete a folder and remove the empty parent folder structure
Sub SmartDeleteFolder(sFolder)
If oFso.FolderExists(sFolder) Then
Log "Request SmartDelete for folder: " & sFolder
SmartDeleteFolderEx sFolder
End If
If b64 AND oFso.FolderExists(Wow64Folder(sFolder)) Then
Log "Request SmartDelete for folder: " & Wow64Folder(sFolder)
SmartDeleteFolderEx Wow64Folder(sFolder)
End If
End Sub 'SmartDeleteFolder
'=======================================================================================================
'Executes the folder delete operation
Sub SmartDeleteFolderEx(sFolder)
Dim Folder
On Error Resume Next
DeleteFolder sFolder : CheckError "SmartDeleteFolderEx"
On Error Goto 0
Set Folder = oFso.GetFolder(oFso.GetParentFolderName(sFolder))
If (Folder.Subfolders.Count = 0) AND (Folder.Files.Count = 0) Then SmartDeleteFolderEx(Folder.Path)
End Sub 'SmartDeleteFolderEx
'=======================================================================================================
'Handles additional folder-path operations on 64 bit environments
Function Wow64Folder(sFolder)
If LCase(Left(sFolder,Len(sWinDir & "\System32"))) = LCase(sWinDir & "\System32") Then
Wow64Folder = sWinDir & "\syswow64" & Right(sFolder,Len(sFolder)-Len(sSys32Dir))
ElseIf LCase(Left(sFolder,Len(sProgramFiles))) = LCase(sProgramFiles) Then
Wow64Folder = sProgramFilesX86 & Right(sFolder,Len(sFolder)-Len(sProgramFiles))
Else
Wow64Folder = "?" 'Return invalid string to ensure the folder cannot exist
End If
End Function 'Wow64Folder
'=======================================================================================================
Function HiveString(hDefKey)
On Error Resume Next
Select Case hDefKey
Case HKCR : HiveString = "HKEY_CLASSES_ROOT"
Case HKCU : HiveString = "HKEY_CURRENT_USER"
Case HKLM : HiveString = "HKEY_LOCAL_MACHINE"
Case HKU : HiveString = "HKEY_USERS"
Case Else : HiveString = hDefKey
End Select
End Function
'=======================================================================================================
Function RegKeyExists(hDefKey,sSubKeyName)
Dim arrKeys
RegKeyExists = False
If oReg.EnumKey(hDefKey,sSubKeyName,arrKeys) = 0 Then RegKeyExists = True
End Function
'=======================================================================================================
Function RegValExists(hDefKey,sSubKeyName,sName)
Dim arrValueTypes, arrValueNames
Dim i
RegValExists = False
If Not RegKeyExists(hDefKey,sSubKeyName) Then Exit Function
If oReg.EnumValues(hDefKey,sSubKeyName,arrValueNames,arrValueTypes) = 0 AND IsArray(arrValueNames) Then
For i = 0 To UBound(arrValueNames)
If LCase(arrValueNames(i)) = Trim(LCase(sName)) Then RegValExists = True
Next
End If 'oReg.EnumValues
End Function
'=======================================================================================================
'Read the value of a given registry entry
Function RegReadValue(hDefKey, sSubKeyName, sName, sValue, sType)
Dim RetVal
Dim Item
Dim arrValues
Select Case UCase(sType)
Case "REG_SZ"
RetVal = oReg.GetStringValue(hDefKey,sSubKeyName,sName,sValue)
If Not RetVal = 0 AND b64 Then RetVal = oReg.GetStringValue(hDefKey,Wow64Key(hDefKey, sSubKeyName),sName,sValue)
Case "REG_EXPAND_SZ"
RetVal = oReg.GetExpandedStringValue(hDefKey,sSubKeyName,sName,sValue)
If Not RetVal = 0 AND b64 Then RetVal = oReg.GetExpandedStringValue(hDefKey,Wow64Key(hDefKey, sSubKeyName),sName,sValue)
Case "REG_MULTI_SZ"
RetVal = oReg.GetMultiStringValue(hDefKey,sSubKeyName,sName,arrValues)
If Not RetVal = 0 AND b64 Then RetVal = oReg.GetMultiStringValue(hDefKey,Wow64Key(hDefKey, sSubKeyName),sName,arrValues)
If RetVal = 0 Then sValue = Join(arrValues,chr(34))
Case "REG_DWORD"
RetVal = oReg.GetDWORDValue(hDefKey,sSubKeyName,sName,sValue)
If Not RetVal = 0 AND b64 Then
RetVal = oReg.GetDWORDValue(hDefKey,Wow64Key(hDefKey, sSubKeyName),sName,sValue)
End If
Case "REG_BINARY"
RetVal = oReg.GetBinaryValue(hDefKey,sSubKeyName,sName,sValue)
If Not RetVal = 0 AND b64 Then RetVal = oReg.GetBinaryValue(hDefKey,Wow64Key(hDefKey, sSubKeyName),sName,sValue)
Case "REG_QWORD"
RetVal = oReg.GetQWORDValue(hDefKey,sSubKeyName,sName,sValue)
If Not RetVal = 0 AND b64 Then RetVal = oReg.GetQWORDValue(hDefKey,Wow64Key(hDefKey, sSubKeyName),sName,sValue)
Case Else
RetVal = -1
End Select 'sValue
RegReadValue = (RetVal = 0)
End Function 'RegReadValue
'=======================================================================================================
'Enumerate a registry key to return all values
Function RegEnumValues(hDefKey,sSubKeyName,arrNames, arrTypes)
Dim RetVal, RetVal64
Dim arrNames32, arrNames64, arrTypes32, arrTypes64
If b64 Then
RetVal = oReg.EnumValues(hDefKey,sSubKeyName,arrNames32,arrTypes32)
RetVal64 = oReg.EnumValues(hDefKey,Wow64Key(hDefKey, sSubKeyName),arrNames64,arrTypes64)
If (RetVal = 0) AND (Not RetVal64 = 0) AND IsArray(arrNames32) AND IsArray(arrTypes32) Then
arrNames = arrNames32
arrTypes = arrTypes32
End If
If (Not RetVal = 0) AND (RetVal64 = 0) AND IsArray(arrNames64) AND IsArray(arrTypes64) Then
arrNames = arrNames64
arrTypes = arrTypes64
End If
If (RetVal = 0) AND (RetVal64 = 0) AND IsArray(arrNames32) AND IsArray(arrNames64) AND IsArray(arrTypes32) AND IsArray(arrTypes64) Then
arrNames = RemoveDuplicates(Split((Join(arrNames32,"\") & "\" & Join(arrNames64,"\")),"\"))
arrTypes = RemoveDuplicates(Split((Join(arrTypes32,"\") & "\" & Join(arrTypes64,"\")),"\"))
End If
Else
RetVal = oReg.EnumValues(hDefKey,sSubKeyName,arrNames,arrTypes)
End If 'b64
RegEnumValues = ((RetVal = 0) OR (RetVal64 = 0)) AND IsArray(arrNames) AND IsArray(arrTypes)
End Function 'RegEnumValues
'=======================================================================================================
'Enumerate a registry key to return all subkeys
Function RegEnumKey(hDefKey,sSubKeyName,arrKeys)
Dim RetVal, RetVal64
Dim arrKeys32, arrKeys64
If b64 Then
RetVal = oReg.EnumKey(hDefKey,sSubKeyName,arrKeys32)
RetVal64 = oReg.EnumKey(hDefKey,Wow64Key(hDefKey, sSubKeyName),arrKeys64)
If (RetVal = 0) AND (Not RetVal64 = 0) AND IsArray(arrKeys32) Then arrKeys = arrKeys32
If (Not RetVal = 0) AND (RetVal64 = 0) AND IsArray(arrKeys64) Then arrKeys = arrKeys64
If (RetVal = 0) AND (RetVal64 = 0) Then
If IsArray(arrKeys32) AND IsArray (arrKeys64) Then
arrKeys = RemoveDuplicates(Split((Join(arrKeys32,"\") & "\" & Join(arrKeys64,"\")),"\"))
ElseIf IsArray(arrKeys64) Then
arrKeys = arrKeys64
Else
arrKeys = arrKeys32
End If
End If
Else
RetVal = oReg.EnumKey(hDefKey,sSubKeyName,arrKeys)
End If 'b64
RegEnumKey = ((RetVal = 0) OR (RetVal64 = 0)) AND IsArray(arrKeys)
End Function 'RegEnumKey
'=======================================================================================================
'Wrapper around oReg.DeleteValue to handle 64 bit
Sub RegDeleteValue(hDefKey, sSubKeyName, sName)
Dim sWow64Key
If RegValExists(hDefKey,sSubKeyName,sName) Then
Log " - Delete registry value: " & HiveString(hDefKey) & "\" & sSubKeyName & " -> " & sName
On Error Resume Next
If Not bSimulate Then oReg.DeleteValue hDefKey, sSubKeyName, sName : CheckError "RegDeleteValue"
On Error Goto 0
End If 'RegValExists
If b64 Then
sWow64Key = Wow64Key(hDefKey, sSubKeyName)
If RegValExists(hDefKey,sWow64Key,sName) Then
Log " - Delete registry value: " & HiveString(hDefKey) & "\" & sWow64Key & " -> " & sName
On Error Resume Next
If Not bSimulate Then oReg.DeleteValue hDefKey, sWow64Key, sName
On Error Goto 0
End If 'RegKeyExists
End If
End Sub 'RegDeleteValue
'=======================================================================================================
'Wrappper around RegDeleteKeyEx to handle 64bit scenrios
Sub RegDeleteKey(hDefKey, sSubKeyName)
Dim sWow64Key
If RegKeyExists(hDefKey, sSubKeyName) Then
Log " - Delete registry value: " & HiveString(hDefKey) & "\" & sSubKeyName
On Error Resume Next
RegDeleteKeyEx hDefKey, sSubKeyName
On Error Goto 0
End If 'RegKeyExists
If b64 Then
sWow64Key = Wow64Key(hDefKey, sSubKeyName)
If RegKeyExists(hDefKey,sWow64Key) Then
Log " - Delete registry value: " & HiveString(hDefKey) & "\" & sWow64Key
On Error Resume Next
RegDeleteKeyEx hDefKey, sWow64Key
On Error Goto 0
End If 'RegKeyExists
End If
End Sub 'RegDeleteKey
'=======================================================================================================
'Recursively delete a registry structure
Sub RegDeleteKeyEx(hDefKey, sSubKeyName)
Dim arrSubkeys
Dim sSubkey
On Error Resume Next
oReg.EnumKey hDefKey, sSubKeyName, arrSubkeys
If IsArray(arrSubkeys) Then
For Each sSubkey In arrSubkeys
RegDeleteKeyEx hDefKey, sSubKeyName & "\" & sSubkey
Next
End If
If Not bSimulate Then oReg.DeleteKey hDefKey, sSubKeyName
End Sub 'RegDeleteKeyEx
'=======================================================================================================
'Return the alternate regkey location on 64bit environment
Function Wow64Key(hDefKey, sSubKeyName)
Dim iPos
Select Case hDefKey
Case HKCU
If Left(sSubKeyName,17) = "Software\Classes\" Then
Wow64Key = Left(sSubKeyName,17) & "Wow6432Node\" & Right(sSubKeyName,Len(sSubKeyName)-17)
Else
iPos = InStr(sSubKeyName,"\")
Wow64Key = Left(sSubKeyName,iPos) & "Wow6432Node\" & Right(sSubKeyName,Len(sSubKeyName)-iPos)
End If
Case HKLM
If Left(sSubKeyName,17) = "Software\Classes\" Then
Wow64Key = Left(sSubKeyName,17) & "Wow6432Node\" & Right(sSubKeyName,Len(sSubKeyName)-17)
Else
iPos = InStr(sSubKeyName,"\")
Wow64Key = Left(sSubKeyName,iPos) & "Wow6432Node\" & Right(sSubKeyName,Len(sSubKeyName)-iPos)
End If
Case Else
Wow64Key = "Wow6432Node\" & sSubKeyName
End Select 'hDefKey
End Function 'Wow64Key
'=======================================================================================================
'Remove duplicate entries from a one dimensional array
Function RemoveDuplicates(Array)
Dim Item
Dim oDic
Set oDic = CreateObject("Scripting.Dictionary")
For Each Item in Array
If Not oDic.Exists(Item) Then oDic.Add Item,Item
Next 'Item
RemoveDuplicates = oDic.Keys
End Function 'RemoveDuplicates
'=======================================================================================================
'Delete a service
Function DeleteService(sService)
Dim Services, Service, Processes, Process
Dim sQuery, sStates
On Error Resume Next
sStates = "STARTED;RUNNING"
sQuery = "Select * From Win32_Service Where Name='" & sService & "'"
Set Services = oWmiLocal.Execquery(sQuery)
'Stop and delete the service
For Each Service in Services
If InStr(sStates,UCase(Service.State))>0 Then
Log "Send stop command to service: " & sService
Service.StopService
End If
'Ensure no more instances of the service are running
Set Processes = oWmiLocal.ExecQuery("Select * From Win32_Process")
For Each Process in Processes
If LCase(Left(Process.Name,Len(sService)))=sService Then
Log "Terminating running process: " & Process.Name
Process.Terminate
End If
Next 'Process
Log " - Deleting Service -> " & sService
If Not bSimulate Then Service.Delete
Next 'Service
Set Services = Nothing
End Function 'DeleteService
'=======================================================================================================
'Translation for setup.exe error codes
Function SetupExeRetVal(RetVal)
Select Case RetVal
Case 0 : SetupExeRetVal = "Success"
Case 30001,1 : SetupExeRetVal = "AbstractMethod"
Case 30002,2 : SetupExeRetVal = "ApiProhibited"
Case 30003,3 : SetupExeRetVal = "AlreadyImpersonatingAUser"
Case 30004,4 : SetupExeRetVal = "AlreadyInitialized"
Case 30005,5 : SetupExeRetVal = "ArgumentNullException"
Case 30006,6 : SetupExeRetVal = "AssertionFailed"
Case 30007,7 : SetupExeRetVal = "CABFileAddFailed"
Case 30008,8 : SetupExeRetVal = "CommandFailed"
Case 30009,9 : SetupExeRetVal = "ConcatenationFailed"
Case 30010,10 : SetupExeRetVal = "CopyFailed"
Case 30011,11 : SetupExeRetVal = "CreateEventFailed"
Case 30012,12 : SetupExeRetVal = "CustomizationPatchNotFound"
Case 30013,13 : SetupExeRetVal = "CustomizationPatchNotApplicable"
Case 30014,14 : SetupExeRetVal = "DuplicateDefinition"
Case 30015,15 : SetupExeRetVal = "ErrorCodeOnly - Passthrough for Win32 error"
Case 30016,16 : SetupExeRetVal = "ExceptionNotThrown"
Case 30017,17 : SetupExeRetVal = "FailedToImpersonateUser"
Case 30018,18 : SetupExeRetVal = "FailedToInitializeFlexDataSource"
Case 30019,19 : SetupExeRetVal = "FailedToStartClassFactories"
Case 30020,20 : SetupExeRetVal = "FileNotFound"
Case 30021,21 : SetupExeRetVal = "FileNotOpen"
Case 30022,22 : SetupExeRetVal = "FlexDialogAlreadyInitialized"
Case 30023,23 : SetupExeRetVal = "HResultOnly - Passthrough for HRESULT errors"
Case 30024,24 : SetupExeRetVal = "HWNDNotFound"
Case 30025,25 : SetupExeRetVal = "IncompatibleCacheAction"
Case 30026,26 : SetupExeRetVal = "IncompleteProductAddOns"
Case 30027,27 : SetupExeRetVal = "InstalledProductStateCorrupt"
Case 30028,28 : SetupExeRetVal = "InsufficientBuffer"
Case 30029,29 : SetupExeRetVal = "InvalidArgument"
Case 30030,30 : SetupExeRetVal = "InvalidCDKey"
Case 30031,31 : SetupExeRetVal = "InvalidColumnType"
Case 30032,31 : SetupExeRetVal = "InvalidConfigAddLanguage"
Case 30033,33 : SetupExeRetVal = "InvalidData"
Case 30034,34 : SetupExeRetVal = "InvalidDirectory"
Case 30035,35 : SetupExeRetVal = "InvalidFormat"
Case 30036,36 : SetupExeRetVal = "InvalidInitialization"
Case 30037,37 : SetupExeRetVal = "InvalidMethod"
Case 30038,38 : SetupExeRetVal = "InvalidOperation"
Case 30039,39 : SetupExeRetVal = "InvalidParameter"
Case 30040,40 : SetupExeRetVal = "InvalidProductFromARP"
Case 30041,41 : SetupExeRetVal = "InvalidProductInConfigXml"
Case 30042,42 : SetupExeRetVal = "InvalidReference"
Case 30043,43 : SetupExeRetVal = "InvalidRegistryValueType"
Case 30044,44 : SetupExeRetVal = "InvalidXMLProperty"
Case 30045,45 : SetupExeRetVal = "InvalidMetadataFile"
Case 30046,46 : SetupExeRetVal = "LogNotInitialized"
Case 30047,47 : SetupExeRetVal = "LogAlreadyInitialized"
Case 30048,48 : SetupExeRetVal = "MissingXMLNode"
Case 30049,49 : SetupExeRetVal = "MsiTableNotFound"
Case 30050,50 : SetupExeRetVal = "MsiAPICallFailure"
Case 30051,51 : SetupExeRetVal = "NodeNotOfTypeElement"
Case 30052,52 : SetupExeRetVal = "NoMoreGraceBoots"
Case 30053,53 : SetupExeRetVal = "NoProductsFound"
Case 30054,54 : SetupExeRetVal = "NoSupportedCulture"
Case 30055,55 : SetupExeRetVal = "NotYetImplemented"
Case 30056,56 : SetupExeRetVal = "NotAvailableCulture"
Case 30057,57 : SetupExeRetVal = "NotCustomizationPatch"
Case 30058,58 : SetupExeRetVal = "NullReference"
Case 30059,59 : SetupExeRetVal = "OCTPatchForbidden"
Case 30060,60 : SetupExeRetVal = "OCTWrongMSIDll"
Case 30061,61 : SetupExeRetVal = "OutOfBoundsIndex"
Case 30062,62 : SetupExeRetVal = "OutOfDiskSpace"
Case 30063,63 : SetupExeRetVal = "OutOfMemory"
Case 30064,64 : SetupExeRetVal = "OutOfRange"
Case 30065,65 : SetupExeRetVal = "PatchApplicationFailure"
Case 30066,66 : SetupExeRetVal = "PreReqCheckFailure"
Case 30067,67 : SetupExeRetVal = "ProcessAlreadyStarted"
Case 30068,68 : SetupExeRetVal = "ProcessNotStarted"
Case 30069,69 : SetupExeRetVal = "ProcessNotFinished"
Case 30070,70 : SetupExeRetVal = "ProductAlreadyDefined"
Case 30071,71 : SetupExeRetVal = "ResourceAlreadyTracked"
Case 30072,72 : SetupExeRetVal = "ResourceNotFound"
Case 30073,73 : SetupExeRetVal = "ResourceNotTracked"
Case 30074,74 : SetupExeRetVal = "SQLAlreadyConnected"
Case 30075,75 : SetupExeRetVal = "SQLFailedToAllocateHandle"
Case 30076,76 : SetupExeRetVal = "SQLFailedToConnect"
Case 30077,77 : SetupExeRetVal = "SQLFailedToExecuteStatement"
Case 30078,78 : SetupExeRetVal = "SQLFailedToRetrieveData"
Case 30079,79 : SetupExeRetVal = "SQLFailedToSetAttribute"
Case 30080,80 : SetupExeRetVal = "StorageNotCreated"
Case 30081,81 : SetupExeRetVal = "StreamNameTooLong"
Case 30082,82 : SetupExeRetVal = "SystemError"
Case 30083,83 : SetupExeRetVal = "ThreadAlreadyStarted"
Case 30084,84 : SetupExeRetVal = "ThreadNotStarted"
Case 30085,85 : SetupExeRetVal = "ThreadNotFinished"
Case 30086,86 : SetupExeRetVal = "TooManyProducts"
Case 30087,87 : SetupExeRetVal = "UnexpectedXMLNodeType"
Case 30088,88 : SetupExeRetVal = "UnexpectedError"
Case 30089,89 : SetupExeRetVal = "Unitialized"
Case 30090,90 : SetupExeRetVal = "UserCancel"
Case 30091,91 : SetupExeRetVal = "ExternalCommandFailed"
Case 30092,92 : SetupExeRetVal = "SPDatabaseOverSize"
Case 30093,93 : SetupExeRetVal = "IntegerTruncation"
'msiexec return values
Case 1259 : SetupExeRetVal = "APPHELP_BLOCK"
Case 1601 : SetupExeRetVal = "INSTALL_SERVICE_FAILURE"
Case 1602 : SetupExeRetVal = "INSTALL_USEREXIT"
Case 1603 : SetupExeRetVal = "INSTALL_FAILURE"
Case 1604 : SetupExeRetVal = "INSTALL_SUSPEND"
Case 1605 : SetupExeRetVal = "UNKNOWN_PRODUCT"
Case 1606 : SetupExeRetVal = "UNKNOWN_FEATURE"
Case 1607 : SetupExeRetVal = "UNKNOWN_COMPONENT"
Case 1608 : SetupExeRetVal = "UNKNOWN_PROPERTY"
Case 1609 : SetupExeRetVal = "INVALID_HANDLE_STATE"
Case 1610 : SetupExeRetVal = "BAD_CONFIGURATION"
Case 1611 : SetupExeRetVal = "INDEX_ABSENT"
Case 1612 : SetupExeRetVal = "INSTALL_SOURCE_ABSENT"
Case 1613 : SetupExeRetVal = "INSTALL_PACKAGE_VERSION"
Case 1614 : SetupExeRetVal = "PRODUCT_UNINSTALLED"
Case 1615 : SetupExeRetVal = "BAD_QUERY_SYNTAX"
Case 1616 : SetupExeRetVal = "INVALID_FIELD"
Case 1618 : SetupExeRetVal = "INSTALL_ALREADY_RUNNING"
Case 1619 : SetupExeRetVal = "INSTALL_PACKAGE_OPEN_FAILED"
Case 1620 : SetupExeRetVal = "INSTALL_PACKAGE_INVALID"
Case 1621 : SetupExeRetVal = "INSTALL_UI_FAILURE"
Case 1622 : SetupExeRetVal = "INSTALL_LOG_FAILURE"
Case 1623 : SetupExeRetVal = "INSTALL_LANGUAGE_UNSUPPORTED"
Case 1624 : SetupExeRetVal = "INSTALL_TRANSFORM_FAILURE"
Case 1625 : SetupExeRetVal = "INSTALL_PACKAGE_REJECTED"
Case 1626 : SetupExeRetVal = "FUNCTION_NOT_CALLED"
Case 1627 : SetupExeRetVal = "FUNCTION_FAILED"
Case 1628 : SetupExeRetVal = "INVALID_TABLE"
Case 1629 : SetupExeRetVal = "DATATYPE_MISMATCH"
Case 1630 : SetupExeRetVal = "UNSUPPORTED_TYPE"
Case 1631 : SetupExeRetVal = "CREATE_FAILED"
Case 1632 : SetupExeRetVal = "INSTALL_TEMP_UNWRITABLE"
Case 1633 : SetupExeRetVal = "INSTALL_PLATFORM_UNSUPPORTED"
Case 1634 : SetupExeRetVal = "INSTALL_NOTUSED"
Case 1635 : SetupExeRetVal = "PATCH_PACKAGE_OPEN_FAILED"
Case 1636 : SetupExeRetVal = "PATCH_PACKAGE_INVALID"
Case 1637 : SetupExeRetVal = "PATCH_PACKAGE_UNSUPPORTED"
Case 1638 : SetupExeRetVal = "PRODUCT_VERSION"
Case 1639 : SetupExeRetVal = "INVALID_COMMAND_LINE"
Case 1640 : SetupExeRetVal = "INSTALL_REMOTE_DISALLOWED"
Case 1641 : SetupExeRetVal = "SUCCESS_REBOOT_INITIATED"
Case 1642 : SetupExeRetVal = "PATCH_TARGET_NOT_FOUND"
Case 1643 : SetupExeRetVal = "PATCH_PACKAGE_REJECTED"
Case 1644 : SetupExeRetVal = "INSTALL_TRANSFORM_REJECTED"
Case 1645 : SetupExeRetVal = "INSTALL_REMOTE_PROHIBITED"
Case 1646 : SetupExeRetVal = "PATCH_REMOVAL_UNSUPPORTED"
Case 1647 : SetupExeRetVal = "UNKNOWN_PATCH"
Case 1648 : SetupExeRetVal = "PATCH_NO_SEQUENCE"
Case 1649 : SetupExeRetVal = "PATCH_REMOVAL_DISALLOWED"
Case 1650 : SetupExeRetVal = "INVALID_PATCH_XML"
Case 3010 : SetupExeRetVal = "SUCCESS_REBOOT_REQUIRED"
Case Else : SetupExeRetVal = "Unknown Return Value"
End Select
End Function 'SetupExeRetVal
'=======================================================================================================
Function GetProductID(sProdID)
Dim sReturn
Select Case sProdId
Case "0010" : sReturn = "WEBFLDRS"
Case "0011" : sReturn = "PROPLUS"
Case "0012" : sReturn = "STANDARD"
Case "0013" : sReturn = "BASIC"
Case "0014" : sReturn = "PRO"
Case "0015" : sReturn = "ACCESS"
Case "0016" : sReturn = "EXCEL"
Case "0017" : sReturn = "SharePointDesigner"
Case "0018" : sReturn = "PowerPoint"
Case "0019" : sReturn = "Publisher"
Case "001A" : sReturn = "Outlook"
Case "001B" : sReturn = "Word"
Case "001C" : sReturn = "AccessRuntime"
Case "001F" : sReturn = "Proof"
Case "0020" : sReturn = "O2007CNV"
Case "0021" : sReturn = "VisualWebDeveloper"
Case "0026" : sReturn = "ExpressionWeb"
Case "0029" : sReturn = "Excel"
Case "002A" : sReturn = "Office64"
Case "002B" : sReturn = "Word"
Case "002C" : sReturn = "Proofing"
Case "002E" : sReturn = "Ultimate"
Case "002F" : sReturn = "HomeAndStudent"
Case "0028" : sReturn = "IME"
Case "0030" : sReturn = "Enterprise"
Case "0031" : sReturn = "ProfessionalHybrid"
Case "0033" : sReturn = "Personal"
Case "0035" : sReturn = "ProfessionalHybrid"
Case "0037" : sReturn = "PowerPoint"
Case "003A" : sReturn = "PrjStd"
Case "003B" : sReturn = "PrjPro"
Case "0044" : sReturn = "InfoPath"
Case "0045" : sReturn = "XWEB"
Case "004A" : sReturn = "OWC11"
Case "0051" : sReturn = "VISPRO"
Case "0052" : sReturn = "VisView"
Case "0053" : sReturn = "VisStd"
Case "0054" : sReturn = "VisMUI"
Case "0055" : sReturn = "VisMUI"
Case "006E" : sReturn = "Shared"
Case "008A" : sReturn = "RecentDocs"
Case "00A1" : sReturn = "ONENOTE"
Case "00A3" : sReturn = "OneNoteHomeStudent"
Case "00A7" : sReturn = "CPAO"
Case "00A9" : sReturn = "InterConnect"
Case "00AF" : sReturn = "PPtView"
Case "00B0" : sReturn = "ExPdf"
Case "00B1" : sReturn = "ExXps"
Case "00B2" : sReturn = "ExPdfXps"
Case "00B4" : sReturn = "PrjMUI"
Case "00B5" : sReturn = "PrjtMUI"
Case "00B9" : sReturn = "AER"
Case "00BA" : sReturn = "Groove"
Case "00CA" : sReturn = "SmallBusiness"
Case "00E0" : sReturn = "Outlook"
Case "00D1" : sReturn = "ACE"
Case "0100" : sReturn = "OfficeMUI"
Case "0101" : sReturn = "OfficeXMUI"
Case "0103" : sReturn = "PTK"
Case "0114" : sReturn = "GrooveSetupMetadata"
Case "0115" : sReturn = "SharedSetupMetadata"
Case "0116" : sReturn = "SharedSetupMetadata"
Case "0117" : sReturn = "AccessSetupMetadata"
Case "011A" : sReturn = "LWConnect"
Case "011F" : sReturn = "OLConnect"
Case "1014" : sReturn = "STS"
Case "1015" : sReturn = "WSSMUI"
Case "1032" : sReturn = "PJSVRAPP"
Case "104B" : sReturn = "SPS"
Case "104E" : sReturn = "SPSMUI"
Case "107F" : sReturn = "OSrv"
Case "1080" : sReturn = "OSrv"
Case "1088" : sReturn = "lpsrvwfe"
Case "10D7" : sReturn = "IFS"
Case "10D8" : sReturn = "IFSMUI"
Case "10EB" : sReturn = "DLCAPP"
Case "10F5" : sReturn = "XLSRVAPP"
Case "10F6" : sReturn = "XlSrvWFE"
Case "10F7" : sReturn = "DLC"
Case "10F8" : sReturn = "SlSrvMui"
Case "10FB" : sReturn = "OSrchWFE"
Case "10FC" : sReturn = "OSRCHAPP"
Case "10FD" : sReturn = "OSrchMUI"
Case "1103" : sReturn = "DLC"
Case "1104" : sReturn = "LHPSRV"
Case "1105" : sReturn = "PIA"
Case "110D" : sReturn = "OSERVER"
Case "110F" : sReturn = "PSERVER"
Case "1110" : sReturn = "WSS"
Case "1121" : sReturn = "SPSSDK"
Case "1122" : sReturn = "SPSDev"
Case Else : sReturn = ""
End Select 'sProdId
GetProductID = sReturn
End Function 'GetProductID
'=======================================================================================================
Sub Log (sLog)
wscript.echo sLog
LogStream.WriteLine sLog
End Sub 'Log
'=======================================================================================================
Sub CheckError(sModule)
If Err <> 0 Then
Log Now & " - " & sModule & " - Source: " & Err.Source & "; Err# (Hex): " & Hex( Err ) & _
"; Err# (Dec): " & Err & "; Description : " & Err.Description
End If 'Err = 0
Err.Clear
End Sub
'=======================================================================================================
'Command line parser
Sub ParseCmdLine
Dim iCnt, iArgCnt
Dim arrArguments
Dim sArg0
iArgCnt = Wscript.Arguments.Count
If iArgCnt = 0 Then
'Create the log
CreateLog
Log "No argument specified. Preparing user prompt"
FindInstalledO12Products
If IsArray(arrInstalledSKUs) Then sDefault = Join(arrInstalledSKUs,",") Else sDefault = "ALL"
sDefault = InputBox("Enter a list of Office 2007 products to remove" & vbCrLf & vbCrLf & _
"Examples:" & vbCrLf & _
"ALL" & vbTab & vbTab & "-> remove all of Office 2007" & vbCrLf & _
"ProPlus,Project" & vbTab & "-> remove ProPlus and Project" & vbCrLf &_
"?" & vbTab & vbTab & "-> display Help", _
"Products to remove", _
sDefault)
If IsEmpty(sDefault) Then 'User cancelled
Log "User cancelled. CleanUp & Exit."
'Undo temporary entries created in ARP
TmpKeyCleanUp
wscript.quit
End If 'IsEmpty(sDefault)
Log "Answer from prompt: " & sDefault
sDefault = Trim(UCase(Trim(Replace(sDefault,Chr(34),""))))
arrArguments = Split(Trim(sDefault)," ")
If UBound(arrArguments) = -1 Then ReDim arrArguments(0)
Else
ReDim arrArguments(iArgCnt-1)
For iCnt = 0 To (iArgCnt-1)
arrArguments(iCnt) = UCase(Wscript.Arguments(iCnt))
Next 'iCnt
End If
'Handle the SKU list
sArg0 = Replace(arrArguments(0),"/","")
sArg0 = Replace(sArg0,"-","")
Select Case sArg0
Case "?"
ShowSyntax
Case "ALL"
bRemoveAll = True
bRemoveOSE = False
Case "ALL,OSE"
bRemoveAll = True
bRemoveOSE = True
Case Else
bRemoveAll = False
bRemoveOSE = False
sSkuRemoveList = arrArguments(0)
End Select
For iCnt = 0 To UBound(arrArguments)
Select Case arrArguments(iCnt)
Case "?","/?","-?"
ShowSyntax
Case "/B","/BYPASS"
If UBound(arrArguments)>iCnt Then
If InStr(arrArguments(iCnt+1),"1")>0 Then bBypass_Stage1 = True
If InStr(arrArguments(iCnt+1),"2")>0 Then bBypass_Stage2 = True
If InStr(arrArguments(iCnt+1),"3")>0 Then bBypass_Stage3 = True
If InStr(arrArguments(iCnt+1),"4")>0 Then bBypass_Stage4 = True
End If
Case "/F","/FORCE"
bForce = True
Case "/L","/LOG"
bLogInitialized = False
If UBound(arrArguments)>iCnt Then
If oFso.FolderExists(arrArguments(iCnt+1)) Then
sLogDir = arrArguments(iCnt+1)
Else
On Error Resume Next
oFso.CreateFolder(arrArguments(iCnt+1))
If Err <> 0 Then sLogDir = sScrubDir Else sLogDir = arrArguments(iCnt+1)
End If
End If
Case "/N","/NOCANCEL"
bNoCancel = True
Case "/O","/OSE"
bRemoveOSE = True
Case "/Q","/QUIET"
bQuiet = True
Case "/P","/PREVIEW"
bSimulate = True
Case Else
End Select
Next 'iCnt
If Not bLogInitialized Then CreateLog
End Sub 'ParseCmdLine
'=======================================================================================================
Sub CreateLog
Dim DateTime
'Create the log file
Set DateTime = CreateObject("WbemScripting.SWbemDateTime")
DateTime.SetVarDate Now,True
On Error Resume Next
Set LogStream = oFso.CreateTextFile(sLogDir & "\" & oWShell.ExpandEnvironmentStrings("%COMPUTERNAME%") & _
"_" & Left(DateTime.Value,14) & "_ScrubLog.txt",True,True)
If Err <> 0 Then
Err.Clear
sLogDir = sScrubDir
Set LogStream = oFso.CreateTextFile(sLogDir & "\" & oWShell.ExpandEnvironmentStrings("%COMPUTERNAME%") & _
"_" & Left(DateTime.Value,14) & "_ScrubLog.txt",True,True)
End If
Log "Microsoft Customer Support Services - Office 2007 Removal Utility" & vbCrLf & vbCrLf & _
"Version: " & VERSION & vbCrLf & _
"64 bit OS: " & b64 & vbCrLf & _
"Start removal: " & Now & vbCrLf
bLogInitialized = True
End Sub 'CreateLog
'=======================================================================================================
Sub RelaunchAsCScript
Dim Argument
Dim sCmdLine
sCmdLine = "cmd.exe /k " & WScript.Path & "\cscript.exe //NOLOGO " & Chr(34) & WScript.scriptFullName & Chr(34)
If Wscript.Arguments.Count > 0 Then
For Each Argument in Wscript.Arguments
sCmdLine = sCmdLine & " " & chr(34) & Argument & chr(34)
Next 'Argument
End If
oWShell.Run sCmdLine,1,False
Wscript.Quit
End Sub 'RelaunchAsCScript
'=======================================================================================================
'Show the expected syntax for the script usage
Sub ShowSyntax
TmpKeyCleanUp
Wscript.Echo sErr & vbCrLf & _
"OffScrub07 V " & VERSION & vbCrLf & _
"Copyright (c) Microsoft Corporation. All Rights Reserved" & vbCrLf & vbCrLf & _
"OffScrub07 helps to remove Office 2007 when a regular uninstall is no longer possible" & vbCrLf & vbCrLf & _
"Usage:" & vbTab & "OffScrub07.vbs [List of config ProductIDs] [Options]" & vbCrLf & vbCrLf & _
vbTab & "OffScrub07.vbs ALL ' Remove all Office 2007 products" & vbCrLf &_
vbTab & "OffScrub07.vbs ProPlus,Project ' Remove ProPlus and Project" & vbCrLf &_
vbTab & "OffScrub07.vbs ALL,OSE ' Remove all products & OSE Service" & vbCrLf &_
vbTab & "/Bypass [List of stage#] ' List of stages that should not run" & vbCrLf & vbCrLf &_
vbTab & vbTab & "1 = Component Detection" & vbCrLf &_
vbTab & vbTab & "2 = Setup.exe" & vbCrLf &_
vbTab & vbTab & "3 = Msiexec.exe" & vbCrLf &_
vbTab & vbTab & "4 = CleanUp of additonal files and registry settings" & vbCrLf & vbCrLf &_
vbTab & "/? ' Displays this help"& vbCrLf &_
vbTab & "/Force ' Forces termination of running processes. May cause data loss!" & vbCrLf &_
vbTab & "/Log [LogfolderPath] ' Custom folder for log files" & vbCrLf & _
vbTab & "/NoCancel ' Setup.exe and Msiexec.exe have no Cancel button" & vbCrLf &_
vbTab & "/OSE ' Forces removal of the Office Source Engine service" & vbCrLf &_
vbTab & "/Quiet ' Setup.exe and Msiexec.exe run quiet with no UI" & vbCrLf &_
vbTab & "/Preview ' Run this script to preview what would get removed"
Wscript.Quit
End Sub 'ShowSyntax
'=======================================================================================================
'=======================================================================================================
' Name: OffScrub10.vbs
' Author: Microsoft Customer Support Services
' Copyright (c) 2009,2010 Microsoft Corporation
' Script to remove (scrub) Office 2010 products
'=======================================================================================================
Option Explicit
Const SCRIPTVERSION = "1.36_fixit"
Const SCRIPTFILE = "OffScrub10.vbs"
Const SCRIPTNAME = "OffScrub10"
Const RETVALFILE = "ScrubRetValFile.txt"
Const OVERSION = "14.0"
Const OVERSIONMAJOR = "14"
Const OREF = "Office14"
Const OREGREF = "OFFICE14."
Const ONAME = "Office 2010"
Const OPACKAGE = "PackageRefs"
Const OFFICEID = "0000000FF1CE}"
Const HKCR = &H80000000
Const HKCU = &H80000001
Const HKLM = &H80000002
Const HKU = &H80000003
Const FOR_WRITING = 2
Const PRODLEN = 13
Const COMPPERMANENT = "00000000000000000000000000000000"
Const UNCOMPRESSED = 38
Const SQUISHED = 20
Const COMPRESSED = 32
Const REG_ARP = "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\"
Const VB_YES = 6
Const MSIOPENDATABASEREADONLY = 0
Const ERROR_SUCCESS = 0 'Bit #1. 0 indicates Success. Script completed successfully
Const ERROR_FAIL = 1 'Bit #1. Failure bit. Indicates an overall script failure.
'RESERVED bit! Returned when process is killed from task manager
Const ERROR_REBOOT_REQUIRED = 2 'Bit #2. Reboot bit. If set a reboot is required
Const ERROR_USERCANCEL = 4 'Bit #3. User Cancel bit. Controlled cancel from script UI
Const ERROR_STAGE1 = 8 'Bit #4. Informational. Error in stage 1. Cleanup operation might leave some files behind
Const ERROR_STAGE2 = 16 'Bit #5. Informational. Application removal with 'Setup.exe' is no longer possible
Const ERROR_STAGE3 = 32 'Bit #6. Informational. Indicates integrity of Windows Installer metadata is in a bad state
Const ERROR_STAGE4 = 64 'Bit #7. Critical script error. Script could not apply the intended cleanup operations
Const ERROR_ELEVATION_USERDECLINED = 128 'Bit #8. Critical script error. User declined to allow mandatory script elevation
Const ERROR_ELEVATION = 256 'Bit #9. Critical script error. The attempt to elevate the process did not succeed
Const ERROR_SCRIPTINIT = 512 'Bit #10. Critical script error. Initialization failed
Const ERROR_RELAUNCH = 1024'Bit #11. Critical script error. This is a temporary value and must not be the final return code
Const ERROR_UNKNOWN = 2048'Bit #12 Critical script error. Script did not complete in a well defined state
Const ERROR_ALL = 4095'Full BitMask
Const ERROR_USER_ABORT = &HC000013A 'RESERVED. Dec -1073741510. Critical error. Returned when user aborts with <Ctrl>+<Break> or closes the cmd window
Const ERROR_INSTALL_FAILURE = 1603
Const INVALID_COMMAND_LINE = 1639
Const INSTALL_ALREADY_RUNNING = 1618
Const ERROR_SUCCESS_CONFIG_COMPLETE = 1728
Const ERROR_SUCCESS_REBOOT_REQUIRED = 3010
'=======================================================================================================
Dim oFso, oMsi, oReg, oWShell, oWmiLocal
Dim ComputerItem, Item, LogStream, TmpKey
Dim arrTmpSKUs, arrDeleteFiles, arrDeleteFolders, arrMseFolders
Dim dicKeepProd, dicKeepLis, dicApps, dicKeepFolder, dicDelRegKey, dicKeepReg
Dim dicInstalledSku, dicRemoveSku, dicKeepSku, dicSrv, dicCSuite, dicCSingle
Dim f64,fLegacyProductFound
Dim sErr,sTmp,sSkuRemoveList,sDefault,sWinDir,sWICacheDir,sMode
Dim sAppData,sTemp,sScrubDir,sProgramFiles,sProgramFilesX86,sCommonProgramFiles,sCommonProgramFilesX86
Dim sAllusersProfile,sProgramData,sLocalAppData,sOInstallRoot
'=======================================================================================================
'Main
'=======================================================================================================
'Configure defaults
Dim iError : iError = ERROR_SUCCESS
Dim sLogDir : sLogDir = ""
Dim sMoveMessage: sMoveMessage = ""
Dim fRemoveOse : fRemoveOse = False
Dim fRemoveOspp : fRemoveOspp = False
Dim fRemoveAll : fRemoveAll = False
Dim fRemoveC2R : fRemoveC2R = False
Dim fRemoveAppV : fRemoveAppV = False
Dim fRemoveCSuites : fRemoveCSuites = False
Dim fRemoveCSingle : fRemoveCSingle = False
Dim fRemoveSrv : fRemoveSrv = False
Dim fKeepUser : fKeepUser = True 'Default to keep per user settings
Dim fSkipSD : fSkipSD = False 'Default to not Skip the Shortcut Detection
Dim fDetectOnly : fDetectOnly = False
Dim fQuiet : fQuiet = False 'Changed default - MWJ
Dim fNoCancel : fNoCancel = True 'Changed default - MWJ
Dim fElevated : fElevated = False
Dim fTryReconcile : fTryReconcile = False
'CAUTION! -> "fForce" will kill running applications which can result in data loss! <- CAUTION
Dim fForce : fForce = True 'Default changed 2/3/2016
'CAUTION! -> "fForce" will kill running applications which can result in data loss! <- CAUTION
Dim fLogInitialized : fLogInitialized = False
Dim fBypass_Stage1 : fBypass_Stage1 = False 'Component Detection
Dim fBypass_Stage2 : fBypass_Stage2 = False 'Setup
Dim fBypass_Stage3 : fBypass_Stage3 = False 'Msiexec
Dim fBypass_Stage4 : fBypass_Stage4 = False 'CleanUp
Dim fRebootRequired : fRebootRequired = False
'Create required objects
Set oWmiLocal = GetObject("winmgmts:\\.\root\cimv2")
Set oWShell = CreateObject("Wscript.Shell")
Set oFso = CreateObject("Scripting.FileSystemObject")
Set oMsi = CreateObject("WindowsInstaller.Installer")
Set oReg = GetObject("winmgmts:\\.\root\default:StdRegProv")
'Get environment path info
sAppData = oWShell.ExpandEnvironmentStrings("%appdata%")
sLocalAppData = oWShell.ExpandEnvironmentStrings("%localappdata%")
sTemp = oWShell.ExpandEnvironmentStrings("%temp%")
sAllUsersProfile = oWShell.ExpandEnvironmentStrings("%allusersprofile%")
sProgramFiles = oWShell.ExpandEnvironmentStrings("%programfiles%")
'Deferred until after architecture check
'sProgramFilesX86 = oWShell.ExpandEnvironmentStrings("%programfiles(x86)%")
sCommonProgramFiles = oWShell.ExpandEnvironmentStrings("%commonprogramfiles%")
'Deferred until after architecture check
'sCommonProgramFilesX86 = oWShell.ExpandEnvironmentStrings("%CommonProgramFiles(x86)%")
sProgramData = oWSHell.ExpandEnvironmentStrings("%programdata%")
sWinDir = oWShell.ExpandEnvironmentStrings("%windir%")
sWICacheDir = sWinDir & "\" & "Installer"
sScrubDir = sTemp & "\" & SCRIPTNAME
'Create the temp folder
If Not oFso.FolderExists(sScrubDir) Then oFso.CreateFolder sScrubDir
'Set the default logging directory
sLogDir = sScrubDir
'Detect if we're running on a 64 bit OS
Set ComputerItem = oWmiLocal.ExecQuery("Select * from Win32_ComputerSystem")
For Each Item In ComputerItem
f64 = Instr(Left(Item.SystemType,3),"64") > 0
If f64 Then Exit For
Next
If f64 Then sProgramFilesX86 = oWShell.ExpandEnvironmentStrings("%programfiles(x86)%")
If f64 Then sCommonProgramFilesX86 = oWShell.ExpandEnvironmentStrings("%CommonProgramFiles(x86)%")
'Update error flag
SetError ERROR_SCRIPTINIT
If NOT CheckRegPermissions Then
'Try to relaunch elevated
RelaunchElevated
'Can't relaunch. Exit out
SetError ERROR_ELEVATION
If UCase(Mid(Wscript.FullName, Len(Wscript.Path) + 2, 1)) = "C" Then
If Not fLogInitialized Then CreateLog
Log "Insufficient registry access permissions - exiting"
End If
'Undo temporary entries created in ARP
TmpKeyCleanUp
' update cached error
SetRetVal iError
Wscript.Quit iError
End If
' clear error flags
ClearError ERROR_ELEVATION
ClearError ERROR_SCRIPTINIT
'Ensure CScript as engine
If Not UCase(Mid(Wscript.FullName, Len(Wscript.Path) + 2, 1)) = "C" Then RelaunchAsCScript
' set retval for file based logic. Needs to be kept on 'user abort'
SetRetVal ERROR_USER_ABORT
'Create Dictionaries
Set dicKeepProd = CreateObject("Scripting.Dictionary")
Set dicInstalledSku = CreateObject("Scripting.Dictionary")
Set dicRemoveSku = CreateObject("Scripting.Dictionary")
Set dicKeepSku = CreateObject("Scripting.Dictionary")
Set dicKeepLis = CreateObject("Scripting.Dictionary")
Set dicKeepFolder = CreateObject("Scripting.Dictionary")
Set dicApps = CreateObject("Scripting.Dictionary")
Set dicDelRegKey = CreateObject("Scripting.Dictionary")
Set dicKeepReg = CreateObject("Scripting.Dictionary")
Set dicSrv = CreateObject("Scripting.Dictionary")
Set dicCSuite = CreateObject("Scripting.Dictionary")
Set dicCSingle = CreateObject("Scripting.Dictionary")
'Call the command line parser
ParseCmdLine
'Get Office Install Folder
If NOT RegReadValue(HKLM,"SOFTWARE\Microsoft\Office\"&OVERSION&"\Common\InstallRoot","Path",sOInstallRoot,"REG_SZ") Then
sOInstallRoot = sProgramFiles & "\Microsoft Office\"&OREF
End If
'Ensure integrity of WI metadata which could fail used APIs otherwise
EnsureValidWIMetadata HKCU,"Software\Classes\Installer\Products",COMPRESSED
EnsureValidWIMetadata HKCR,"Installer\Products",COMPRESSED
EnsureValidWIMetadata HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products",COMPRESSED
EnsureValidWIMetadata HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components",COMPRESSED
EnsureValidWIMetadata HKCR,"Installer\Components",COMPRESSED
'Add initial known .exe files that might need to be closed
dicApps.Add "communicator.exe","communicator.exe"
Select Case OVERSIONMAJOR
Case "12"
Case "14"
dicApps.Add "bcssync.exe","bcssync.exe"
dicApps.Add "officesas.exe","officesas.exe"
dicApps.Add "officesasscheduler.exe","officesasscheduler.exe"
dicApps.Add "msosync.exe","msosync.exe"
dicApps.Add "onenotem.exe","onenotem.exe"
Case Else
End Select
'-------------------
'Stage # 0 - Basics |
'-------------------
'Build a list with installed/registered Office products
sTmp = "Stage # 0 " & chr(34) & "Basics" & chr(34) & " (" & Time & ")"
Log vbCrLf & sTmp & vbCrLf & String(Len(sTmp),"=") & vbCrLf
FindInstalledOProducts
If dicInstalledSku.Count > 0 Then Log "Found registered product(s): " & Join(RemoveDuplicates(dicInstalledSku.Items),",") &vbCrLf
'Validate the list of products we got from the command line if applicable
ValidateRemoveSkuList
'Log detection results
If dicRemoveSku.Count > 0 Then Log "Product(s) to be removed: " & Join(RemoveDuplicates(dicRemoveSku.Items),",")
sMode = "Selected " & ONAME & " products"
If Not dicRemoveSku.Count > 0 Then sMode = "Orphaned " & ONAME & " products"
If fRemoveAll Then sMode = "All " & ONAME & " products"
Log "Final removal mode: " & sMode
Log "Remove OSE service: " & fRemoveOse &vbCrLf
'Log preview mode if applicable
If fDetectOnly Then Log "*************************************************************************"
If fDetectOnly Then Log "* PREVIEW MODE *"
If fDetectOnly Then Log "* All uninstall and delete operations will only be logged not executed! *"
If fDetectOnly Then Log "*************************************************************************" & vbCrLf
'Check if there are legacy products installed
CheckForLegacyProducts
If fLegacyProductFound Then Log "Found legacy Office products that will not be removed." Else Log "No legacy Office products found."
'Cache .msi files
If dicRemoveSku.Count > 0 Then CacheMsiFiles
'Log Sku/Prod detection results
LogSkuResults
'Init complete. Reset the return value
ClearError ERROR_SCRIPTINIT
'--------------------------------
'Stage # 1 - Component Detection |
'--------------------------------
sTmp = "Stage # 1 " & chr(34) & "Component Detection" & chr(34) & " (" & Time & ")"
Log vbCrLf & sTmp & vbCrLf & String(Len(sTmp),"=") & vbCrLf
If Not fBypass_Stage1 Then
'Build a list with files which are installed/registered to a product that's going to be removed
Log "Prepare for CleanUp stages."
Log "Identifying removable elements. This can take several minutes."
ScanComponents
Else
Log "Skipping Component Detection because bypass was requested."
End If
'End all running Office applications
If fForce OR fQuiet Then CloseOfficeApps
'----------------------
'Stage # 2 - Setup.exe |
'----------------------
sTmp = "Stage # 2 " & chr(34) & "Setup.exe" & chr(34) & " (" & Time & ")"
Log vbCrLf & sTmp & vbCrLf & String(Len(sTmp),"=") & vbCrLf
If Not fBypass_Stage2 Then
SetupExeRemoval
Else
Log "Skipping Setup.exe because bypass was requested."
End If
'------------------------
'Stage # 3 - Msiexec.exe |
'------------------------
sTmp = "Stage # 3 " & chr(34) & "Msiexec.exe" & chr(34) & " (" & Time & ")"
Log vbCrLf & sTmp & vbCrLf & String(Len(sTmp),"=") & vbCrLf
If Not fBypass_Stage3 Then
MsiexecRemoval
Else
Log "Skipping Msiexec.exe because bypass was requested."
End If
'--------------------
'Stage # 4 - CleanUp |
'--------------------
'Removal of files and registry settings
sTmp = "Stage # 4 " & chr(34) & "CleanUp" & chr(34) & " (" & Time & ")"
Log vbCrLf & sTmp & vbCrLf & String(Len(sTmp),"=") & vbCrLf
If Not fBypass_Stage4 Then
'Office Source Engine
If fRemoveOse Then RemoveOSE
'Softgrid Service
If fRemoveAppV Then RemoveSG
'Local Installation Source (MSOCache)
WipeLIS
'Obsolete files
If fRemoveAll Then
FileWipeAll
Else
FileWipeIndividual
End If
'Empty Folders
DeleteEmptyFolders
'Restore Explorer if needed
If fForce Then RestoreExplorer
'Registry data
RegWipe
'Wipe orphaned files from Windows Installer cache
MsiClearOrphanedFiles
'Temporary .msi files in scrubcache
DeleteMsiScrubCache
'Temporary files
DelScrubTmp
Else
Log "Skipping CleanUp because bypass was requested."
End If
If Not sMoveMessage = "" Then Log vbCrLf & "Please remove this folder after next reboot: " & sMoveMessage
'THE END
Log vbCrLf & "End removal: " & Now & vbCrLf
Log vbCrLf & "For detailed logging please refer to the log in folder " &chr(34)&sScrubDir&chr(34)&vbCrLf
If fRebootRequired Then
Log vbCrLf & "A restart is required to complete the operation!"
If fQuiet Then 'If NOT fQuiet Then
If MsgBox("Do you want to reboot now?",vbYesNo,"Reboot Required") = VB_YES Then
Dim colOS, oOS
Dim oWmiReboot
Set oWmiReboot = GetObject("winmgmts:{impersonationLevel=impersonate,(Shutdown)}!\\.\root\cimv2")
Set colOS = oWmiReboot.ExecQuery ("Select * from Win32_OperatingSystem")
For Each oOS in colOS
oOS.Reboot()
Next
End If
End If
End If
If NOT fQuiet Then
For Each Item in Wscript.Arguments
If Item = "UAC" Then
wscript.stdout.write "Press <Enter> to close this window"
sTemp = wscript.stdin.read(1)
End If
Next 'Argument
End If
' update cached error and quit
SetRetVal iError
wscript.quit iError
'=======================================================================================================
'=======================================================================================================
'Stage 0 - 4 Subroutines
'=======================================================================================================
'Office configuration products are listed with their configuration product name in the "Uninstall" key
'To identify an Office configuration product all of these condiditions have to be met:
' - "SystemComponent" does not have a value of "1" (DWORD)
' - "OPACKAGE" (see constant declaration) entry exists and is not empty
' - "DisplayVersion" exists and the 2 leftmost digits are "OVERSIONMAJOR"
Sub FindInstalledOProducts
Dim ArpItem, File
Dim sCurKey, sValue, sConfigName, sProdC, sCVHValue
Dim sProductCodeList, sProductCode
Dim arrKeys, arrMultiSzValues
Dim fSystemComponent0, fPackages, fDisplayVersion, fReturn, fCategorized
If dicInstalledSku.Count > 0 Then Exit Sub 'Already done from InputBox prompt
'Handle orphaned products to get them added to the detection scope
If fTryReconcile Then
For Each File in oFso.GetFolder(sWICacheDir).Files
If Len(File.Name)>3 Then
Select Case LCase(Right(File.Name,4))
Case ".msi"
sProductCode = ""
sProductCode = GetMsiProductCode(File.Path)
If InScope(sProductCode) Then
If NOT RegKeyExists(HKLM,REG_ARP & sProductCode) Then
'Ensure the orphaned item is getting removed
If Len(sSkuRemoveList) > 0 Then
sSkuRemoveList = sSkuRemoveList & "," & GetProductID(Mid(sProductCode,11,4))
Else
sSkuRemoveList = GetProductID(Mid(sProductCode,11,4))
End If
'Add to ScrubDir
oFso.CopyFile File.Path,sScrubDir & "\" & prod & ".msi",True
'Register the product with MSI
MsiRegisterProduct(File.Path)
End If 'NOT sProductCode
End If 'InScope
Case Else
End Select
End If '>3
Next 'File
End If 'fTryReconcile
'Locate standalone Office products that have no configuration product entry and create a
'temporary configuration entry
ReDim arrTmpSKUs(-1)
If RegEnumKey(HKLM,REG_ARP,arrKeys) Then
For Each ArpItem in arrKeys
If InScope(ArpItem) Then
sCurKey = REG_ARP & ArpItem & "\"
fSystemComponent0 = Not (RegReadValue(HKLM,sCurKey,"SystemComponent",sValue,"REG_DWORD") AND (sValue = "1"))
If (fSystemComponent0 AND (NOT RegReadValue(HKLM,sCurKey,"CVH",sCVHValue,"REG_DWORD"))) Then
RegReadValue HKLM,sCurKey,"DisplayVersion",sValue,"REG_SZ"
Redim arrMultiSzValues(0)
'Logic changed to drop the LCID identifier
'sConfigName = GetProductID(Mid(ArpItem,11,4)) & "_" & CInt("&h" & Mid(ArpItem,16,4))
sConfigName = OREGREF & GetProductID(Mid(ArpItem,11,4))
If NOT RegKeyExists(HKLM,REG_ARP&sConfigName) Then
'Create a new ARP item
ReDim Preserve arrTmpSKUs(UBound(arrTmpSKUs)+1)
arrTmpSKUs(UBound(arrTmpSKUs)) = sConfigName
oReg.CreateKey HKLM,REG_ARP & sConfigName
arrMultiSzValues(0) = sConfigName
oReg.SetMultiStringValue HKLM,REG_ARP & sConfigName,OPACKAGE,arrMultiSzValues
arrMultiSzValues(0) = ArpItem
oReg.SetMultiStringValue HKLM,REG_ARP & sConfigName,"ProductCodes",arrMultiSzValues
oReg.SetStringValue HKLM,REG_ARP & sConfigName,"DisplayVersion",sValue
oReg.SetDWordValue HKLM,REG_ARP & sConfigName,"SystemComponent",0
Else
'Update the existing temporary ARP item
fReturn = RegReadValue(HKLM,REG_ARP&sConfigName,"ProductCodes",sProdC,"REG_MULTI_SZ")
If NOT InStr(sProdC,ArpItem)>0 Then sProdC = sProdC & chr(34) & ArpItem
oReg.SetMultiStringValue HKLM,REG_ARP & sConfigName,"ProductCodes",Split(sProdC,chr(34))
End If 'RegKeyExists
End If 'fSystemComponent0
End If 'InScope
Next 'ArpItem
End If 'RegEnumKey
'Find the configuration products
If RegEnumKey(HKLM,REG_ARP,arrKeys) Then
For Each ArpItem in arrKeys
sCurKey = REG_ARP & ArpItem & "\"
sValue = ""
fSystemComponent0 = NOT (RegReadValue(HKLM,sCurKey,"SystemComponent",sValue,"REG_DWORD") AND (sValue = "1"))
fPackages = RegReadValue(HKLM,sCurKey,OPACKAGE,sValue,"REG_MULTI_SZ")
fDisplayVersion = RegReadValue(HKLM,sCurKey,"DisplayVersion",sValue,"REG_SZ")
If fDisplayVersion Then
If Len(sValue) > 1 Then
fDisplayVersion = (Left(sValue,2) = OVERSIONMAJOR)
Else
fDisplayVersion = False
End If
End If
If (fSystemComponent0 AND fPackages AND fDisplayVersion) OR (fSystemComponent0 AND fDisplayVersion AND InStr(UCase(ArpItem),"CLICK2RUN")>0) Then
If InStr(ArpItem,".")>0 Then sConfigName = UCase(Mid(ArpItem,InStr(ArpItem,".")+1)) Else sConfigName = UCase(ArpItem)
If NOT dicInstalledSku.Exists(sConfigName) Then dicInstalledSku.Add sConfigName,sConfigName
'Categorize the SKU
'Three categories are available: ClientSuite, ClientSingleProduct, Server
If RegReadValue(HKLM,REG_ARP&OREGREF&sConfigName,"ProductCodes",sProductCodeList,"REG_MULTI_SZ") OR (sConfigName = "CLICK2RUN") Then
fCategorized = False
If sConfigName = "CLICK2RUN" Then sProductCodeList = "{90" & OVERSIONMAJOR & "0011-0062-0000-0000-0000000FF1CE}"
For Each sProductCode in Split(sProductCodeList,chr(34))
If Len(sProductCode) = 38 Then
If NOT Mid(sProductCode,11,1) = "0" Then
'Server product
If NOT dicSrv.Exists(UCase(sConfigName)) Then dicSrv.Add UCase(sConfigName),sConfigName
fCategorized = True
Exit For
Else
Select Case Mid(sProductCode,11,4)
'Client Suites
Case "000F","0011","0012","0013","0014","0015","0016","0017","0018","0019","001A","001B","0029","002B","002E","002F","0030","0031","0033","0035","0037","003D","0044","0049","0061","0062","0066","006C","006D","006F","0074","00A1","00A3","00A9","00BA","00CA","00E0","0100","0103","011A"
If NOT dicCSuite.Exists(UCase(sConfigName)) Then dicCSuite.Add UCase(sConfigName),sConfigName
fCategorized = True
Exit For
Case Else
End Select
End If
End If 'Len 38
Next 'sProductCode
If NOT fCategorized Then
If NOT dicCSingle.Exists(UCase(sConfigName)) Then dicCSingle.Add UCase(sConfigName),sConfigName
End If 'fCategorized
End If 'RegReadValue "ProductCodes"
End If
Next 'ArpItem
End If 'RegEnumKey
End Sub 'FindInstalledOProducts
'=======================================================================================================
'Check if there are Office products from previous versions on the computer
Sub CheckForLegacyProducts
Const OLEGACY = "78E1-11D2-B60F-006097C998E7}.6000-11D3-8CFE-0050048383C9}.6000-11D3-8CFE-0150048383C9}.BDCA-11D1-B7AE-00C04FB92F3D}.6D54-11D4-BEE3-00C04F990354}"
Dim Product
'Set safe default
fLegacyProductFound = True
For Each Product in oMsi.Products
If Len(Product) = 38 Then
'Handle O09 - O11 Products
If InStr(OLEGACY, UCase(Right(Product, 28)))>0 Then
'Found legacy Office product. Keep flag in default and exit
Exit Sub
End If
If UCase(Right(Product,PRODLEN))=OFFICEID Then
Select Case Mid(Product,4,2)
Case "12"
If CInt(OVERSIONMAJOR) > 12 Then
'Found legacy Office product. Keep flag in default and exit
Exit Sub
End If
Case Else
End Select
End If
End If '38
Next 'Product
fLegacyProductFound = False
End Sub 'CheckForLegacyProducts
'=======================================================================================================
'Create clean list of Products to remove.
'Strip off bad & empty contents
Sub ValidateRemoveSkuList
Dim Sku, Key, sProductCode, sProductCodeList
Dim arrRemoveSKUs
If fRemoveAll Then
'Remove all mode
For Each Key in dicInstalledSku.Keys
dicRemoveSku.Add Key,dicInstalledSku.Item(Key)
Next 'Key
Else
'Remove individual products or preconfigured configurations mode
'Ensure to have a string with no unexpected contents
sSkuRemoveList = Replace(sSkuRemoveList,";",",")
sSkuRemoveList = Replace(sSkuRemoveList," ","")
sSkuRemoveList = Replace(sSkuRemoveList,Chr(34),"")
While InStr(sSkuRemoveList,",,")>0
sSkuRemoveList = Replace(sSkuRemoveList,",,",",")
Wend
'Prepare 'remove' and 'keep' dictionaries to determine what has to be removed
'Initial pre-fill of 'keep' dic
For Each Key in dicInstalledSku.Keys
dicKeepSku.Add Key,dicInstalledSku.Item(Key)
Next 'Key
'Determine contents of keep and remove dic
'Individual products
arrRemoveSKUs = Split(UCase(sSkuRemoveList),",")
For Each Sku in arrRemoveSKUs
If Sku = "OSE" Then fRemoveOse = True
If Sku = "CLICK2RUN" Then fRemoveC2R = True
If dicKeepSku.Exists(Sku) Then
'A Sku to remove has been passed in
'remove the item from the keep dic
dicKeepSku.Remove(Sku)
'Now add it to the remove dic
If NOT dicRemoveSku.Exists(Sku) Then dicRemoveSku.Add Sku,Sku
End If
Next 'Sku
'Client Suite Category
If fRemoveCSuites Then
fRemoveC2R = True
For Each Key in dicInstalledSku.Keys
If dicCSuite.Exists(Key) Then
If dicKeepSku.Exists(Key) Then dicKeepSku.Remove(Key)
If NOT dicRemoveSku.Exists(Key) Then dicRemoveSku.Add Key,Key
End If
Next 'Key
End If 'fRemoveCSuites
'Client Single/Standalone Category
If fRemoveCSingle Then
For Each Key in dicInstalledSku.Keys
If dicCSingle.Exists(Key) Then
If dicKeepSku.Exists(Key) Then dicKeepSku.Remove(Key)
If NOT dicRemoveSku.Exists(Key) Then dicRemoveSku.Add Key,Key
End If
Next 'Key
End If 'fRemoveCSingle
'Server Category
If fRemoveSrv Then
For Each Key in dicInstalledSku.Keys
If dicSrv.Exists(Key) Then
If dicKeepSku.Exists(Key) Then dicKeepSku.Remove(Key)
If NOT dicRemoveSku.Exists(Key) Then dicRemoveSku.Add Key,Key
End If
Next 'Key
End If 'fRemoveSrv
If NOT dicKeepSku.Count > 0 Then fRemoveAll = True
End If 'fRemoveAll
'Fill the KeepProd dic
For Each Sku in dicKeepSku.Keys
If RegReadValue(HKLM,REG_ARP & OREGREF & Sku,"ProductCodes",sProductCodeList,"REG_MULTI_SZ") Then
For Each sProductCode in Split(sProductCodeList,chr(34))
If Len(sProductCode) = 38 Then
If NOT dicKeepProd.Exists(sProductCode) Then dicKeepProd.Add sProductCode,Sku
End If '38
Next 'sProductCod
End If
Next 'Sku
If fRemoveAll OR fRemoveOse Then CheckRemoveOSE
If fRemoveAll OR fRemoveOspp Then CheckRemoveOspp
If fRemoveAll OR fRemoveC2R Then CheckRemoveSG
End Sub 'ValidateRemoveSkuList
'=======================================================================================================
'Check if SoftGrid Client can be scrubbed
Sub CheckRemoveSG
Dim Key
Dim sPKey
Dim arrKeys
If NOT CInt(OVERSIONMAJOR) > 12 Then
fRemoveC2R = False
Exit Sub
End If
If fForce Then
fRemoveAppV = True
Exit Sub
End If
fRemoveAppV = False
If RegEnumKey (HKLM,"SOFTWARE\Microsoft\SoftGrid\4.5\Client\Applications",arrKeys) Then
For Each Key in arrKeys
If Len(Key)>15 Then
'Get Partial product Key
sPKey = Right(Key,16)
If Left(sPKey,4) = "90"&OVERSIONMAJOR Then
If NOT GetProductID(Mid(sPKey,5,4)) = "CLICK2RUN" Then Exit Sub
Else
Exit Sub
End If
Else
Exit Sub
End If
Next 'Key
End If
'If we got here it's only Click2Run apps
fRemoveAppV = True
End Sub 'CheckRemoveSG
'=======================================================================================================
'Check if OSE service can be scrubbed
Sub CheckRemoveOSE
Const O11 = "6000-11D3-8CFE-0150048383C9}"
Dim Product
If fRemoveOse Then Exit Sub
For Each Product in oMsi.Products
If Len(Product) = 38 Then
If UCase(Right(Product,28)) = O11 Then
'Found Office 2003 Product. Set flag to not remove the OSE service
Exit Sub
End If
If UCase(Right(Product,PRODLEN))=OFFICEID Then
Select Case Mid(Product,4,2)
Case "12","14","15","16","17"
'Found another Office product. Set flag to keep the OSE service
If NOT Mid(Product,4,2) = OVERSIONMAJOR Then
fRemoveOse = False
Exit Sub
End If
Case Else
End Select
End If
End If '38
Next 'Product
fRemoveOse = True
End Sub 'CheckRemoveOSE
'=======================================================================================================
'Check if OSPP service can be scrubbed
Sub CheckRemoveOSPP
Dim Product
If NOT CInt(OVERSIONMAJOR) > 12 Then
fRemoveOspp = False
Exit Sub
End If
If fRemoveOspp Then Exit Sub
For Each Product in oMsi.Products
If Len(Product) = 38 Then
If UCase(Right(Product,PRODLEN))=OFFICEID Then
Select Case Mid(Product,4,2)
Case "14","15","16","17"
'Found another Office product. Set flag to keep the OSPP service
If NOT Mid(Product,4,2) = OVERSIONMAJOR Then
fRemoveOspp = False
Exit Sub
End If
Case Else
End Select
End If
End If '38
Next 'Product
fRemoveOspp = True
End Sub 'CheckRemoveOSPP
'=======================================================================================================
'Cache .msi files for products that will be removed in case they are needed for later file detection
Sub CacheMsiFiles
Dim Product
Dim sMsiFile
'Non critical routine for failures.
'Errors will be logged but must not fail the execution
On Error Resume Next
Log " Cache .msi files to temporary Scrub folder"
'Cache the files
For Each Product in oMsi.Products
'Ensure valid GUID length
If InScope(Product) Then
If (fRemoveAll OR CheckDelete(Product))Then
CheckError "CacheMsiFiles"
sMsiFile = oMsi.ProductInfo(Product,"LocalPackage") : CheckError "CacheMsiFiles"
LogOnly " - " & Product & ".msi"
If oFso.FileExists(sMsiFile) Then oFso.CopyFile sMsiFile,sScrubDir & "\" & Product & ".msi",True
CheckError "CacheMsiFiles"
End If
End If 'InScope
Next 'Product
Err.Clear
End Sub 'CacheMsiFiles
'=======================================================================================================
'Build a list of all files that will be deleted
Sub ScanComponents
Const MSIINSTALLSTATE_LOCAL = 3
Dim FileList, RegList, ComponentID, CompClient, Record, qView, MsiDb
Dim Processes, Process, Prop, prod
Dim sQuery, sSubKeyName, sPath, sFile, sMsiFile, sCompClient, sComponent, sCompReg
Dim fRemoveComponent, fAffectedComponent, fIsPermanent
Dim i, iProgress, iCompCnt, iRemCnt
Dim dicFLError, oDic, oFolderDic, dicCompPath
Dim hDefKey
'Logfile
Set FileList = oFso.OpenTextFile(sScrubDir & "\FileList.txt",FOR_WRITING,True,True)
Set RegList = oFso.OpenTextFile(sScrubDir & "\RegList.txt",FOR_WRITING,True,True)
'FileListError dic
Set dicFLError = CreateObject("Scripting.Dictionary")
Set oDic = CreateObject("Scripting.Dictionary")
Set oFolderDic = CreateObject("Scripting.Dictionary")
Set dicCompPath = CreateObject("Scripting.Dictionary")
'Prevent that API errors fail script execution
On Error Resume Next
iCompCnt = oMsi.Components.Count
If NOT Err = 0 Then
'API failure
Log "Error during components detection. Cannot complete this task."
SetError ERROR_STAGE1
Err.Clear
Exit Sub
End If
'Ensure to not divide by zero
If iCompCnt = 0 Then iCompCnt = 1
LogOnly " Scanning " & iCompCnt & " components"
'Enum all Components
For Each ComponentID In oMsi.Components
'Progress bar
i = i + 1
If iProgress < (i / iCompCnt) * 100 Then
wscript.stdout.write "." : LogStream.Write "."
iProgress = iProgress + 1
If iProgress = 35 OR iProgress = 70 Then Log ""
End If
'Check if all ComponentClients will be removed
sCompClient = ""
iRemCnt = 0
fIsPermanent = False
fRemoveComponent = False 'Flag to track if the component will be completely removed
fAffectedComponent = False 'Flag to track if some clients remain installed who have a none shared location
dicCompPath.RemoveAll
For Each CompClient In oMsi.ComponentClients(ComponentID)
If Err = 0 Then
'Ensure valid guid length
If Len(CompClient) = 38 Then
sPath = ""
sPath = LCase(oMsi.ComponentPath(CompClient,ComponentID))
sPath = Replace(sPath,"?",":")
'Scan for msidbComponentAttributesPermanent flag
If CompClient = "{00000000-0000-0000-0000-000000000000}" Then
fIsPermanent = True
iRemCnt = iRemCnt + 1
End If
fRemoveComponent = InScope(CompClient)
If fRemoveComponent Then fRemoveComponent = CheckDelete(CompClient)
If fRemoveComponent Then
iRemCnt = iRemCnt + 1
fAffectedComponent = True
'Since the scope remains within one Office family the keypath for the component
'is assumed to be identical
If sCompClient = "" Then sCompClient = CompClient
Else
If NOT dicCompPath.Exists(sPath) Then dicCompPath.Add sPath,CompClient
End If
Else
If NOT dicFLError.Exists("Error: Invalid metadata found. ComponentID: "&ComponentID &", ComponentClient: "&CompClient) Then _
dicFLError.Add "Error: Invalid metadata found. ComponentID: "&ComponentID &", ComponentClient: "&CompClient, ComponentID
End If '38
Else
Err.Clear
End If 'Err = 0
Next 'CompClient
'Determine if the component resources go away
sPath = ""
fRemoveComponent = fAffectedComponent AND (iRemCnt = oMsi.ComponentClients(ComponentID).Count)
If NOT fRemoveComponent AND fAffectedComponent Then
'Flag as removable if component has a unique keypath
sPath = LCase(oMsi.ComponentPath(sCompClient,ComponentID))
sPath = Replace(sPath,"?",":")
fRemoveComponent = NOT dicCompPath.Exists(sPath)
End If
If fRemoveComponent Then
'Check msidbComponentAttributesPermanent flag
If fIsPermanent AND NOT fForce Then fRemoveComponent = False
End If
If fRemoveComponent Then
'Component resources go away for this product
Err.Clear
'Add the component registration key to ensure removal
sCompReg = "Installer\Components\"&GetCompressedGuid(ComponentID)&"\"
If NOT dicDelRegKey.Exists(sCompReg) Then
dicDelRegKey.Add sCompReg,HKCR
RegList.WriteLine HiveString(HKCR)&"\"&sCompReg
End If
sCompReg = "SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components\"&GetCompressedGuid(ComponentID)&"\"
If NOT dicDelRegKey.Exists(sCompReg) Then
dicDelRegKey.Add sCompReg,HKLM
RegList.WriteLine HiveString(HKCR)&"\"&sCompReg
End If
'Get the component path
If sPath = "" Then
sPath = LCase(oMsi.ComponentPath(sCompClient,ComponentID))
sPath = Replace(sPath,"?",":")
End If
If Len(sPath) > 4 Then
If Left(sPath,1) = "0" Then
'Registry keypath
Select Case Left(sPath,2)
Case "00"
sPath = Mid(sPath,5)
hDefKey = HKCR
Case "01"
sPath = Mid(sPath,5)
hDefKey = HKCU
Case "02","22"
sPath = Mid(sPath,5)
hDefKey = HKLM
Case Else
'
End Select
If NOT dicDelRegKey.Exists(sPath) Then
dicDelRegKey.Add sPath,hDefKey
RegList.WriteLine HiveString(hDefKey)&"\"&sPath
End If
Else
'File
If oFso.FileExists(sPath) Then
sPath = oFso.GetFile(sPath).ParentFolder
If Not oFolderDic.Exists(sPath) Then oFolderDic.Add sPath,sPath
'Get the .msi file
If oFso.FileExists(sScrubDir & "\" & sCompClient & ".msi") Then
sMsiFile = sScrubDir & "\" & sCompClient & ".msi"
Else
sMsiFile = oMsi.ProductInfo(sCompClient,"LocalPackage")
End If
If Not Err = 0 Then
If NOT dicFLError.Exists("Failed to obtain .msi file for product "&sCompClient) Then _
dicFLError.Add "Failed to obtain .msi file for product "&sCompClient, ComponentID
Err.Clear
End If
Set MsiDb = oMsi.OpenDatabase(sMsiFile,MSIOPENDATABASEREADONLY)
If Err = 0 Then
'Get the component name from the 'Component' table
sQuery = "SELECT `Component`,`ComponentId` FROM Component WHERE `ComponentId` = '" & ComponentID &"'"
Set qView = MsiDb.OpenView(sQuery) : qView.Execute
Set Record = qView.Fetch()
If Not Record Is Nothing Then sComponent = Record.Stringdata(1)
'Get filenames from the 'File' table
sQuery = "SELECT `Component_`,`FileName` FROM File WHERE `Component_` = '" & sComponent &"'"
Set qView = MsiDb.OpenView(sQuery) : qView.Execute
Set Record = qView.Fetch()
Do Until Record Is Nothing
'Read the filename
sFile = Record.StringData(2)
If InStr(sFile,"|") > 0 Then sFile = Mid(sFile,InStr(sFile,"|")+1,Len(sFile))
'sFile = sPath & "\" & sFile
If Not oDic.Exists(sPath & "\" & sFile) Then
'Exception handler
fAdd = True
Select Case UCase(sFile)
Case "FPERSON.DLL"
For Each prod in oMsi.Products
If NOT Checkdelete(prod) Then
If oMsi.FeatureState(prod, "MSTagPluginNamesFiles") = MSIINSTALLSTATE_LOCAL Then
fAdd = False
Exit For
End If
End If
Next 'prod
Case Else
End Select
If fAdd Then
oDic.Add sPath & "\" & sFile,sFile
FileList.WriteLine sFile
If Len(sFile)>4 Then
sFile = LCase(sFile)
If Right(sFile,4) = ".exe" Then
If NOT dicApps.Exists(sFile) Then
Select Case sFile
Case "setup.exe","ose.exe","osppsvc.exe","explorer.exe","cvhsvc.exe","sftvsa.exe","sftlist.exe","sftplay.exe","sftvol.exe","sftfs.exe"
Case Else
dicApps.Add sFile,LCase(sPath) & "\" & sFile
End Select
End If 'dicApps.Exists
End If '.exe
End If 'Len > 4
End If 'fAdd
End If 'oDic.Exists
Set Record = qView.Fetch()
Loop
Set Record = Nothing
qView.Close
Set qView = Nothing
Else
If NOT dicFLError.Exists("Error: Could not read from .msi file: "&sMsiFile) Then _
dicFLError.Add "Error: Could not read from .msi file: "&sMsiFile, ComponentID
Err.Clear
End If 'Err = 0
End If 'FileExists(sPath)
End If
End If 'Len(sPath) > 4
Else
'Add the path to the 'Keep' dictionary
Err.Clear
For Each CompClient In oMsi.ComponentClients(ComponentID)
'Get the component path
sPath = "" : sPath = LCase(oMsi.ComponentPath(CompClient,ComponentID))
sPath = Replace(sPath,"?",":")
If Len(sPath) > 4 Then
If Left(sPath,1) = "0" Then
'Registry keypath
Select Case Left(sPath,2)
Case "00"
sPath = Mid(sPath,5)
hDefKey = HKCR
Case "01"
sPath = Mid(sPath,5)
hDefKey = HKCU
Case "02","22"
sPath = Mid(sPath,5)
hDefKey = HKLM
Case Else
'
End Select
If NOT dicKeepReg.Exists(LCase(sPath)) Then
dicKeepReg.Add LCase(sPath),hDefKey
End If
Else
'File keypath
If oFso.FileExists(sPath) Then
If NOT dicKeepFolder.Exists(LCase(sPath)) Then dicKeepFolder.Add LCase(sPath)
sPath = LCase(oFso.GetFile(sPath).ParentFolder) & "\"
If NOT dicKeepFolder.Exists(sPath) Then AddKeepFolder sPath
End If
'Folder keypath
If oFso.FolderExists(sPath) Then AddKeepFolder sPath
End If 'Is Registry
End If 'sPath > 4
Next 'CompClient
End If 'fRemoveComponent
Next 'ComponentID
Err.Clear
On Error Goto 0
'Click2Run detection
If C2RInstalled Then
'Add executables that might need to be closed
If NOT dicApps.Exists("cvh.exe") Then dicApps.Add "cvh.exe","cvh.exe"
If NOT dicApps.Exists("officevirt.exe") Then dicApps.Add "officevirt.exe","officevirt.exe"
Set Processes = oWmiLocal.ExecQuery("Select * From Win32_Process")
For Each Process in Processes
For Each Prop in Process.Properties_
If Prop.Name = "ExecutablePath" Then
If Len(Prop.Value) > 2 Then
If UCase(Left(Prop.Value,2)) = "Q:" Then
If NOT dicApps.Exists(LCase(Process.Name)) Then dicApps.Add LCase(Process.Name),Process.Name
End If 'Q:
End If '>2
End If 'ExcecutablePath
Next 'Prop
Next 'Process
End If 'C2RInstalled
Log " Done" & vbCrLf
If dicFLError.Count > 0 Then LogOnly Join(dicFLError.Keys,vbCrLf)
If Not oFolderDic.Count = 0 Then arrDeleteFolders = oFolderDic.Keys Else Set arrDeleteFolders = Nothing
If Not oDic.Count = 0 Then arrDeleteFiles = oDic.Keys Else Set arrDeleteFiles = Nothing
End Sub 'ScanComponents
'=======================================================================================================
'Detect if Click2Run products are installed on the client
Function C2RInstalled
Dim Key, sPKey, sValue, VProd
Dim arrKeys
If RegEnumKey (HKLM,REG_ARP,arrKeys) Then
For Each Key in arrKeys
If InScope(Key)=38 Then
If RegReadValue(HKLM,REG_ARP&"\"&Key,"CVH",sValue,"REG_DWORD") Then
If sValue = "1" Then
C2RInstalled = True
Exit Function
End If
End If
End If
Next 'Key
End If
If RegEnumKey (HKLM,"SOFTWARE\Microsoft\SoftGrid\4.5\Client\Applications",arrKeys) Then
For Each Key in arrKeys
If Len(Key)>15 Then
'Get Partial product Key
sPKey = Right(Key,16)
If Left(sPKey,4) = "90" & OVERSIONMAJOR Then
If GetProductID(Mid(sPKey,5,4)) = "CLICK2RUN" Then
C2RInstalled = True
Exit Function
End If
End If
End If
Next 'Key
End If
End Function 'C2RInstalled
'=======================================================================================================
'Try to remove the products by calling setup.exe
Sub SetupExeRemoval
Dim OseService, Service, TextStream
Dim iSetupCnt, RetVal
Dim Sku, sConfigFile, sUninstallCmd, sCatalyst, sCVHBS, sDll, sDisplayLevel, sNoCancel
iSetupCnt = 0
If Not dicRemoveSku.Count > 0 Then
Log " Nothing to remove for Setup.exe"
Exit Sub
End If
For Each Sku in dicRemoveSku.Keys
If Sku="CLICK2RUN" Then
'Reset Softgrid
ResetSG
If f64 Then
sCVHBS = sCommonProgramFilesX86 & "\Microsoft Shared\Virtualization Handler\CVHBS.exe"
Else
sCVHBS = sCommonProgramFiles & "\Microsoft Shared\Virtualization Handler\CVHBS.exe"
End If
If oFso.FileExists(sCVHBS) Then
CvhbsDialogHandler
sUninstallCmd = Chr(34) & sCVHBS & Chr(34) & " /removesilent"
iSetupCnt = iSetupCnt + 1
Log " - Calling CVHBS.exe to remove " & Sku
If Not fDetectOnly Then
On Error Resume Next
RetVal = oWShell.Run(sUninstallCmd,0,True) : CheckError "CVHBSRemoval"
fRebootRequired = True
SetError ERROR_REBOOT_REQUIRED
Log " - CVHBS.exe returned: " & SetupRetVal(Retval) & " (" & RetVal & ")" & vbCrLf
On Error Goto 0
Else
Log " -> Removal suppressed in preview mode."
End If
Else
Log "Error: Office Click-to-Run CVHBS.exe appears to be missing"
End If 'oFso.FileExists
'Make sure that C2R keys are gone to unblock the msiexec task
End If 'Sku = Click2run
Next 'Sku
'Ensure that the OSE service is *installed, *not disabled, *running under System context.
'If validation fails exit out of this sub.
Set OseService = oWmiLocal.Execquery("Select * From Win32_Service Where Name like 'ose%'")
If OseService.Count = 0 Then Exit Sub
For Each Service in OseService
If (Service.StartMode = "Disabled") AND (Not Service.ChangeStartMode("Manual")=0) Then Exit Sub
If (Not Service.StartName = "LocalSystem") AND (Service.Change( , , , , , , "LocalSystem", "")) Then Exit Sub
Next 'Service
For Each Sku in dicRemoveSku.Keys
If Sku="CLICK2RUN" Then
'Already done
Else
'Create an "unattended" config.xml file for uninstall
If fQuiet Then sDisplayLevel = "None" Else sDisplayLevel="Basic"
If fNoCancel Then sNoCancel="Yes" Else sNoCancel="No"
Set TextStream = oFso.OpenTextFile(sScrubDir & "\config.xml",FOR_WRITING,True,True)
TextStream.Writeline "<Configuration Product=""" & Sku & """>"
TextStream.Writeline "<Display Level=""" & sDisplayLevel & """ CompletionNotice=""No"" SuppressModal=""Yes"" NoCancel=""" & sNoCancel & """ AcceptEula=""Yes"" />"
TextStream.Writeline "<Logging Type=""Verbose"" Path=""" & sLogDir & """ Template=""Microsoft Office " & Sku & " Setup(*).txt"" />"
TextStream.Writeline "<Setting Id=""SETUP_REBOOT"" Value=""Never"" />"
TextStream.Writeline "</Configuration>"
TextStream.Close
Set TextStream = Nothing
'Ensure path to setup.exe is valid to prevent errors
sDll = ""
If RegReadValue(HKLM,REG_ARP & OREGREF & Sku,"UninstallString",sCatalyst,"REG_SZ") Then
If InStr(LCase(sCatalyst),"/dll")>0 Then sDll = Right(sCatalyst,Len(sCatalyst)-InStr(LCase(sCatalyst),"/dll")+2)
If InStr(sCatalyst,"/")>0 Then sCatalyst = Left(sCatalyst,InStr(sCatalyst,"/")-1)
sCatalyst = Trim(Replace(sCatalyst,Chr(34),""))
If NOT oFso.FileExists(sCatalyst) Then
sCatalyst = sCommonProgramFiles & "\" & OREF & "\Office Setup Controller\setup.exe"
If NOT oFso.FileExists(sCatalyst) AND f64 Then
sCatalyst = sCommonProgramFilesX86 & "" & OREF & "\Office Setup Controller\setup.exe"
End If
End If
If oFso.FileExists(sCatalyst) Then
sUninstallCmd = Chr(34) & sCatalyst & Chr(34) & " /uninstall " & Sku & " /config " & Chr(34) & sScrubDir & "\config.xml" & Chr(34) & sDll
iSetupCnt = iSetupCnt + 1
Log " - Calling Setup.exe to remove " & Sku '& vbCrLf & sUninstallCmd
If Not fDetectOnly Then
On Error Resume Next
RetVal = oWShell.Run(sUninstallCmd,0,True) : CheckError "SetupExeRemoval"
Log " - Setup.exe returned: " & SetupRetVal(Retval) & " (" & RetVal & ")" & vbCrLf
fRebootRequired = fRebootRequired OR (RetVal = "3010")
If fRebootRequired Then SetError ERROR_REBOOT_REQUIRED
Select Case CInt(RetVal)
Case ERROR_SUCCESS,ERROR_SUCCESS_CONFIG_COMPLETE,ERROR_SUCCESS_REBOOT_REQUIRED
'success no action required
Case Else
SetError ERROR_STAGE2
End Select
On Error Goto 0
Else
Log " -> Removal suppressed in preview mode."
End If
Else
Log " Error: Office setup.exe appears to be missing"
SetError ERROR_STAGE2
End If 'RetVal = 0) AND oFso.FileExists
End If 'RegReadValue
End If 'C2R
Next 'Sku
If iSetupCnt = 0 Then Log " Nothing to remove for setup."
End Sub 'SetupExeRemoval
'=======================================================================================================
'Invoke msiexec to remove individual .MSI packages
Sub MsiexecRemoval
Dim Product
Dim i
Dim sCmd, sReturn, sMsiProp
Dim fRegWipe, fC2RRegWipe
fRegWipe = False
fC2RRegWipe = False
Select Case OVERSIONMAJOR
Case "11"
sMsiProp = " REBOOT=ReallySuppress NOLOCALCACHEROLLBACK=1"
Case "12"
fRegWipe = True
sMsiProp = " REBOOT=ReallySuppress NOREMOVESPAWN=True"
Case "14"
fRegWipe = True
sMsiProp = " REBOOT=ReallySuppress NOREMOVESPAWN=True"
fC2RRegWipe = True
Case Else
End Select
'Clear up ARP first to avoid possible custom action dependencies
If fRegWipe Then RegWipeARP
'Check MSI registered products
'Office System does only support per machine installation so it's sufficient to use Installer.Products
i = 0
For Each Product in oMsi.Products
If InScope(Product) Then
If fRemoveAll OR CheckDelete(Product) Then
i = i + 1
Log " Calling msiexec.exe to remove " & Product
sCmd = "msiexec.exe /x" & Product & sMsiProp
If fC2RRegWipe Then
'Need to clear out C2R registration first
If Mid(Product,11,3)="006" Then RegWipeC2R
End If
If fQuiet Then
sCmd = sCmd & " /q"
Else
sCmd = sCmd & " /qb-"
End If
sCmd = sCmd & " /l*v+ "&chr(34)&sLogDir&"\Uninstall_"&Product&".log"&chr(34)
If NOT fDetectOnly Then
LogOnly " - Calling msiexec with '"&sCmd&"'"
'Execute the patch uninstall
sReturn = oWShell.Run(sCmd, 0, True)
Log " - msiexec returned: " & SetupRetVal(sReturn) & " (" & sReturn & ")" & vbCrLf
fRebootRequired = fRebootRequired OR (sReturn = "3010")
If fRebootRequired Then SetError ERROR_REBOOT_REQUIRED
Select Case CInt(sReturn)
Case ERROR_SUCCESS,ERROR_SUCCESS_CONFIG_COMPLETE,ERROR_SUCCESS_REBOOT_REQUIRED
'success no action required
Case Else
SetError ERROR_STAGE3
End Select
Else
Log " -> Removal suppressed in preview mode."
LogOnly " -> Command: "&sCmd
End If
End If 'CheckDelete
End If 'InScope
Next 'Product
If i = 0 Then Log " Nothing to remove for msiexec"
End Sub 'MsiexecRemoval
'=======================================================================================================
'Remove the OSE (Office Source Engine) service
Sub RemoveOSE
On Error Resume Next
Log vbCrLf & " OSE CleanUp"
DeleteService "ose"
'Delete the folder
DeleteFolder sCommonProgramFiles & "\Microsoft Shared\Source Engine"
'Delete the registration
RegDeleteKey HKLM,"SYSTEM\CurrentControlSet\Services\ose\"
End Sub 'RemoveOSE
'=======================================================================================================
'Remove the Softgrid services (App-V and Click2Run)
Sub RemoveSG
On Error Resume Next
Log " Softgrid CleanUp"
DeleteService("cvhsvc")
DeleteService("SftList")
DeleteService("SftPlay")
DeleteService("SftVol")
DeleteService("SftFs")
DeleteService("SftVsa")
'Delete the folder
DeleteFolder sAppdata & "\SoftGrid Client"
DeleteFolder sLocalAppData & "\SoftGrid Client"
DeleteFolder sProgramData & "\Microsoft\Application Virtualization Client\SoftGrid Client"
DeleteFolder sProgramData & "\Microsoft\Application Virtualization Client"
DeleteFolder sProgramfiles & "\Microsoft\Microsoft Application Virtualization Client"
DeleteFolder sProgramfiles & "\Microsoft Application Virtualization Client"
'Delete the registration
RegDeleteKey HKLM,"SYSTEM\CurrentControlSet\Services\cvhsvc"
RegDeleteKey HKLM,"SYSTEM\CurrentControlSet\Services\sftfs"
RegDeleteKey HKLM,"SYSTEM\CurrentControlSet\Services\sftlist"
RegDeleteKey HKLM,"SYSTEM\CurrentControlSet\Services\sftplay"
RegDeleteKey HKLM,"SYSTEM\CurrentControlSet\Services\sftredir"
RegDeleteKey HKLM,"SYSTEM\CurrentControlSet\Services\sftvol"
RegDeleteKey HKLM,"SYSTEM\CurrentControlSet\Services\sftvsa"
RegDeleteKey HKLM,"SYSTEM\CurrentControlSet\Services\sftfs"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\SoftGrid\4.5"
RegDeleteKey HKCU,"Software\Microsoft\SoftGrid\4.5\Client\AppFS"
RegDeleteKey HKCU,"Software\Microsoft\SoftGrid\4.5\Client\Applications"
RegDeleteKey HKCU,"Software\Microsoft\SoftGrid\4.5\Client\FileExtensions"
RegDeleteKey HKCU,"Software\Microsoft\SoftGrid\4.5\Client\FileTypes"
RegDeleteKey HKCU,"Software\Microsoft\SoftGrid\4.5\Client\UserInfo"
'C2R places custom permissions on these regkeys which prevent them from getting deleted
'RegDeleteKey HKCU,"Software\Microsoft\SoftGrid\4.5\Client\Network"
'RegDeleteKey HKCU,"Software\Microsoft\SoftGrid\4.5\Client\Packages"
'RegDeleteKey HKCU,"Software\Microsoft\SoftGrid\4.5\Client"
'RegDeleteKey HKCU,"Software\Microsoft\SoftGrid\4.5"
End Sub 'RemoveSG
'=======================================================================================================
'Stops all Softgrid services and virtual applications
Sub ResetSG
Dim Processes, Process
Dim fWait
Dim iRet
On Error Resume Next
fWait = False
Log " Doing Action: ResetSG"
'Close all running (virtualized) Office applications
'OfficeVirt.exe needs to be shut down first
Set Processes = oWmiLocal.ExecQuery("Select * From Win32_Process Where Name like 'officevirt%.exe'")
For Each Process in Processes
Log " - End process " & Process.Name
iRet = Process.Terminate()
CheckError "ResetSG: " & "Process.Name"
fWait = True
Next 'Process
'Shut down CVH.exe
Set Processes = oWmiLocal.ExecQuery("Select * From Win32_Process Where Name='cvh.exe'")
For Each Process in Processes
Log " - End process " & Process.Name
iRet = Process.Terminate()
CheckError "ResetSG: " & "Process.Name"
Next 'Process
'Close running instances
Set Processes = oWmiLocal.ExecQuery("Select * From Win32_Process")
For Each Process in Processes
If dicApps.Exists(LCase(Process.Name)) Then
Log " - End process " & Process.Name
iRet = Process.Terminate()
CheckError "CloseOfficeApps: " & "Process.Name"
End If
Next 'Process
If fWait Then wscript.sleep 10000
'Stop all SoftGrid services
iRet = StopService("cvhsvc")
iRet = StopService("SftList")
iRet = StopService("SftPlay")
iRet = StopService("SftVol")
iRet = StopService("SftFs")
iRet = StopService("SftVsa")
End Sub 'ResetSG
'=======================================================================================================
'File cleanup operations for the Local Installation Source (MSOCache)
Sub WipeLIS
Const LISROOT = "MSOCache\All Users\"
Dim LogicalDisks, Disk, Folder, SubFolder, MseFolder, File, Files
Dim arrSubFolders
Dim sFolder
Dim fRemoveFolder
Log vbCrLf & " LIS CleanUp"
'Search all hard disks
Set LogicalDisks = oWmiLocal.ExecQuery("Select * From Win32_LogicalDisk WHERE DriveType=3")
For Each Disk in LogicalDisks
If oFso.FolderExists(Disk.DeviceID & "\" & LISROOT) Then
Set Folder = oFso.GetFolder(Disk.DeviceID & "\" & LISROOT)
For Each Subfolder in Folder.Subfolders
If Len(Subfolder) > 37 Then
If fRemoveAll Then
If (Mid(Subfolder.Name,26,PRODLEN) = OFFICEID AND Mid(SubFolder.Name,4,2)=OVERSIONMAJOR) OR _
LCase(Right(Subfolder.Name,7)) = OVERSIONMAJOR &".data" Then DeleteFolder Subfolder.Path
Else
If (Mid(Subfolder.Name,26,PRODLEN) = OFFICEID AND Mid(SubFolder.Name,4,2)=OVERSIONMAJOR) AND _
CheckDelete(UCase(Left(Subfolder.Name,38))) AND _
UCase(Right(Subfolder,1))= UCase(Left(Disk.DeviceID,1))Then DeleteFolder Subfolder.Path
End If
End If 'Len > 37
Next 'Subfolder
If (Folder.Subfolders.Count = 0) AND (Folder.Files.Count = 0) Then
sFolder = Folder.Path
Set Folder = Nothing
SmartDeleteFolder sFolder
End If
End If 'oFso.FolderExists
Next 'Disk
'MSECache
If EnumFolders(sProgramFiles,arrSubFolders) Then
For Each SubFolder in arrSubFolders
If UCase(Right(SubFolder,9))="\MSECACHE" Then
ReDim arrMseFolders(-1)
Set Folder = oFso.GetFolder(SubFolder)
GetMseFolderStructure Folder
For Each MseFolder in arrMseFolders
If oFso.FolderExists(MseFolder) Then
fRemoveFolder = False
Set Folder = oFso.GetFolder(MseFolder)
Set Files = Folder.Files
For Each File in Files
If (LCase(Right(File.Name,4))=".msi") Then
If CheckDelete(ProductCode(File.Path)) Then
fRemoveFolder = True
Exit For
End If 'CheckDelete
End If
Next 'File
Set Files = Nothing
Set Folder = Nothing
If fRemoveFolder Then SmartDeleteFolder MseFolder
End If 'oFso.FolderExists(MseFolder)
Next 'MseFolder
End If
Next 'SubFolder
End If 'oFso.FolderExists
End Sub 'WipeLis
'=======================================================================================================
'Wipe files and folders as documented in KB 928218
Sub FileWipeAll
Dim sFolder
Dim Folder, Subfolder
If fForce OR fQuiet Then CloseOfficeApps
'Handle other services.
Select Case OVERSIONMAJOR
Case "11"
Case "12"
Case "14"
DeleteService "odserv"
DeleteService "Microsoft Office Groove Audit Service"
DeleteService "Microsoft SharePoint Workspace Audit Service"
Case Else
End Select
'User specific files
If NOT fKeepUser Then
'Delete files that should be backed up before deleting them
CopyAndDeleteFile sAppdata & "\Microsoft\Templates\Normal.dotm"
CopyAndDeleteFile sAppdata & "\Microsoft\Templates\Normalemail.dotm"
sFolder = sAppdata & "\microsoft\document building blocks"
If oFso.FolderExists(sFolder) Then
Set Folder = oFso.GetFolder(sFolder)
For Each Subfolder In Folder.Subfolders
If oFso.FileExists(Subfolder & "\blocks.dotx") Then CopyAndDeleteFile Subfolder & "\blocks.dotx"
Next 'Subfolder
Set Folder = Nothing
End If 'oFso.FolderExists(sFolder)
End If
'Run the individual filewipe from component detection first
FileWipeIndividual
'Take care of the rest
DeleteFolder sOInstallRoot
DeleteFolder sCommonProgramFiles & "\Microsoft Shared\" & OREF
DeleteFile sAllUsersProfile & "\Application Data\Microsoft\Office\Data\opa"&OVERSIONMAJOR&".dat"
DeleteFile sAllUsersProfile & "\Application Data\Microsoft\Office\Data\opa"&OVERSIONMAJOR&".bak"
DeleteFile sAllUsersProfile & "\Microsoft\Office\Data\opa"&OVERSIONMAJOR&".dat"
DeleteFile sAllUsersProfile & "\Microsoft\Office\Data\opa"&OVERSIONMAJOR&".bak"
If (fRemoveOspp OR fForce) AND CInt(OVERSIONMAJOR)>12 Then
DeleteService "osppsvc"
DeleteFolder sCommonProgramFiles & "\Microsoft Shared\OfficeSoftwareProtectionPlatform"
DeleteFolder sAllUsersProfile & "\Microsoft\OfficeSoftwareProtectionPlatform"
End If
Select Case OVERSIONMAJOR
Case "12"
Case "14"
DeleteFile oWShell.SpecialFolders("AllUsersStartup")&"\OfficeSAS.lnk"
DeleteFile oWShell.SpecialFolders("Startup")&"\OneNote 2010 Screen Clipper and Launcher.lnk"
Case Else
End Select
End Sub 'FileWipeAll
'=======================================================================================================
'Wipe individual files & folders related to SKU's that are no longer installed
Sub FileWipeIndividual
Dim LogicalDisks, Disk
Dim File, Files, XmlFile, scFiles, oFile, Folder, SubFolder, Processes, Process, item
Dim sFile, sFolder, sPath, sConfigName, sContents, sProductCode, sLocalDrives,sScQuery
Dim arrSubfolders
Dim fKeepFolder, fDeleteSC
Dim iRet
Log vbCrLf & " File CleanUp"
If IsArray(arrDeleteFiles) Then
If fForce OR fQuiet Then
Log " Doing Action: StopOSE"
iRet = StopService("ose")
Set Processes = oWmiLocal.ExecQuery("Select * From Win32_Service Where Name like 'ose%.exe'")
For Each Process in Processes
LogOnly " - Running process : " & Process.Name
Log " -> Ending process: " & Process.Name
iRet = Process.Terminate()
Next 'Process
LogOnly " End Action: StopOSE"
CloseOfficeApps
End If
'Wipe individual files detected earlier
LogOnly " Removing left behind files"
For Each sFile in arrDeleteFiles
If oFso.FileExists(sFile) Then DeleteFile sFile
Next 'File
End If 'IsArray
'Wipe Catalyst in commonfiles
sFolder = sCommonProgramFiles & "\microsoft shared\"&OREF&"\Office Setup Controller\"
If EnumFolderNames(sFolder,arrSubFolders) Then
For Each SubFolder in arrSubFolders
sPath = sFolder & SubFolder
If InStr(SubFolder,".")>0 Then sConfigName = UCase(Left(SubFolder,InStr(SubFolder,".")-1))Else sConfigName = UCase(Subfolder)
If GetFolderPath(sPath) Then
Set Folder = oFso.GetFolder(sPath)
Set Files = Folder.Files
fKeepFolder = False
For Each File In Files
If Len(File.Name)>3 Then
If (LCase(Right(File.Name,4))=".xml") Then
If Len(File.Name) >= Len(sConfigName) Then
If (UCase(Left(File.Name,Len(sConfigName)))=sConfigName) Then
Set XmlFile = oFso.OpenTextFile(File,1)
sContents = XmlFile.ReadAll
Set XmlFile = Nothing
sProductCode = ""
On Error Resume Next
sProductCode = Mid(sContents,InStr(sContents,"ProductCode=")+Len("ProductCode=")+1,38)
On Error Goto 0
If Len(sProductCode) = 38 Then
If CheckDelete(sProductCode) Then DeleteFile File.Path Else fKeepFolder = True
End If
End If 'sConfigName
End If 'Len >=
End If '.xml
End If 'Len(File.Name)>3
Next 'File
Set Files = Nothing
Set Folder = Nothing
If Not fKeepFolder Then DeleteFolder sPath
End If 'GetFolderPath
Next 'SubFolder
End If 'EnumFolderNames
'Wipe Shortcuts from local hard disks
If NOT fSkipSD Then
On Error Resume Next
Log " Searching for shortcuts. This can take some time ..."
Set LogicalDisks = oWmiLocal.ExecQuery("Select * From Win32_LogicalDisk WHERE DriveType=3")
For Each Disk in LogicalDisks
sLocalDrives = sLocalDrives & UCase(Disk.DeviceID) & "\;"
sScQuery = "Select * From Win32_ShortcutFile WHERE Drive='"&Disk.DeviceID&"'"
Set scFiles = oWmiLocal.ExecQuery(sScQuery)
For Each File in scFiles
fDeleteSC = False
'Compare if the shortcut target is in the list of executables that will be removed
If Len(File.Target)>0 Then
For Each item in dicApps.Items
If LCase(File.Target) = item Then
fDeleteSC = True
Exit For
End If
Next 'item
End If
'Handle Windows Installer shortcuts
If InStr(File.Target,"{")>0 Then
If Len(File.Target)>=InStr(File.Target,"{")+37 Then
If CheckDelete(Mid(File.Target,InStr(File.Target,"{"),38)) Then fDeleteSC = True
End If
End If
'Handle C2R
If InStr(File.Target,"CVH.EXE")>0 AND (fRemoveAll OR fRemoveC2R) Then
If InStr(File.Target,"90" & OVERSIONMAJOR & "006")>0 Then fDeleteSC = True
End If
If fDeleteSC Then
If Not IsArray(arrDeleteFolders) Then ReDim arrDeleteFolders(0)
sFolder = Left(File.Description,InStrRev(File.Description,"\")-1)
If Not arrDeleteFolders(UBound(arrDeleteFolders)) = sFolder Then
ReDim Preserve arrDeleteFolders(UBound(arrDeleteFolders)+1)
arrDeleteFolders(UBound(arrDeleteFolders)) = sFolder
End If
DeleteFile File.Description
End If 'fDeleteSC
Next 'scFile
Next
On Error Goto 0
End If 'NOT SkipSD
Err.Clear
End Sub 'FileWipeIndividual
'=======================================================================================================
Sub DelScrubTmp
On Error Resume Next
If oFso.FileExists(sScrubDir&"\CvhbsQuiet.vbs") Then oFso.DeleteFile sScrubDir&"\CvhbsQuiet.vbs",True
If oFso.FolderExists(sScrubDir & "\ScrubTmp") Then oFso.DeleteFolder sScrubDir & "\ScrubTmp",True
End Sub 'DelScrubTmp
'=======================================================================================================
'Ensure there are no unexpected .msi files in the scrub folder
Sub DeleteMsiScrubCache
Dim Folder, File, Files
On Error Resume Next 'Error handling inlined
Log vbCrLf & " ScrubCache CleanUp"
Set Folder = oFso.GetFolder(sScrubDir) : CheckError "DeleteMsiScrubCache"
Set Files = Folder.Files
For Each File in Files
CheckError "DeleteMsiScrubCache"
If LCase(Right(File.Name,4))=".msi" Then
CheckError "DeleteMsiScrubCache"
DeleteFile File.Path : CheckError "DeleteMsiScrubCache"
End If
Next 'File
End Sub 'DeleteMsiScrubCache
'=======================================================================================================
Sub MsiClearOrphanedFiles
Const USERSIDEVERYONE = "s-1-1-0"
Const MSIINSTALLCONTEXT_ALL = 7
Const MSIPATCHSTATE_ALL = 15
On Error Resume Next 'Error handling inlined
Dim Patch, AllPatches, Product, AllProducts
Dim File, Files, Folder
Dim sFName, sLocalMsp, sLocalMsi, sPatchList, sMsiList
Set Folder = oFso.GetFolder(sWinDir & "\Installer")
Set Files = Folder.Files
Log vbCrLf & " Windows Installer cache CleanUp"
'Get a complete list of patches
Err.Clear
Set AllPatches = oMsi.PatchesEx("",USERSIDEVERYONE,MSIINSTALLCONTEXT_ALL,MSIPATCHSTATE_ALL)
If Err <> 0 Then
CheckError "MsiClearOrphanedFiles (msp)"
Else
'Fill a comma separated stringlist with all .msp patchfiles
For Each Patch in AllPatches
sLocalMsp = "" : sLocalMsp = LCase(Patch.Patchproperty("LocalPackage")) : CheckError "MsiClearOrphanedFiles (msp)"
sPatchList = sPatchList & sLocalMsp & ","
Next 'Patch
'Delete all non referenced .msp files from %windir%\installer
For Each File in Files
sFName = "" : sFName = LCase(File.Path)
If LCase(Right(sFName,4)) = ".msp" Then
If Not InStr(sPatchList,sFName) > 0 Then
'While this is an orphaned file keep the scope of Office only
If InStr(UCase(MspTargets(File.Path)),OFFICEID)>0 Then DeleteFile File.Path
End If
End If 'LCase(Right(sFName,4))
Next 'File
End If 'Err=0
'Get a complete list products
Err.Clear
Set AllProducts = oMsi.ProductsEx("",USERSIDEVERYONE,MSIINSTALLCONTEXT_ALL)
If Err <> 0 Then
CheckError "MsiClearOrphanedFiles (msi)"
Else
'Fill a comma separated stringlist with all .msi files
For Each Product in AllProducts
sLocalMsi = "" : sLocalMsi = LCase(Product.InstallProperty("LocalPackage")) : CheckError "MsiClearOrphanedFiles (msi)"
sMsiList = sMsiList & sLocalMsi & ","
Next 'Product
'Delete all non referenced .msi files from %windir%\installer
For Each File in Files
sFName = "" : sFName = LCase(File.Path)
If LCase(Right(sFName,4)) = ".msi" Then
If Not InStr(sMsiList,sFName) > 0 Then
'While this is an orphaned file keep the scope of Office only
If UCase(Right(ProductCode(File.Path),PRODLEN))=OFFICEID Then DeleteFile File.Path
End If
End If 'LCase(Right(sFName,4)) = ".msi"
Next 'File
End If 'Err=0
End Sub 'MsiClearOrphanedFiles
'=======================================================================================================
Sub RegWipe
Dim Item, Name, Sku, key
Dim hDefKey, sSubKeyName, sCurKey, value, sValue, sGuid
Dim fkeep, fSystemComponent0, fPackages, fDisplayVersion
Dim arrKeys, arrNames, arrTypes, arrMultiSzValues, arrMultiSzNewValues
Dim arrTestNames,arrTestTypes
Dim i, iLoopCnt, iPos
Dim fDelReg
Log vbCrLf & " Registry CleanUp"
'Wipe registry data
'User Profile settings
RegDeleteKey HKCU,"Software\Policies\Microsoft\Office\" & OVERSION & "\"
If NOT fKeepUser Then
RegDeleteKey HKCU,"Software\Microsoft\Office\" & OVERSION & "\"
End If 'fKeepUser
'Computer specific settings
If fRemoveAll Then
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Office\" & OVERSION & "\"
If fRemoveOse OR fForce Then
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Office Test\"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Office\Common\","LastAccessInstall"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Office\Common\","MID"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Office\Excel\Addins\Microsoft.PerformancePoint.Planning.Client.Excel\"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Office\InfoPath\Converters\Import\InfoPath.DesignerExcelImport\Versions\",OVERSION
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Office\InfoPath\Converters\Import\InfoPath.DesignerWordImport\Versions\",OVERSION
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Office\Outlook\"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Shared Tools\Text Converters\Export\MEWord12\"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Shared Tools\Text Converters\Export\Word12\"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Shared Tools\Text Converters\Export\Word97\"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Shared Tools\Text Converters\Import\MEWord12\"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Shared Tools\Text Converters\Import\Word12\"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Shared Tools\Text Converters\Import\Word97\"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Run\","GrooveMonitor"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Run\","LobiServer"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Run\","BCSSync"
RegDeleteKey HKLM,"SYSTEM\CurrentControlSet\Services\Outlook\"
End If
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Office\Common\OffDiag\Location\",OVERSIONMAJOR
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Terminal Server\Install\Software\Microsoft\Office\" & OVERSION & "\"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Office\Common\OffDiag\Location\",OVERSIONMAJOR
RegDeleteKey HKLM,"SOFTWARE\Microsoft\OfficeCustomizeWizard\" & OVERSION & "\"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Terminal Server\Install\SOFTWARE\Microsoft\OfficeCustomizeWizard\" & OVERSION & "\"
Select Case OVERSIONMAJOR
Case "11"
'Jet_Replication
sValue = ""
If RegReadValue(HKCR,"CLSID\{CC2C83A6-9BE4-11D0-98E7-00C04FC2CAF5}\InprocServer32","SystemDB",sValue,"REG_SZ") Then
If Len(sValue) > Len(sOInstallRoot) Then
If LCase(Left(sValue,Len(sOInstallRoot))) = LCase(sOInstallRoot) Then RegDeleteKey HKCR,"CLSID\{CC2C83A6-9BE4-11D0-98E7-00C04FC2CAF5}\InprocServer32\"
End If
End If
Case "12"
Case "14"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\OfficeSoftwareProtectionPlatform\"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\OfficeSoftwareProtectionPlatform_Test\"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Office\Common\ActiveX Compatibility\{00024512-0000-0000-C000-000000000046}\"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Office\OneNote\Adapters\","{456B0D0E-49DD-4C95-8DB6-175F54DE69A3}"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{42042206-2D85-11D3-8CFF-005004838597}"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{993BE281-6695-4BA5-8A2A-7AACBFAAB69E}"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{0006F045-0000-0000-C000-000000000046}"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{C41662BB-1FA0-4CE0-8DC5-9B7F8279FF97}"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{7CCA70DB-DE7A-4FB7-9B2B-52E2335A3B5A}"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{506F4668-F13E-4AA1-BB04-B43203AB3CC0}"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{D66DC78C-4F61-447F-942B-3FB6980118CF}"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Browser Helper Objects\{B4F3A835-0E21-4959-BA22-42B3008E02FF}\"
'Groove Extensions
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellExecuteHooks\","{B5A7F190-DDA6-4420-B3BA-52453494E6CD}"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{99FD978C-D287-4F50-827F-B2C658EDA8E7}"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{AB5C5600-7E6E-4B06-9197-9ECEF74D31CC}"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{920E6DB1-9907-4370-B3A0-BAFC03D81399}"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{16F3DD56-1AF5-4347-846D-7C10C4192619}"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{2916C86E-86A6-43FE-8112-43ABE6BF8DCC}"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{72853161-30C5-4D22-B7F9-0BBC1D38A37E}"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{6C467336-8281-4E60-8204-430CED96822D}"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{2A541AE1-5BF6-4665-A8A3-CFA9672E4291}"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{B5A7F190-DDA6-4420-B3BA-52453494E6CD}"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{A449600E-1DC6-4232-B948-9BD794D62056}"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{3D60EDA7-9AB4-4DA8-864C-D9B5F2E7281D}"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{387E725D-DC16-4D76-B310-2C93ED4752A0}"
RegDeleteKey HKLM,"SOFTWARE\Classes\*\shellex\ContextMenuHandlers\XXX Groove GFS Context Menu Handler XXX\"
RegDeleteKey HKLM,"SOFTWARE\Classes\AllFilesystemObjects\shellex\ContextMenuHandlers\XXX Groove GFS Context Menu Handler XXX\"
RegDeleteKey HKLM,"SOFTWARE\Classes\Directory\shellex\ContextMenuHandlers\XXX Groove GFS Context Menu Handler XXX\"
RegDeleteKey HKLM,"SOFTWARE\Classes\Folder\ShellEx\ContextMenuHandlers\XXX Groove GFS Context Menu Handler XXX\"
RegDeleteKey HKLM,"SOFTWARE\Classes\Directory\Background\shellex\ContextMenuHandlers\XXX Groove GFS Context Menu Handler XXX\"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers\Groove Explorer Icon Overlay 1 (GFS Unread Stub)\"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers\Groove Explorer Icon Overlay 2 (GFS Stub)\"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers\Groove Explorer Icon Overlay 2.5 (GFS Unread Folder)\"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers\Groove Explorer Icon Overlay 3 (GFS Folder)\"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers\Groove Explorer Icon Overlay 4 (GFS Unread Mark)\"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Browser Helper Objects\{72853161-30C5-4D22-B7F9-0BBC1D38A37E}\"
Case Else
End Select
'Win32Assemblies
If RegEnumKey(HKCR,"Installer\Win32Assemblies\",arrKeys) Then
For Each Item in arrKeys
If InStr(UCase(Item),OREF)>0 Then RegDeleteKey HKCR,"Installer\Win32Assemblies\"&Item & "\"
Next 'Item
End If 'RegEnumKey
'Groove blocks reinstall if it locates groove.exe over this key
If RegKeyExists(HKCR,"GrooveFile\Shell\Open\Command\") Then
sValue = ""
RegReadValue HKCR,"GrooveFile\Shell\Open\Command\","",sValue,"REG_SZ"
If InStr(sValue,"\"&OREF&"\")>0 Then RegDeleteKey HKCR,"GrooveFile\"
End If 'RegKeyExists
End If 'fRemoveAll
Select Case OVERSIONMAJOR
Case "11"
For iLoopCnt = 1 to 3
Select Case iLoopCnt
Case 1
'CIW - HKCU
sSubKeyName = "Software\Microsoft\OfficeCustomizeWizard\" & OVERSION & "\RegKeyPaths\"
hDefKey = HKCU
Case 2
'CIW - HKLM
sSubKeyName = "SOFTWARE\Microsoft\OfficeCustomizeWizard\" & OVERSION & "\RegKeyPaths\"
hDefKey = HKLM
Case 3
'Add/Remove Programs
sSubKeyName = REG_ARP
hDefKey = HKLM
End Select
If RegEnumKey(hDefKey,sSubKeyName,arrKeys) Then
For Each Item in arrKeys
'OFFICEID id
If Len(Item)>37 Then
sGuid = UCase(Left(Item,38))
If Right(sGuid,PRODLEN)=OFFICEID Then
If CheckDelete(sGuid) Then
RegDeleteKey hDefKey, sSubKeyName & Item & "\"
End If
End If 'Right(Item,PRODLEN)=OFFICEID
End If 'Len(Item)>37
Next 'Item
If iLoopCnt < 3 Then
If RegEnumValues(hDefKey,sSubKeyName,arrNames,arrTypes) Then
i = 0
For Each Name in arrNames
If RegReadValue(hDefKey,sSubKeyName,Name,sValue,arrTypes(i)) Then
If sValue = sGuid Then RegDeleteValue hDefKey,sSubKeyName,Name
End If
i = i + 1
Next
End If
End If
End If
If NOT RegEnumKey(hDefKey,sSubKeyName,arrKeys) Then RegDeleteKey hDefKey,"Software\Microsoft\OfficeCustomizeWizard\11.0\"
If NOT RegEnumKey(hDefKey,"Software\Microsoft\OfficeCustomizeWizard\11.0\",arrKeys) Then RegDeleteKey hDefKey,"Software\Microsoft\OfficeCustomizeWizard\"
Next 'iLoopCnt
Case "12"
'Add/Remove Programs
RegWipeARP
Case "14"
'Add/Remove Programs
RegWipeARP
Case Else
End Select
'UpgradeCodes, WI config, WI global config
For iLoopCnt = 1 to 5
Select Case iLoopCnt
Case 1
sSubKeyName = "SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UpgradeCodes\"
hDefKey = HKLM
Case 2
sSubKeyName = "Installer\UpgradeCodes\"
hDefKey = HKCR
Case 3
sSubKeyName = "SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\"
hDefKey = HKLM
Case 4
sSubKeyName = "Installer\Features\"
hDefKey = HKCR
Case 5
sSubKeyName = "Installer\Products\"
hDefKey = HKCR
Case Else
sSubKeyName = ""
hDefKey = ""
End Select
If RegEnumKey(hDefKey,sSubKeyName,arrKeys) Then
For Each Item in arrKeys
'Ensure we have the expected length for a compressed GUID
If Len(Item)=32 Then
'Expand the GUID
sGuid = GetExpandedGuid(Item)
'Check if it's an Office key
If InScope(sGuid) Then
If fRemoveAll Then
RegDeleteKey hDefKey,sSubKeyName & Item & "\"
Else
If iLoopCnt < 3 Then
'Enum all entries
RegEnumValues hDefKey,sSubKeyName & Item,arrNames,arrTypes
If IsArray(arrNames) Then
'Delete entries within removal scope
For Each Name in arrNames
If Len(Name)=32 Then
sGuid = GetExpandedGuid(Name)
If CheckDelete(sGuid) Then RegDeleteValue hDefKey, sSubKeyName & Item & "\", Name
Else
'Invalid data -> delete the value
RegDeleteValue hDefKey, sSubKeyName & Item & "\", Name
End If
Next 'Name
End If 'IsArray(arrNames)
'If all entries were removed - delete the key
RegEnumValues hDefKey,sSubKeyName & Item,arrNames,arrTypes
If Not IsArray(arrNames) Then RegDeleteKey hDefKey, sSubKeyName & Item & "\"
Else 'iLoopCnt >= 3
If CheckDelete(sGuid) Then RegDeleteKey hDefKey, sSubKeyName & Item & "\"
End If 'iLoopCnt < 3
End If 'fRemoveAll
End If 'InScope
End If 'Len(Item)=32
Next 'Item
End If 'RegEnumKey
Next 'iLoopCnt
'Components
sSubKeyName = "SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components\"
If RegEnumKey(HKLM,sSubKeyName,arrKeys) Then
For Each Item in arrKeys
'Ensure we have the expected length for a compressed GUID
If Len(Item)=32 Then
If RegEnumValues(HKLM,sSubKeyName & Item,arrNames,arrTypes) Then
If IsArray(arrNames) Then
For Each Name in arrNames
If Len(Name)=32 Then
sGuid = GetExpandedGuid(Name)
If CheckDelete(sGuid) Then
RegDeleteValue HKLM, sSubKeyName & Item & "\", Name
'Check if the key is now empty
If NOT RegEnumValues(HKCR,sSubKeyName & Item,arrTestNames,arrTestTypes) Then
If NOT dicDelRegKey.Exists(sSubKeyName&Item&"\") Then dicDelRegKey.Add sSubKeyName&Item&"\",HKCR
End If
End If
End If '32
Next 'Name
End If 'IsArray
End If 'RegEnumValues
End If '32
Next 'Item
End If 'RegEnumKey
'Published Components
sSubKeyName = "Installer\Components\"
If RegEnumKey(HKCR,sSubKeyName,arrKeys) Then
For Each Item in arrKeys
'Ensure we have the expected length for a compressed GUID
If Len(Item)=32 Then
If RegEnumValues(HKCR,sSubKeyName & Item,arrNames,arrTypes) Then
If IsArray(arrNames) Then
For Each Name in arrNames
If RegReadValue (HKCR,sSubKeyName & Item, Name, sValue,"REG_MULTI_SZ") Then
arrMultiSzValues = Split(sValue,chr(34))
If IsArray(arrMultiSzValues) Then
i = -1
ReDim arrMultiSzNewValues(-1)
fDelReg = False
For Each value in arrMultiSzValues
If Len(value) > 19 Then
sGuid = ""
If GetDecodedGuid(Left(value,SQUISHED),sGuid) Then
If CheckDelete(sGuid) Then
fDelReg = True
Else
i = i + 1
ReDim Preserve arrMultiSzNewValues(i)
arrMultiSzNewValues(i) = value
End If 'CheckDelete
End If 'decode
End If '19
Next 'Value
If NOT (i = -1) Then
If NOT fDetectOnly Then
If NOT UBound(arrMultiSzValues) = i Then oReg.SetMultiStringValue HKCR,sSubKeyName & Item,Name,arrMultiSzNewValues
End If
Else
If fDelReg Then
RegDeleteValue HKCR,sSubKeyName & Item & "\", Name
'Check if the key is now empty
If NOT RegEnumValues(HKCR,sSubKeyName & Item,arrTestNames,arrTestTypes) Then
If NOT dicDelRegKey.Exists(sSubKeyName&Item&"\") Then dicDelRegKey.Add sSubKeyName&Item&"\",HKCR
End If
End If 'DelReg
End If
End If 'IsArray
End If
Next 'Name
End If 'IsArray
End If 'RegEnumValues
End If '32
Next 'Item
End If 'RegEnumKey
'Delivery
hDefKey = HKLM
sSubKeyName = "SOFTWARE\Microsoft\Office\Delivery\SourceEngine\Downloads\"
If RegEnumKey(HKLM,sSubKeyName,arrKeys) Then
For Each Item in arrKeys
If Len(Item) > 37 Then
If fRemoveAll Then
If (Mid(Item,26,PRODLEN)=OFFICEID AND Mid(Item,4,2)=OVERSIONMAJOR) OR _
LCase(Right(Item,7))=OVERSIONMAJOR&".data" Then RegDeleteKey HKLM,sSubKeyName & Item & "\"
Else
If (Mid(Item,26,PRODLEN)=OFFICEID AND Mid(Item,4,2)=OVERSIONMAJOR) AND _
CheckDelete(UCase(Left(Item,38))) Then RegDeleteKey HKLM,sSubKeyName & Item & "\"
End If
End If '37
Next 'Item
End If 'RegEnumKey
'Registration
hDefKey = HKLM
sSubKeyName = "SOFTWARE\Microsoft\Office\"&OVERSION&"\Registration\"
If RegEnumKey(HKLM,sSubKeyName,arrKeys) Then
For Each Item in arrKeys
If Len(Item)>37 Then
If CheckDelete(UCase(Left(Item,38))) Then RegDeleteKey HKLM,sSubKeyName & Item & "\"
End If
Next 'Item
End If 'RegEnumKey
'User Preconfigurations
hDefKey = HKLM
sSubKeyName = "SOFTWARE\Microsoft\Office\"&OVERSION&"\User Settings\"
If RegEnumKey(HKLM,sSubKeyName,arrKeys) Then
For Each Item in arrKeys
If Len(Item)>37 Then
If CheckDelete(UCase(Left(Item,38))) Then RegDeleteKey HKLM,sSubKeyName & Item & "\"
End If
Next 'Item
End If 'RegEnumKey
'Click2Run Cleanup
If CInt(OVERSIONMAJOR) > 12 Then RegWipeC2R
'Known Keypath settings
For Each key in dicDelRegKey.Keys
If Right(key,1) = "\" Then
RegDeleteKey dicDelRegKey.Item(key),key
Else
iPos = InStrRev(Key,"\")
If iPos > 0 Then RegDeleteValue dicDelRegKey.Item(key), Left(key,iPos - 1), Mid(key,iPos+1)
End If
Next
'Temporary entries in ARP
TmpKeyCleanUp
End Sub 'RegWipe
'=======================================================================================================
'Clean up Add/Remove Programs registry
Sub RegWipeARP
Dim Item, Name, Sku, key
Dim sSubKeyName, sCurKey, sValue, sGuid
Dim fkeep, fSystemComponent0, fPackages, fDisplayVersion
Dim arrKeys
'Add/Remove Programs
sSubKeyName = REG_ARP
If RegEnumKey(HKLM,sSubKeyName,arrKeys) Then
For Each Item in arrKeys
'*0FF1CE*
If Len(Item)>37 Then
sGuid = UCase(Left(Item,38))
If InScope(sGuid) Then
If CheckDelete(sGuid) Then RegDeleteKey HKLM, sSubKeyName & Item
End If 'InScope
End If 'Len(Item)>37
'Config entries
sCurKey = sSubKeyName & Item & "\"
fSystemComponent0 = Not (RegReadValue(HKLM,sCurKey,"SystemComponent",sValue,"REG_DWORD") AND (sValue = "1"))
fPackages = RegReadValue(HKLM,sCurKey,OPACKAGE,sValue,"REG_MULTI_SZ")
fDisplayVersion = RegReadValue(HKLM,sCurKey,"DisplayVersion",sValue,"REG_SZ")
If fDisplayVersion AND Len(sValue) > 1 Then
fDisplayVersion = (Left(sValue,2) = OVERSIONMAJOR)
End If
If (fSystemComponent0 AND fPackages AND fDisplayVersion) OR (fSystemComponent0 AND fDisplayVersion AND InStr(UCase(Item),"CLICK2RUN")>0) Then
fKeep = False
If Not fRemoveAll Then
For Each Sku in dicKeepSku.Keys
If UCase(Item) = OREGREF & Sku Then
fkeep = True
Exit For
End If
Next 'Sku
End If
If Not fkeep Then RegDeleteKey HKLM, sSubKeyName & Item
End If
Next 'Item
End If 'RegEnumKey
End Sub 'RegWipeARP
'=======================================================================================================
'Clean up Click2Run specific registrations
Sub RegWipeC2R
Dim Item
Dim sSubKeyName
Dim arrKeys
'Click2Run Cleanup
If fRemoveAll OR fRemoveC2R Then
RegDeleteKey HKCU,"Software\Microsoft\Office\CVH"
RegDeleteKey HKCU,"Software\Microsoft\Office\" & OVERSION & "\CVH"
RegDeleteKey HKLM,"Software\Microsoft\Office\" & OVERSION & "\CVH"
RegDeleteKey HKLM,"Software\Microsoft\Office\" & OVERSION & "\CVHSettings"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Office\" & OVERSION & "\Common\InstallRoot\Virtual"
'Control Panel Items
RegDeleteKey HKLM,"Software\Microsoft\Windows\CurrentVersion\explorer\ControlPanel\NameSpace\{F9ACD2D6-09C8-4103-995C-912DE68DDE1E}"
RegDeleteKey HKCR,"CLSID\{F9ACD2D6-09C8-4103-995C-912DE68DDE1E}"
RegDeleteKey HKLM,"Software\Microsoft\Windows\CurrentVersion\explorer\ControlPanel\NameSpace\{005CB1F2-224F-4738-B051-91A96758F50C}"
RegDeleteKey HKCR,"CLSID\{005CB1F2-224F-4738-B051-91A96758F50C}"
sSubKeyName = "SOFTWARE\Microsoft\SoftGrid\4.5\Client\Packages\"
If RegEnumKey(HKLM,sSubKeyName,arrKeys) Then
For Each Item in arrKeys
If CheckDelete(Item) Then RegDeleteKey HKLM,sSubKeyName & Item
Next 'Item
End If 'RegEnumKey
If RegEnumKey(HKCU,sSubKeyName,arrKeys) Then
For Each Item in arrKeys
If CheckDelete(Item) Then RegDeleteKey HKLM,sSubKeyName & Item
Next 'Item
End If 'RegEnumKey
End If
End Sub 'RegWipeC2R
'=======================================================================================================
'Clean up temporary registry keys
Sub TmpKeyCleanUp
Dim TmpKey
If fLogInitialized Then Log " Remove temporary registry entries"
If IsArray(arrTmpSKUs) Then
For Each TmpKey in arrTmpSKUs
oReg.DeleteKey HKLM, REG_ARP & TmpKey
Next 'Item
End If 'IsArray
End Sub 'TmpKeyCleanUp
'=======================================================================================================
' Helper Functions
'=======================================================================================================
'Create a log with the results of the SKU detection
Sub LogSkuResults
Dim SkuLog, SkuKey , p
On Error Resume Next 'Don't fail on logging
Set SkuLog = oFso.OpenTextFile(sScrubDir & "\SkuLog.txt",FOR_WRITING,True,True)
SkuLog.WriteLine "Installed SKUs (All):"
SkuLog.WriteLine "====================="
For Each SkuKey in dicInstalledSku.Keys
SkuLog.WriteLine " - " & SkuKey
Next 'Key
SkuLog.WriteLine vbCrLf & "Server SKUs:"
SkuLog.WriteLine "============"
For Each SkuKey in dicSrv.Keys
SkuLog.WriteLine " - " & SkuKey
Next 'Key
SkuLog.WriteLine vbCrLf & "Client Suite SKUs:"
SkuLog.WriteLine "=================="
For Each SkuKey in dicCSuite.Keys
SkuLog.WriteLine " - " & SkuKey
Next 'Key
SkuLog.WriteLine vbCrLf & "Client Standalone SKUs:"
SkuLog.WriteLine "======================="
For Each SkuKey in dicCSingle.Keys
SkuLog.WriteLine " - " & SkuKey
Next 'Key
SkuLog.WriteLine vbCrLf & "Installed Products (All):"
SkuLog.WriteLine "========================="
For Each p in oMsi.Products
If InScope(p) Then
SkuLog.Write " - " & p & " - "
SkuLog.Write oMsi.ProductInfo(p, "ProductName")
SkuLog.WriteLine " "
End If
Next 'Product
SkuLog.WriteLine vbCrLf & "***************************************************************************************************" & vbCrLf
SkuLog.WriteLine vbCrLf & "SKUs to keep:"
SkuLog.WriteLine "============="
For Each SkuKey in dicKeepSku.Keys
SkuLog.WriteLine " - " & SkuKey
Next 'Key
SkuLog.WriteLine vbCrLf & "Products to keep:"
SkuLog.WriteLine "================="
For Each p in dicKeepProd.Keys
SkuLog.Write " - " & p & " - "
SkuLog.Write oMsi.ProductInfo(p, "ProductName")
SkuLog.WriteLine " "
Next 'Key
SkuLog.WriteLine vbCrLf & "***************************************************************************************************" & vbCrLf
SkuLog.WriteLine vbCrLf & "SKUs to remove:"
SkuLog.WriteLine "==============="
For Each SkuKey in dicRemoveSku.Keys
SkuLog.WriteLine " - " & SkuKey
Next 'Key
SkuLog.WriteLine vbCrLf & "Products to remove:"
SkuLog.WriteLine "==================="
For Each p in oMsi.Products
If InScope(p) Then
If (fRemoveAll OR CheckDelete(p))Then
SkuLog.Write " - " & p & " - "
SkuLog.Write oMsi.ProductInfo(p, "ProductName")
SkuLog.WriteLine " "
End If
End If 'InScope
Next 'Product
SkuLog.Close
Set SkuLog = Nothing
End Sub 'LogSkuResults
'=======================================================================================================
'Set error bit(s) and cache the value to file
Sub SetError(ErrorBit)
iError = iError OR ErrorBit
Select Case ErrorBit
Case ERROR_STAGE4,ERROR_ELEVATION_USERDECLINED,ERROR_ELEVATION
iError = iError OR ERROR_FAIL
End Select
End Sub
'=======================================================================================================
'Clear error bit(s) and cache to file
Sub ClearError(ErrorBit)
iError = iError AND (ERROR_ALL - ErrorBit)
Select Case ErrorBit
Case ERROR_STAGE4,ERROR_ELEVATION_USERDECLINED,ERROR_ELEVATION
iError = iError AND (ERROR_ALL - ERROR_FAIL)
End Select
End Sub
'=======================================================================================================
'Write return value to file
Sub SetRetVal(iError)
Dim RetValFileStream
On Error Resume Next 'don't fail script execution if writing the return value to file fails
Dim SystemDrive : SystemDrive = OWshell.ExpandEnvironmentStrings("%systemdrive%")
Set RetValFileStream = oFso.createTextFile(SystemDrive & "\" & RETVALFILE,True,True)
RetValFileStream.Write iError
RetValFileStream.Close
End Sub 'SetRetVal
'=======================================================================================================
'Read return value from file.
'Used to ensure return value can get obtained from an elevated process
Function GetRetValFromFile ()
Dim RetValFileStream
Dim iRetValFromFile
Dim SystemDrive : SystemDrive = OWshell.ExpandEnvironmentStrings("%systemdrive%")
On Error Resume Next 'don't fail script execution when getting the return value from file fails
If oFso.FileExists(SystemDrive & "\" & RETVALFILE) Then
Set RetValFileStream = oFso.OpenTextFile(SystemDrive & "\" & RETVALFILE,1,False,-2)
GetRetValFromFile = RetValFileStream.ReadAll
RetValFileStream.Close
Exit Function
End If
Err.Clear
GetRetValFromFile = ERROR_UNKNOWN
End Function 'GetRetValFromFile
'=======================================================================================================
'Returns the process id of Me
Function GetMyProcessId()
Dim iParentProcessId
iParentProcessId = 0
' try to obtain from creating a new cscript instance
On Error Resume Next
iParentProcessId = GetObject("winmgmts:root\cimv2").Get("Win32_Process.Handle='" & oWShell.Exec("cscript.exe").ProcessId & "'").ParentProcessId
On Error Goto 0
If iParentProcessId > 0 Then
' succeeded to obtain the process id
GetMyProcessId = iParentProcessId
Exit Function
End If
' failed to obtain the id from the creation of a new instance
' get it from enum of Win32_Process
Dim Process,Processes
Err.Clear
Set Processes = oWmiLocal.ExecQuery("Select * From Win32_Process WHERE Name='cscript.exe' AND CommandLine like '%" & SCRIPTNAME & "%'")
For Each Process in Processes
iParentProcessId = Process.ProcessId
Exit For
Next
GetMyProcessId = iParentProcessId
End Function 'GetMyProcessId
'=======================================================================================================
'End all running instances of applications that will be removed
Sub CloseOfficeApps
Dim Processes, Process
Dim fWait
Dim iRet
On Error Resume Next
fWait = False
Log " Doing Action: CloseOfficeApps"
'OfficeVirt.exe needs to be shut down first
Set Processes = oWmiLocal.ExecQuery("Select * From Win32_Process Where Name like 'officevirt%.exe'")
For Each Process in Processes
If dicApps.Exists(LCase(Process.Name)) Then
Log " - End process " & Process.Name
iRet = Process.Terminate()
CheckError "CloseOfficeApps: " & "Process.Name"
fWait = True
End If
Next 'Process
Set Processes = oWmiLocal.ExecQuery("Select * From Win32_Process")
For Each Process in Processes
If dicApps.Exists(LCase(Process.Name)) Then
Log " - End process " & Process.Name
iRet = Process.Terminate()
CheckError "CloseOfficeApps: " & "Process.Name"
If Process.Name = "CVH.EXE" Then fWait = True
End If
Next 'Process
If fWait Then
wscript.sleep 10000
End If
LogOnly " End Action: CloseOfficeApps"
End Sub 'CloseOfficeApps
'=======================================================================================================
'CVHBS.exe has no true unattended option
'To ensure quiet automation does not break this dialog box handler monitors the process
Sub CvhbsDialogHandler
Dim CvhbsQuiet
Dim sRunCmd, sQuote
Set CvhbsQuiet = oFso.CreateTextFile(sScrubDir&"\CvhbsQuiet.vbs",True,True)
sQuote = "&chr(34)&"
CvhbsQuiet.WriteLine "On Error Resume Next"
CvhbsQuiet.WriteLine "Set oShell = CreateObject("&chr(34)&"WScript.Shell"&chr(34)&")"
CvhbsQuiet.WriteLine "Set oWmiLocal = GetObject("&chr(34)&"winmgmts:\\.\root\cimv2"&chr(34)&")"
CvhbsQuiet.WriteLine "wscript.sleep 10000"
CvhbsQuiet.WriteLine "Do"
CvhbsQuiet.WriteLine "Set Processes = oWmiLocal.ExecQuery("&chr(34)&"Select * From Win32_Process Where Name='cvhbs.exe'"&chr(34)&")"
CvhbsQuiet.WriteLine "iCnt = Processes.Count"
CvhbsQuiet.WriteLine "If iCnt > 0 Then"
CvhbsQuiet.WriteLine "sCommand = "&chr(34)&"tasklist /FI "&chr(34)&sQuote&chr(34)&"WINDOWTITLE eq click*"&chr(34)&sQuote&chr(34)&" /FO CSV /NH"&chr(34)
CvhbsQuiet.WriteLine "Set oExec = oShell.Exec(sCommand)"
CvhbsQuiet.WriteLine "sCmdOut = oExec.StdOut.ReadAll()"
CvhbsQuiet.WriteLine "Do While oExec.Status = 0"
CvhbsQuiet.WriteLine "WScript.Sleep 200"
CvhbsQuiet.WriteLine "Loop"
CvhbsQuiet.WriteLine "If InStr(sCmdOut,"&chr(34)&","&chr(34)&")>0 Then"
CvhbsQuiet.WriteLine "sCmdOut = Replace(sCmdOut,chr(34),"&chr(34)&chr(34)&")"
CvhbsQuiet.WriteLine "arrCol = Split(sCmdOut,"&chr(34)&","&chr(34)&")"
CvhbsQuiet.WriteLine "sPid = arrCol(1)"
CvhbsQuiet.WriteLine "oShell.AppActivate sPID"
CvhbsQuiet.WriteLine "oShell.SendKeys "&chr(34)&"{ENTER}"&chr(34)
CvhbsQuiet.WriteLine "End If"
CvhbsQuiet.WriteLine "End If"
CvhbsQuiet.WriteLine "wscript.sleep 10000"
CvhbsQuiet.WriteLine "Loop While iCnt > 0"
CvhbsQuiet.Close
sRunCmd = "cscript "&chr(34)&sScrubDir&"\CvhbsQuiet.vbs"&chr(34)
oWShell.Run sRunCmd, 0, False
End Sub 'CvhbsDialogHandler
'=======================================================================================================
'Ensure Windows Explorer is restarted if needed
Sub RestoreExplorer
Dim Processes
'Non critical routine. Don't fail on error
On Error Resume Next
wscript.sleep 1000
Set Processes = oWmiLocal.ExecQuery("Select * From Win32_Process Where Name='explorer.exe'")
If Processes.Count < 1 Then oWShell.Run "explorer.exe"
End Sub 'RestoreExploer
'=======================================================================================================
'Check registry access permissions. Failure will terminate the script
Function CheckRegPermissions
Const KEY_QUERY_VALUE = &H0001
Const KEY_SET_VALUE = &H0002
Const KEY_CREATE_SUB_KEY = &H0004
Const DELETE = &H00010000
Dim sSubKeyName
Dim fReturn
CheckRegPermissions = True
sSubKeyName = "Software\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\"
oReg.CheckAccess HKLM, sSubKeyName, KEY_QUERY_VALUE, fReturn
If Not fReturn Then CheckRegPermissions = False
oReg.CheckAccess HKLM, sSubKeyName, KEY_SET_VALUE, fReturn
If Not fReturn Then CheckRegPermissions = False
oReg.CheckAccess HKLM, sSubKeyName, KEY_CREATE_SUB_KEY, fReturn
If Not fReturn Then CheckRegPermissions = False
oReg.CheckAccess HKLM, sSubKeyName, DELETE, fReturn
If Not fReturn Then CheckRegPermissions = False
End Function 'CheckRegPermissions
'=======================================================================================================
'Check if an Office product is still registered with a SKU that stays on the computer
Function CheckDelete(sProductCode)
'Ensure valid GUID length
If NOT Len(sProductCode) = 38 Then
CheckDelete = False
Exit Function
End If
'If it's a non Office ProductCode exit with false right away
CheckDelete = InScope(sProductCode)
If Not CheckDelete Then Exit Function
If dicKeepProd.Exists(UCase(sProductCode)) Then CheckDelete = False
End Function 'CheckDelete
'=======================================================================================================
'Check if ProductCode is in scope
Function InScope(sProductCode)
Dim fInScope
Dim sProd
fInScope = False
If Len(sProductCode) = 38 Then
sProd = UCase(sProductCode)
Select Case OVERSIONMAJOR
Case "11"
If Right(sProd,PRODLEN)=OFFICEID Then InScope = True
Case "12"
If Right(sProd,PRODLEN)=OFFICEID AND Mid(sProd,4,2) = OVERSIONMAJOR Then fInScope = True
Case "14"
If Right(sProd,PRODLEN)=OFFICEID AND Mid(sProd,4,2) = OVERSIONMAJOR Then fInScope = True
Case Else
End Select
End If '38
InScope = fInScope
End Function 'InScope
'=======================================================================================================
'Register an orphaned .msi product as installed for MSI
Sub MsiRegisterProduct (sMsiFile)
Dim sDisplayVersion, sCurKey, sDisplayName, sLang, sProductCode, sTmpKey
Dim iCnt
'Create a temporary keys to simulate an installed product
sProductCode = ""
sProductCode = GetMsiProductCode(sMsiFile)
sDisplayVersion = GetMsiProductVersion(sMsiFile)
If sDisplayVersion = "" Then sDisplayVersion = OVERSION & ".0000.0000"
sDisplayName = GetMsiProductName(sMsiFile)
If sDisplayName = "" Then sDisplayName = sProductCode
Select Case OVERSIONMAJOR
Case "9","10","11"
sLang = CInt("&h" & Mid(sProductCode,6,4))
Case "12","14"
sLang = CInt("&h" & Mid(sProductCode,16,4))
Case Else
End Select
For iCnt = 1 To 3
Select Case iCnt
Case 1
sCurKey = REG_ARP & sProductCode
oReg.CreateKey HKLM,sCurKey
Case 2
sCurKey = "SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\" & GetCompressedGuid(sProductCode)
oReg.CreateKey HKLM,sCurKey
oReg.CreateKey HKLM,sCurKey & "\Features"
oReg.CreateKey HKLM,sCurKey & "\InstallProperties"
oReg.CreateKey HKLM,sCurKey & "\Patches"
oReg.CreateKey HKLM,sCurKey & "\Usage"
sCurKey = sCurKey & "\InstallProperties"
oReg.SetStringValue HKLM,sCurKey,"LocalPackage",sMsiFile
Case 3
sCurKey = "Installer\Products\" & GetCompressedGuid(sProductCode)
sTmpKey = sCurKey
oReg.CreateKey HKCR,sCurKey
oReg.SetDWordValue HKCR,sCurKey,"AdvertiseFlags",388
oReg.SetDWordValue HKCR,sCurKey,"Assignment",1
oReg.SetDWordValue HKCR,sCurKey,"AuthorizedLUAApp",0
oReg.SetStringValue HKCR,sCurKey,"Clients",":"
oReg.SetDWordValue HKCR,sCurKey,"DeploymentFlags",3
oReg.SetDWordValue HKCR,sCurKey,"InstanceType",0
oReg.SetDWordValue HKCR,sCurKey,"Language",sLang
oReg.SetStringValue HKCR,sCurKey,"PackageCode",GetMsiPackageCode(sMsiFile)
oReg.SetStringValue HKCR,sCurKey,"ProductName",sDisplayName
oReg.SetDWordValue HKCR,sCurKey,"VersionMinor",0
sCurKey = sTmpKey & "\SourceList"
oReg.CreateKey HKCR,sCurKey
oReg.SetExpandedStringValue HKCR,sCurKey,"LastUsedSource",sScrubDir
oReg.SetStringValue HKCR,sCurKey,"PackageName",Mid(sMsiFile,InstrRev(sMsiFile,"\")+1)
sCurKey = sTmpKey & "\SourceList\Media"
oReg.CreateKey HKCR,sCurKey
oReg.SetStringValue HKCR,sCurKey,"1",OREF & ";1"
oReg.SetStringValue HKCR,sCurKey,"DiskPrompt",sDisplayName
sCurKey = sTmpKey & "\SourceList\Net"
oReg.CreateKey HKCR,sCurKey
oReg.SetExpandedStringValue HKCR,sCurKey,"1",sScrubDir
Case Else
End Select
If iCnt <3 Then
oReg.SetStringValue HKLM,sCurKey,"Comments",""
oReg.SetStringValue HKLM,sCurKey,"Contact",""
oReg.SetStringValue HKLM,sCurKey,"DisplayName",sDisplayName
oReg.SetStringValue HKLM,sCurKey,"DisplayVersion",sDisplayVersion
oReg.SetDWordValue HKLM,sCurKey,"EstimatedSize",0
oReg.SetStringValue HKLM,sCurKey,"HelpLink",""
oReg.SetStringValue HKLM,sCurKey,"HelpTelephone",""
oReg.SetStringValue HKLM,sCurKey,"InstallDate","20100101"
If f64 Then
oReg.SetStringValue HKLM,sCurKey,"InstallLocation",sProgramFilesX86
Else
oReg.SetStringValue HKLM,sCurKey,"InstallLocation",sProgramFiles
End If
oReg.SetStringValue HKLM,sCurKey,"InstallSource",sScrubDir
oReg.SetDWordValue HKLM,sCurKey,"Language",sLang
oReg.SetExpandedStringValue HKLM,sCurKey,"ModifyPath","MsiExec.exe /X" & sProductCode
oReg.SetDWordValue HKLM,sCurKey,"NoModify",1
oReg.SetStringValue HKLM,sCurKey,"Publisher","Microsoft Corporation"
oReg.SetStringValue HKLM,sCurKey,"Readme",""
oReg.SetStringValue HKLM,sCurKey,"Size",""
oReg.SetDWordValue HKLM,sCurKey,"SystemComponent",0
oReg.SetExpandedStringValue HKLM,sCurKey,"UninstallString","MsiExec.exe /X" & sProductCode
oReg.SetStringValue HKLM,sCurKey,"URLInfoAbout",""
oReg.SetStringValue HKLM,sCurKey,"URLUpdateInfo",""
oReg.SetDWordValue HKLM,sCurKey,"Version",0
oReg.SetDWordValue HKLM,sCurKey,"VersionMajor",OVERSIONMAJOR
oReg.SetDWordValue HKLM,sCurKey,"VersionMinor",0
oReg.SetDWordValue HKLM,sCurKey,"WindowsInstaller",1
End If '< 3
Next 'iCnt
End Sub 'MsiRegisterProduct
'=======================================================================================================
'Obtain the ProductCode (GUID) from a .msi package
'The function will open the .msi database and query the 'Property' table to retrieve the ProductCode
Function GetMsiProductCode(sMsiFile)
Dim MsiDb,Record
Dim qView
On Error Resume Next
GetMsiProductCode = ""
Set Record = Nothing
Set MsiDb = oMsi.OpenDatabase(sMsiFile,MSIOPENDATABASEREADONLY)
Set qView = MsiDb.OpenView("SELECT `Value` FROM Property WHERE `Property` = 'ProductCode'")
qView.Execute
Set Record = qView.Fetch
GetMsiProductCode = Record.StringData(1)
qView.Close
End Function 'GetMsiProductCode
'=======================================================================================================
'Obtain the ProductVersion from a .msi package
'The function will open the .msi database and query the 'Property' table to retrieve the ProductCode
Function GetMsiProductVersion(sMsiFile)
Dim MsiDb,Record
Dim qView
On Error Resume Next
GetMsiProductVersion = ""
Set Record = Nothing
Set MsiDb = oMsi.OpenDatabase(sMsiFile,MSIOPENDATABASEREADONLY)
Set qView = MsiDb.OpenView("SELECT `Value` FROM Property WHERE `Property` = 'ProductVersion'")
qView.Execute
Set Record = qView.Fetch
GetMsiProductVersion = Record.StringData(1)
qView.Close
End Function 'GetMsiProductVersion
'=======================================================================================================
'Obtain the ProductVersion from a .msi package
'The function will open the .msi database and query the 'Property' table to retrieve the ProductCode
Function GetMsiProductName(sMsiFile)
Dim MsiDb,Record
Dim qView
On Error Resume Next
GetMsiProductName = ""
Set Record = Nothing
Set MsiDb = oMsi.OpenDatabase(sMsiFile,MSIOPENDATABASEREADONLY)
Set qView = MsiDb.OpenView("SELECT `Value` FROM Property WHERE `Property` = 'ProductName'")
qView.Execute
Set Record = qView.Fetch
GetMsiProductName = Record.StringData(1)
qView.Close
End Function 'GetMsiProductVersion
'=======================================================================================================
'Obtain the PackageCode (GUID) from a .msi package
'The function will the .msi'S SummaryInformation stream
Function GetMsiPackageCode(sMsiFile)
On Error Resume Next
Const PID_REVNUMBER = 9
GetMsiPackageCode = ""
GetMsiPackageCode = GetCompressedGuid(oMsi.SummaryInformation(sMsiFile,MSIOPENDATABASEREADONLY).Property(PID_REVNUMBER))
End Function 'GetMsiPackageCode
'=======================================================================================================
'Returns a string with a list of ProductCodes from the summary information stream
Function MspTargets (sMspFile)
Const MSIOPENDATABASEMODE_PATCHFILE = 32
Const PID_TEMPLATE = 7
Dim Msp
'Non critical routine. Don't fail on error
On Error Resume Next
MspTargets = ""
If oFso.FileExists(sMspFile) Then
Set Msp = Msi.OpenDatabase(WScript.Arguments(0),MSIOPENDATABASEMODE_PATCHFILE)
If Err = 0 Then MspTargets = Msp.SummaryInformation.Property(PID_TEMPLATE)
End If 'oFso.FileExists(sMspFile)
End Function 'MspTargets
'=======================================================================================================
'Return the ProductCode {GUID} from a .MSI package
Function ProductCode(sMsi)
Const MSIUILEVELNONE = 2 'No UI
Dim MsiSession
On Error Resume Next
'Non critical routine. Don't fail on error
If oFso.FileExists(sMsi) Then
oMsi.UILevel = MSIUILEVELNONE
Set MsiSession = oMsi.OpenPackage(sMsi,1)
ProductCode = MsiSession.ProductProperty("ProductCode")
Set MsiSession = Nothing
Else
ProductCode = ""
End If 'oFso.FileExists(sMsi)
End Function 'ProductCode
'=======================================================================================================
Function GetExpandedGuid (sGuid)
Dim i
'Ensure valid length
If NOT Len(sGuid) = 32 Then Exit Function
GetExpandedGuid = "{" & StrReverse(Mid(sGuid,1,8)) & "-" & _
StrReverse(Mid(sGuid,9,4)) & "-" & _
StrReverse(Mid(sGuid,13,4))& "-"
For i = 17 To 20
If i Mod 2 Then
GetExpandedGuid = GetExpandedGuid & mid(sGuid,(i + 1),1)
Else
GetExpandedGuid = GetExpandedGuid & mid(sGuid,(i - 1),1)
End If
Next
GetExpandedGuid = GetExpandedGuid & "-"
For i = 21 To 32
If i Mod 2 Then
GetExpandedGuid = GetExpandedGuid & mid(sGuid,(i + 1),1)
Else
GetExpandedGuid = GetExpandedGuid & mid(sGuid,(i - 1),1)
End If
Next
GetExpandedGuid = GetExpandedGuid & "}"
End Function
'=======================================================================================================
'Converts a GUID into the compressed format
Function GetCompressedGuid (sGuid)
Dim sCompGUID
Dim i
'Ensure Valid Length
If NOT Len(sGuid) = 38 Then Exit Function
sCompGUID = StrReverse(Mid(sGuid,2,8)) & _
StrReverse(Mid(sGuid,11,4)) & _
StrReverse(Mid(sGuid,16,4))
For i = 21 To 24
If i Mod 2 Then
sCompGUID = sCompGUID & Mid(sGuid, (i + 1), 1)
Else
sCompGUID = sCompGUID & Mid(sGuid, (i - 1), 1)
End If
Next
For i = 26 To 37
If i Mod 2 Then
sCompGUID = sCompGUID & Mid(sGuid, (i - 1), 1)
Else
sCompGUID = sCompGUID & Mid(sGuid, (i + 1), 1)
End If
Next
GetCompressedGuid = sCompGUID
End Function
'=======================================================================================================
'Unsquish GUID
Function GetDecodedGuid(sEncGuid, sGuid)
Dim sDecode, sTable, sHex, iChr
Dim arrTable
Dim i, iAsc, pow85, decChar
Dim lTotal
Dim fFailed
fFailed = False
sTable = "0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff," & _
"0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff," & _
"0xff,0x00,0xff,0xff,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0xff," & _
"0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0xff,0xff,0xff,0x16,0xff,0x17," & _
"0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27," & _
"0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x30,0x31,0x32,0x33,0xff,0x34,0x35,0x36," & _
"0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x40,0x41,0x42,0x43,0x44,0x45,0x46," & _
"0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,0x50,0x51,0x52,0xff,0x53,0x54,0xff"
arrTable = Split(sTable,",")
lTotal = 0 : pow85 = 1
For i = 0 To 19
fFailed = True
If i Mod 5 = 0 Then
lTotal = 0 : pow85 = 1
End If ' i Mod 5 = 0
iAsc = Asc(Mid(sEncGuid,i+1,1))
sHex = arrTable(iAsc)
If iAsc >=128 Then Exit For
If sHex = "0xff" Then Exit For
iChr = CInt("&h"&Right(sHex,2))
lTotal = lTotal + (iChr * pow85)
If i Mod 5 = 4 Then sDecode = sDecode & DecToHex(lTotal)
pow85 = pow85 * 85
fFailed = False
Next 'i
If NOT fFailed Then sGuid = "{"&Mid(sDecode,1,8)&"-"& _
Mid(sDecode,13,4)&"-"& _
Mid(sDecode,9,4)&"-"& _
Mid(sDecode,23,2) & Mid(sDecode,21,2)&"-"& _
Mid(sDecode,19,2) & Mid(sDecode,17,2) & Mid(sDecode,31,2) & Mid(sDecode,29,2) & Mid(sDecode,27,2) & Mid(sDecode,25,2) &"}"
GetDecodedGuid = NOT fFailed
End Function 'GetDecodedGuid
'=======================================================================================================
'Convert a long decimal to hex
Function DecToHex(lDec)
Dim sHex
Dim iLen
Dim lVal, lExp
Dim arrChr
arrChr = Array("0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F")
sHex = ""
lVal = lDec
lExp = 16^10
While lExp >= 1
If lVal >= lExp Then
sHex = sHex & arrChr(Int(lVal / lExp))
lVal = lVal - lExp * Int(lVal / lExp)
Else
sHex = sHex & "0"
If sHex = "0" Then sHex = ""
End If
lExp = lExp / 16
Wend
iLen = 8 - Len(sHex)
If iLen > 0 Then sHex = String(iLen,"0") & sHex
DecToHex = sHex
End Function
'=======================================================================================================
'Ensures that only valid metadata entries exist to avoid API failures
Sub EnsureValidWIMetadata (hDefKey,sKey,iValidLength)
Dim arrKeys
Dim SubKey
If Len(sKey) > 1 Then
If Right(sKey,1) = "\" Then sKey = Left(sKey,Len(sKey)-1)
End If
If RegEnumKey(hDefKey,sKey,arrKeys) Then
For Each SubKey in arrKeys
If NOT Len(SubKey) = iValidLength Then
RegDeleteKey hDefKey,sKey & "\" & SubKey & "\"
End If
Next 'SubKey
End If
End Sub 'EnsureValidWIMetadata
'=======================================================================================================
'Create a backup copy of the file in the ScrubDir then delete the file
Sub CopyAndDeleteFile(sFile)
Dim File
'Error handling inlined
On Error Resume Next
If oFso.FileExists(sFile) Then
Set File = oFso.GetFile(sFile)
If Not oFso.FolderExists(sScrubDir & "\" & File.ParentFolder.Name) Then oFso.CreateFolder sScrubDir & "\" & File.ParentFolder.Name
If Not fDetectOnly Then
LogOnly " - Backing up file: " & sFile
oFso.CopyFile sFile,sScrubDir & "\" & File.ParentFolder.Name & "\" & File.Name,True : CheckError "CopyAndDeleteFile"
Set File = Nothing
DeleteFile(sFile)
Else
LogOnly " - Simulate CopyAndDelete file: " & sFile
End If
End If 'oFso.FileExists
End Sub 'CopyAndDeleteFile
'=======================================================================================================
'Wrapper to delete a file
Sub DeleteFile(sFile)
Dim File
Dim sFileName, sNewPath
On Error Resume Next
If dicKeepFolder.Exists(LCase(sFile)) Then
If NOT fForce Then
LogOnly " - Disallowing the delete of still required keypath element: " & sFile
Exit Sub
Else
LogOnly " - Enforced delete of still required keypath element: " & sFile
LogOnly " Remaining applications will need a repair!"
End If
End If
If f64 Then
If dicKeepFolder.Exists(LCase(Wow64Folder(sFile))) Then
If NOT fForce Then
LogOnly " - Disallowing the delete of still required keypath element: " & sFile
Exit Sub
Else
LogOnly " - Enforced delete of still required keypath element: " & sFile
LogOnly " Remaining applications will need a repair!"
End If
End If
End If
If oFso.FileExists(sFile) Then
LogOnly " - Delete file: " & sFile
If Not fDetectOnly Then oFso.DeleteFile sFile,True
If Err <> 0 Then
CheckError "DeleteFile"
If fForce Then
'Try to move the file and delete from there
Set File = oFso.GetFile(sFile)
sFileName = File.Name
sNewPath = sScrubDir & "\ScrubTmp"
Set File = Nothing
If Not oFso.FolderExists(sNewPath) Then oFso.CreateFolder(sNewPath)
'Move the file
LogOnly " - Move file to: " & sNewPath & "\" & sFileName
oFso.MoveFile sFile,sNewPath & "\" & sFileName
If Err <> 0 Then
CheckError "DeleteFile (move)"
End If 'Err <> 0
End If 'fForce
End If 'Err <> 0
End If 'oFso.FileExists
End Sub 'DeleteFile
'=======================================================================================================
'64 bit aware wrapper to return the requested folder
Function GetFolderPath(sPath)
GetFolderPath = True
If oFso.FolderExists(sPath) Then Exit Function
If f64 AND oFso.FolderExists(Wow64Folder(sPath)) Then
sPath = Wow64Folder(sPath)
Exit Function
End If
GetFolderPath = False
End Function 'GetFolderPath
'=======================================================================================================
'Enumerates subfolder names of a folder and returns True if subfolders exist
Function EnumFolderNames (sFolder, arrSubFolders)
Dim Folder, Subfolder
Dim sSubFolders
If oFso.FolderExists(sFolder) Then
Set Folder = oFso.GetFolder(sFolder)
For Each Subfolder in Folder.Subfolders
sSubFolders = sSubFolders & Subfolder.Name & ","
Next 'Subfolder
End If
If f64 AND oFso.FolderExists(Wow64Folder(sFolder)) Then
Set Folder = oFso.GetFolder(Wow64Folder(sFolder))
For Each Subfolder in Folder.Subfolders
sSubFolders = sSubFolders & Subfolder.Name & ","
Next 'Subfolder
End If
If Len(sSubFolders)>0 Then arrSubFolders = RemoveDuplicates(Split(Left(sSubFolders,Len(sSubFolders)-1),","))
EnumFolderNames = Len(sSubFolders)>0
End Function 'EnumFolderNames
'=======================================================================================================
'Enumerates subfolders of a folder and returns True if subfolders exist
Function EnumFolders (sFolder, arrSubFolders)
Dim Folder, Subfolder
Dim sSubFolders
If oFso.FolderExists(sFolder) Then
Set Folder = oFso.GetFolder(sFolder)
For Each Subfolder in Folder.Subfolders
sSubFolders = sSubFolders & Subfolder.Path & ","
Next 'Subfolder
End If
If f64 AND oFso.FolderExists(Wow64Folder(sFolder)) Then
Set Folder = oFso.GetFolder(Wow64Folder(sFolder))
For Each Subfolder in Folder.Subfolders
sSubFolders = sSubFolders & Subfolder.Path & ","
Next 'Subfolder
End If
If Len(sSubFolders)>0 Then arrSubFolders = RemoveDuplicates(Split(Left(sSubFolders,Len(sSubFolders)-1),","))
EnumFolders = Len(sSubFolders)>0
End Function 'EnumFolders
'=======================================================================================================
Sub GetMseFolderStructure (Folder)
Dim SubFolder
For Each SubFolder in Folder.SubFolders
ReDim Preserve arrMseFolders(UBound(arrMseFolders)+1)
arrMseFolders(UBound(arrMseFolders)) = SubFolder.Path
GetMseFolderStructure SubFolder
Next 'SubFolder
End Sub 'GetMseFolderStructure
'=======================================================================================================
'Wrapper to delete a folder
Sub DeleteFolder(sFolder)
Dim Folder
Dim sDelFolder, sFolderName, sNewPath
'Ensure trailing "\"
sFolder = sFolder & "\"
While InStr(sFolder,"\\")>0
sFolder = Replace(sFolder,"\\","\")
Wend
If dicKeepFolder.Exists(LCase(sFolder)) Then
If NOT fForce Then
LogOnly " - Disallowing the delete of still required keypath element: " & sFolder
Exit Sub
Else
LogOnly " - Enforced delete of still required keypath element: " & sFolder
LogOnly " Remaining applications will need a repair!"
End If
End If
If f64 Then
If dicKeepFolder.Exists(LCase(Wow64Folder(sFolder))) Then
If NOT fForce Then
LogOnly " - Disallowing the delete of still required keypath element: " & sFolder
Exit Sub
Else
LogOnly " - Enforced delete of still required keypath element: " & sFolder
LogOnly " Remaining applications will need a repair!"
End If
End If
End If
'Strip trailing "\"
If Len(sFolder) > 1 Then
sFolder = Left(sFolder,Len(sFolder)-1)
End If
On Error Resume Next
If oFso.FolderExists(sFolder) Then
sDelFolder = sFolder
ElseIf f64 AND oFso.FolderExists(Wow64Folder(sFolder)) Then
sDelFolder = Wow64Folder(sFolder)
Else
Exit Sub
End If
If Not fDetectOnly Then
LogOnly " - Delete folder: " & sDelFolder
oFso.DeleteFolder sDelFolder,True
Else
LogOnly " - Simulate delete folder: " & sDelFolder
End If
If Err <> 0 Then
CheckError "DeleteFolder"
'Try to move the folder and delete from there
Set Folder = oFso.GetFolder(sDelFolder)
sFolderName = Folder.Name
sNewPath = sScrubDir & "\ScrubTmp"
Set Folder = Nothing
'Ensure we stay within the same drive
If Not oFso.FolderExists(sNewPath) Then oFso.CreateFolder(sNewPath)
'Move the folder
LogOnly " - Moving folder to: " & sNewPath & "\" & sFolderName
oFso.MoveFolder sFolder,sNewPath & "\" & sFolderName
If Err <> 0 Then
CheckError "DeleteFolder (move)"
End If 'Err <> 0
End If 'Err <> 0
End Sub 'DeleteFolder
'=======================================================================================================
'Delete empty folder structures
Sub DeleteEmptyFolders
Dim Folder
Dim sFolder
If Not IsArray(arrDeleteFolders) Then Exit Sub
Log vbCrLf & " Empty Folder Cleanup"
For Each sFolder in arrDeleteFolders
If oFso.FolderExists(sFolder) Then
Set Folder = oFso.GetFolder(sFolder)
If (Folder.Subfolders.Count = 0) AND (Folder.Files.Count = 0) Then
Set Folder = Nothing
SmartDeleteFolder sFolder
End If
End If
Next 'sFolder
End Sub 'DeleteEmptyFolders
'=======================================================================================================
'Wrapper to delete a folder and remove the empty parent folder structure
Sub SmartDeleteFolder(sFolder)
If oFso.FolderExists(sFolder) Then
If Not fDetectOnly Then
LogOnly " Request SmartDelete for folder: " & sFolder
SmartDeleteFolderEx sFolder
Else
LogOnly " Simulate request SmartDelete for folder: " & sFolder
End If
End If
If f64 AND oFso.FolderExists(Wow64Folder(sFolder)) Then
If Not fDetectOnly Then
LogOnly "Request SmartDelete for folder: " & Wow64Folder(sFolder)
SmartDeleteFolderEx Wow64Folder(sFolder)
Else
LogOnly "Simulate request SmartDelete for folder: " & Wow64Folder(sFolder)
End If
End If
End Sub 'SmartDeleteFolder
'=======================================================================================================
'Executes the folder delete operation
Sub SmartDeleteFolderEx(sFolder)
Dim Folder
On Error Resume Next
DeleteFolder sFolder : CheckError "SmartDeleteFolderEx"
On Error Goto 0
Set Folder = oFso.GetFolder(oFso.GetParentFolderName(sFolder))
If (Folder.Subfolders.Count = 0) AND (Folder.Files.Count = 0) Then SmartDeleteFolderEx(Folder.Path)
End Sub 'SmartDeleteFolderEx
'=======================================================================================================
'Adds the folder structure to the 'KeepFolder' dictionary
Sub AddKeepFolder(sPath)
Dim Folder
'Ensure trailing "\"
sPath = LCase(sPath) & "\"
While InStr(sPath,"\\")>0
sPath = Replace(sPath,"\\","\")
Wend
If NOT dicKeepFolder.Exists (sPath) Then
dicKeepFolder.Add sPath,sPath
Else
Exit Sub
End If
sPath = LCase(oFso.GetParentFolderName(sPath)) & "\"
If oFso.FolderExists(sPath) Then AddKeepFolder(sPath)
End Sub
'=======================================================================================================
'Handles additional folder-path operations on 64 bit environments
Function Wow64Folder(sFolder)
If LCase(Left(sFolder,Len(sWinDir & "\System32"))) = LCase(sWinDir & "\System32") Then
Wow64Folder = sWinDir & "\syswow64" & Right(sFolder,Len(sFolder)-Len(sSys32Dir))
ElseIf LCase(Left(sFolder,Len(sProgramFiles))) = LCase(sProgramFiles) Then
Wow64Folder = sProgramFilesX86 & Right(sFolder,Len(sFolder)-Len(sProgramFiles))
Else
Wow64Folder = "?" 'Return invalid string to ensure the folder cannot exist
End If
End Function 'Wow64Folder
'=======================================================================================================
Function HiveString(hDefKey)
On Error Resume Next
Select Case hDefKey
Case HKCR : HiveString = "HKEY_CLASSES_ROOT"
Case HKCU : HiveString = "HKEY_CURRENT_USER"
Case HKLM : HiveString = "HKEY_LOCAL_MACHINE"
Case HKU : HiveString = "HKEY_USERS"
Case Else : HiveString = hDefKey
End Select
End Function
'=======================================================================================================
Function RegKeyExists(hDefKey,sSubKeyName)
Dim arrKeys
RegKeyExists = False
If oReg.EnumKey(hDefKey,sSubKeyName,arrKeys) = 0 Then RegKeyExists = True
End Function
'=======================================================================================================
Function RegValExists(hDefKey,sSubKeyName,sName)
Dim arrValueTypes, arrValueNames
Dim i
RegValExists = False
If Not RegKeyExists(hDefKey,sSubKeyName) Then Exit Function
If oReg.EnumValues(hDefKey,sSubKeyName,arrValueNames,arrValueTypes) = 0 AND IsArray(arrValueNames) Then
For i = 0 To UBound(arrValueNames)
If LCase(arrValueNames(i)) = Trim(LCase(sName)) Then RegValExists = True
Next
End If 'oReg.EnumValues
End Function
'=======================================================================================================
'Read the value of a given registry entry
Function RegReadValue(hDefKey, sSubKeyName, sName, sValue, sType)
Dim RetVal
Dim Item
Dim arrValues
Select Case UCase(sType)
Case "1","REG_SZ"
RetVal = oReg.GetStringValue(hDefKey,sSubKeyName,sName,sValue)
If Not RetVal = 0 AND f64 Then RetVal = oReg.GetStringValue(hDefKey,Wow64Key(hDefKey, sSubKeyName),sName,sValue)
Case "2","REG_EXPAND_SZ"
RetVal = oReg.GetExpandedStringValue(hDefKey,sSubKeyName,sName,sValue)
If Not RetVal = 0 AND f64 Then RetVal = oReg.GetExpandedStringValue(hDefKey,Wow64Key(hDefKey, sSubKeyName),sName,sValue)
Case "7","REG_MULTI_SZ"
RetVal = oReg.GetMultiStringValue(hDefKey,sSubKeyName,sName,arrValues)
If Not RetVal = 0 AND f64 Then RetVal = oReg.GetMultiStringValue(hDefKey,Wow64Key(hDefKey, sSubKeyName),sName,arrValues)
If RetVal = 0 Then sValue = Join(arrValues,chr(34))
Case "4","REG_DWORD"
RetVal = oReg.GetDWORDValue(hDefKey,sSubKeyName,sName,sValue)
If Not RetVal = 0 AND f64 Then
RetVal = oReg.GetDWORDValue(hDefKey,Wow64Key(hDefKey, sSubKeyName),sName,sValue)
End If
Case "3","REG_BINARY"
RetVal = oReg.GetBinaryValue(hDefKey,sSubKeyName,sName,sValue)
If Not RetVal = 0 AND f64 Then RetVal = oReg.GetBinaryValue(hDefKey,Wow64Key(hDefKey, sSubKeyName),sName,sValue)
Case "11","REG_QWORD"
RetVal = oReg.GetQWORDValue(hDefKey,sSubKeyName,sName,sValue)
If Not RetVal = 0 AND f64 Then RetVal = oReg.GetQWORDValue(hDefKey,Wow64Key(hDefKey, sSubKeyName),sName,sValue)
Case Else
RetVal = -1
End Select 'sValue
RegReadValue = (RetVal = 0)
End Function 'RegReadValue
'=======================================================================================================
'Enumerate a registry key to return all values
Function RegEnumValues(hDefKey,sSubKeyName,arrNames, arrTypes)
Dim RetVal, RetVal64
Dim arrNames32, arrNames64, arrTypes32, arrTypes64
If f64 Then
RetVal = oReg.EnumValues(hDefKey,sSubKeyName,arrNames32,arrTypes32)
RetVal64 = oReg.EnumValues(hDefKey,Wow64Key(hDefKey, sSubKeyName),arrNames64,arrTypes64)
If (RetVal = 0) AND (Not RetVal64 = 0) AND IsArray(arrNames32) AND IsArray(arrTypes32) Then
arrNames = arrNames32
arrTypes = arrTypes32
End If
If (Not RetVal = 0) AND (RetVal64 = 0) AND IsArray(arrNames64) AND IsArray(arrTypes64) Then
arrNames = arrNames64
arrTypes = arrTypes64
End If
If (RetVal = 0) AND (RetVal64 = 0) AND IsArray(arrNames32) AND IsArray(arrNames64) AND IsArray(arrTypes32) AND IsArray(arrTypes64) Then
arrNames = RemoveDuplicates(Split((Join(arrNames32,"\") & "\" & Join(arrNames64,"\")),"\"))
arrTypes = RemoveDuplicates(Split((Join(arrTypes32,"\") & "\" & Join(arrTypes64,"\")),"\"))
End If
Else
RetVal = oReg.EnumValues(hDefKey,sSubKeyName,arrNames,arrTypes)
End If 'f64
RegEnumValues = ((RetVal = 0) OR (RetVal64 = 0)) AND IsArray(arrNames) AND IsArray(arrTypes)
End Function 'RegEnumValues
'=======================================================================================================
'Enumerate a registry key to return all subkeys
Function RegEnumKey(hDefKey,sSubKeyName,arrKeys)
Dim RetVal, RetVal64
Dim arrKeys32, arrKeys64
If f64 Then
RetVal = oReg.EnumKey(hDefKey,sSubKeyName,arrKeys32)
RetVal64 = oReg.EnumKey(hDefKey,Wow64Key(hDefKey, sSubKeyName),arrKeys64)
If (RetVal = 0) AND (Not RetVal64 = 0) AND IsArray(arrKeys32) Then arrKeys = arrKeys32
If (Not RetVal = 0) AND (RetVal64 = 0) AND IsArray(arrKeys64) Then arrKeys = arrKeys64
If (RetVal = 0) AND (RetVal64 = 0) Then
If IsArray(arrKeys32) AND IsArray (arrKeys64) Then
arrKeys = RemoveDuplicates(Split((Join(arrKeys32,"\") & "\" & Join(arrKeys64,"\")),"\"))
ElseIf IsArray(arrKeys64) Then
arrKeys = arrKeys64
Else
arrKeys = arrKeys32
End If
End If
Else
RetVal = oReg.EnumKey(hDefKey,sSubKeyName,arrKeys)
End If 'f64
RegEnumKey = ((RetVal = 0) OR (RetVal64 = 0)) AND IsArray(arrKeys)
End Function 'RegEnumKey
'=======================================================================================================
'Wrapper around oReg.DeleteValue to handle 64 bit
Sub RegDeleteValue(hDefKey, sSubKeyName, sName)
Dim sWow64Key
Dim iRetVal
If dicKeepReg.Exists(LCase(sSubKeyName & sName)) Then
If NOT fForce Then
LogOnly " - Disallowing the delete of still required keypath element: " & HiveString(hDefKey) & "\" & sSubKeyName & sName
Exit Sub
Else
LogOnly " - Enforced delete of still required keypath element. Remaining applications will need a repair!"
End If
End If
If f64 Then
If dicKeepReg.Exists(LCase(Wow64Key(hDefKey, sSubKeyName) & sName)) Then
If NOT fForce Then
LogOnly " - Disallowing the delete of still required keypath element: " & HiveString(hDefKey) & "\" & sSubKeyName & sName
Exit Sub
Else
LogOnly " - Enforced delete of still required keypath element. Remaining applications will need a repair!"
End If
End If
End If
If RegValExists(hDefKey,sSubKeyName,sName) Then
On Error Resume Next
If Not fDetectOnly Then
LogOnly " - Delete registry value: " & HiveString(hDefKey) & "\" & sSubKeyName & " -> " & sName
iRetVal = 0
iRetVal = oReg.DeleteValue(hDefKey, sSubKeyName, sName)
CheckError "RegDeleteValue"
If NOT (iRetVal=0) Then
LogOnly " Delete failed. Return value: "&iRetVal
SetError ERROR_STAGE4
End If
Else
LogOnly " - Simulate delete registry value: " & HiveString(hDefKey) & "\" & sSubKeyName & " -> " & sName
End If
On Error Goto 0
End If 'RegValExists
If f64 Then
sWow64Key = Wow64Key(hDefKey, sSubKeyName)
If RegValExists(hDefKey,sWow64Key,sName) Then
On Error Resume Next
If Not fDetectOnly Then
LogOnly " - Delete registry value: " & HiveString(hDefKey) & "\" & sWow64Key & " -> " & sName
iRetVal = 0
iRetVal = oReg.DeleteValue(hDefKey, sWow64Key, sName)
CheckError "RegDeleteValue"
If NOT (iRetVal=0) Then
LogOnly " Delete failed. Return value: "&iRetVal
SetError ERROR_STAGE4
End If
Else
LogOnly " - Simulate delete registry value: " & HiveString(hDefKey) & "\" & sWow64Key & " -> " & sName
End If
On Error Goto 0
End If 'RegKeyExists
End If
End Sub 'RegDeleteValue
'=======================================================================================================
'Wrappper around RegDeleteKeyEx to handle 64bit scenrios
Sub RegDeleteKey(hDefKey, sSubKeyName)
Dim sWow64Key
'Ensure trailing "\"
sSubKeyName = sSubKeyName & "\"
While InStr(sSubKeyName,"\\")>0
sSubKeyName = Replace(sSubKeyName,"\\","\")
Wend
If dicKeepReg.Exists(LCase(sSubKeyName)) Then
If NOT fForce Then
LogOnly " - Disallowing the delete of still required keypath element: " & HiveString(hDefKey) & "\" & sSubKeyName
Exit Sub
Else
LogOnly " - Enforced delete of still required keypath element. Remaining applications will need a repair!"
End If
End If
If f64 Then
If dicKeepReg.Exists(LCase(Wow64Key(hDefKey, sSubKeyName))) Then
If NOT fForce Then
LogOnly " - Disallowing the delete of still required keypath element: " & HiveString(hDefKey) & "\" & sSubKeyName
Exit Sub
Else
LogOnly " - Enforced delete of still required keypath element. Remaining applications will need a repair!"
End If
End If
End If
If Len(sSubKeyName) > 1 Then
'Strip of trailing "\"
sSubKeyName = Left(sSubKeyName,Len(sSubKeyName)-1)
End If
If RegKeyExists(hDefKey, sSubKeyName) Then
If Not fDetectOnly Then
LogOnly " - Delete registry key: " & HiveString(hDefKey) & "\" & sSubKeyName
On Error Resume Next
RegDeleteKeyEx hDefKey, sSubKeyName
On Error Goto 0
Else
LogOnly " - Simulate delete registry key: " & HiveString(hDefKey) & "\" & sSubKeyName
End If
End If 'RegKeyExists
If f64 Then
sWow64Key = Wow64Key(hDefKey, sSubKeyName)
If RegKeyExists(hDefKey,sWow64Key) Then
If Not fDetectOnly Then
LogOnly " - Delete registry key: " & HiveString(hDefKey) & "\" & sWow64Key
On Error Resume Next
RegDeleteKeyEx hDefKey, sWow64Key
On Error Goto 0
Else
LogOnly " - Simulate delete registry key: " & HiveString(hDefKey) & "\" & sWow64Key
End If
End If 'RegKeyExists
End If
End Sub 'RegDeleteKey
'=======================================================================================================
'Recursively delete a registry structure
Sub RegDeleteKeyEx(hDefKey, sSubKeyName)
Dim arrSubkeys
Dim sSubkey
Dim iRetVal
On Error Resume Next
oReg.EnumKey hDefKey, sSubKeyName, arrSubkeys
If IsArray(arrSubkeys) Then
For Each sSubkey In arrSubkeys
RegDeleteKeyEx hDefKey, sSubKeyName & "\" & sSubkey
Next
End If
If Not fDetectOnly Then
iRetVal = 0
iRetVal = oReg.DeleteKey(hDefKey,sSubKeyName)
If NOT (iRetVal=0) Then
SetError ERROR_STAGE4
LogOnly " Delete failed. Return value: "&iRetVal
End If
End If
End Sub 'RegDeleteKeyEx
'=======================================================================================================
'Return the alternate regkey location on 64bit environment
Function Wow64Key(hDefKey, sSubKeyName)
Dim iPos
Select Case hDefKey
Case HKCU
If Left(sSubKeyName,17) = "Software\Classes\" Then
Wow64Key = Left(sSubKeyName,17) & "Wow6432Node\" & Right(sSubKeyName,Len(sSubKeyName)-17)
Else
iPos = InStr(sSubKeyName,"\")
Wow64Key = Left(sSubKeyName,iPos) & "Wow6432Node\" & Right(sSubKeyName,Len(sSubKeyName)-iPos)
End If
Case HKLM
If Left(sSubKeyName,17) = "Software\Classes\" Then
Wow64Key = Left(sSubKeyName,17) & "Wow6432Node\" & Right(sSubKeyName,Len(sSubKeyName)-17)
Else
iPos = InStr(sSubKeyName,"\")
Wow64Key = Left(sSubKeyName,iPos) & "Wow6432Node\" & Right(sSubKeyName,Len(sSubKeyName)-iPos)
End If
Case Else
Wow64Key = "Wow6432Node\" & sSubKeyName
End Select 'hDefKey
End Function 'Wow64Key
'=======================================================================================================
'Remove duplicate entries from a one dimensional array
Function RemoveDuplicates(Array)
Dim Item
Dim oDic
Set oDic = CreateObject("Scripting.Dictionary")
For Each Item in Array
If Not oDic.Exists(Item) Then oDic.Add Item,Item
Next 'Item
RemoveDuplicates = oDic.Keys
End Function 'RemoveDuplicates
'=======================================================================================================
'Uses WMI to stop a service
Function StopService(sService)
Dim Services, Service
Dim sQuery
Dim iRet
On Error Resume Next
iRet = 0
sQuery = "Select * From Win32_Service Where Name='" & sService & "'"
Set Services = oWmiLocal.Execquery(sQuery)
'Stop the service
For Each Service in Services
If UCase(Service.State) = "STARTED" Then iRet = Service.StopService
If UCase(Service.State) = "RUNNING" Then iRet = Service.StopService
Next 'Service
StopService = (iRet = 0)
End Function 'StopService
'=======================================================================================================
'Delete a service
Sub DeleteService(sService)
Dim Services, Service, Processes, Process
Dim sQuery, sStates
Dim iRet
On Error Resume Next
sStates = "STARTED;RUNNING"
sQuery = "Select * From Win32_Service Where Name='" & sService & "'"
Set Services = oWmiLocal.Execquery(sQuery)
'Stop and delete the service
For Each Service in Services
Log " Found service " & sService & " in state " & Service.State
If InStr(sStates,UCase(Service.State))>0 Then iRet = Service.StopService()
'Ensure no more instances of the service are running
Set Processes = oWmiLocal.ExecQuery("Select * From Win32_Process Where Name='" & sService & ".exe'")
For Each Process in Processes
iRet = Process.Terminate()
Next 'Process
If Not fDetectOnly Then
Log " - Deleting Service -> " & sService
iRet = Service.Delete()
Else
Log " - Simulate deleting Service -> " & sService
End If
Next 'Service
Set Services = Nothing
Err.Clear
End Sub 'DeleteService
'=======================================================================================================
'Translation for setup.exe error codes
Function SetupRetVal(RetVal)
Select Case RetVal
Case 0 : SetupRetVal = "Success"
Case 30001,1 : SetupRetVal = "AbstractMethod"
Case 30002,2 : SetupRetVal = "ApiProhibited"
Case 30003,3 : SetupRetVal = "AlreadyImpersonatingAUser"
Case 30004,4 : SetupRetVal = "AlreadyInitialized"
Case 30005,5 : SetupRetVal = "ArgumentNullException"
Case 30006,6 : SetupRetVal = "AssertionFailed"
Case 30007,7 : SetupRetVal = "CABFileAddFailed"
Case 30008,8 : SetupRetVal = "CommandFailed"
Case 30009,9 : SetupRetVal = "ConcatenationFailed"
Case 30010,10 : SetupRetVal = "CopyFailed"
Case 30011,11 : SetupRetVal = "CreateEventFailed"
Case 30012,12 : SetupRetVal = "CustomizationPatchNotFound"
Case 30013,13 : SetupRetVal = "CustomizationPatchNotApplicable"
Case 30014,14 : SetupRetVal = "DuplicateDefinition"
Case 30015,15 : SetupRetVal = "ErrorCodeOnly - Passthrough for Win32 error"
Case 30016,16 : SetupRetVal = "ExceptionNotThrown"
Case 30017,17 : SetupRetVal = "FailedToImpersonateUser"
Case 30018,18 : SetupRetVal = "FailedToInitializeFlexDataSource"
Case 30019,19 : SetupRetVal = "FailedToStartClassFactories"
Case 30020,20 : SetupRetVal = "FileNotFound"
Case 30021,21 : SetupRetVal = "FileNotOpen"
Case 30022,22 : SetupRetVal = "FlexDialogAlreadyInitialized"
Case 30023,23 : SetupRetVal = "HResultOnly - Passthrough for HRESULT errors"
Case 30024,24 : SetupRetVal = "HWNDNotFound"
Case 30025,25 : SetupRetVal = "IncompatibleCacheAction"
Case 30026,26 : SetupRetVal = "IncompleteProductAddOns"
Case 30027,27 : SetupRetVal = "InstalledProductStateCorrupt"
Case 30028,28 : SetupRetVal = "InsufficientBuffer"
Case 30029,29 : SetupRetVal = "InvalidArgument"
Case 30030,30 : SetupRetVal = "InvalidCDKey"
Case 30031,31 : SetupRetVal = "InvalidColumnType"
Case 30032,31 : SetupRetVal = "InvalidConfigAddLanguage"
Case 30033,33 : SetupRetVal = "InvalidData"
Case 30034,34 : SetupRetVal = "InvalidDirectory"
Case 30035,35 : SetupRetVal = "InvalidFormat"
Case 30036,36 : SetupRetVal = "InvalidInitialization"
Case 30037,37 : SetupRetVal = "InvalidMethod"
Case 30038,38 : SetupRetVal = "InvalidOperation"
Case 30039,39 : SetupRetVal = "InvalidParameter"
Case 30040,40 : SetupRetVal = "InvalidProductFromARP"
Case 30041,41 : SetupRetVal = "InvalidProductInConfigXml"
Case 30042,42 : SetupRetVal = "InvalidReference"
Case 30043,43 : SetupRetVal = "InvalidRegistryValueType"
Case 30044,44 : SetupRetVal = "InvalidXMLProperty"
Case 30045,45 : SetupRetVal = "InvalidMetadataFile"
Case 30046,46 : SetupRetVal = "LogNotInitialized"
Case 30047,47 : SetupRetVal = "LogAlreadyInitialized"
Case 30048,48 : SetupRetVal = "MissingXMLNode"
Case 30049,49 : SetupRetVal = "MsiTableNotFound"
Case 30050,50 : SetupRetVal = "MsiAPICallFailure"
Case 30051,51 : SetupRetVal = "NodeNotOfTypeElement"
Case 30052,52 : SetupRetVal = "NoMoreGraceBoots"
Case 30053,53 : SetupRetVal = "NoProductsFound"
Case 30054,54 : SetupRetVal = "NoSupportedCulture"
Case 30055,55 : SetupRetVal = "NotYetImplemented"
Case 30056,56 : SetupRetVal = "NotAvailableCulture"
Case 30057,57 : SetupRetVal = "NotCustomizationPatch"
Case 30058,58 : SetupRetVal = "NullReference"
Case 30059,59 : SetupRetVal = "OCTPatchForbidden"
Case 30060,60 : SetupRetVal = "OCTWrongMSIDll"
Case 30061,61 : SetupRetVal = "OutOfBoundsIndex"
Case 30062,62 : SetupRetVal = "OutOfDiskSpace"
Case 30063,63 : SetupRetVal = "OutOfMemory"
Case 30064,64 : SetupRetVal = "OutOfRange"
Case 30065,65 : SetupRetVal = "PatchApplicationFailure"
Case 30066,66 : SetupRetVal = "PreReqCheckFailure"
Case 30067,67 : SetupRetVal = "ProcessAlreadyStarted"
Case 30068,68 : SetupRetVal = "ProcessNotStarted"
Case 30069,69 : SetupRetVal = "ProcessNotFinished"
Case 30070,70 : SetupRetVal = "ProductAlreadyDefined"
Case 30071,71 : SetupRetVal = "ResourceAlreadyTracked"
Case 30072,72 : SetupRetVal = "ResourceNotFound"
Case 30073,73 : SetupRetVal = "ResourceNotTracked"
Case 30074,74 : SetupRetVal = "SQLAlreadyConnected"
Case 30075,75 : SetupRetVal = "SQLFailedToAllocateHandle"
Case 30076,76 : SetupRetVal = "SQLFailedToConnect"
Case 30077,77 : SetupRetVal = "SQLFailedToExecuteStatement"
Case 30078,78 : SetupRetVal = "SQLFailedToRetrieveData"
Case 30079,79 : SetupRetVal = "SQLFailedToSetAttribute"
Case 30080,80 : SetupRetVal = "StorageNotCreated"
Case 30081,81 : SetupRetVal = "StreamNameTooLong"
Case 30082,82 : SetupRetVal = "SystemError"
Case 30083,83 : SetupRetVal = "ThreadAlreadyStarted"
Case 30084,84 : SetupRetVal = "ThreadNotStarted"
Case 30085,85 : SetupRetVal = "ThreadNotFinished"
Case 30086,86 : SetupRetVal = "TooManyProducts"
Case 30087,87 : SetupRetVal = "UnexpectedXMLNodeType"
Case 30088,88 : SetupRetVal = "UnexpectedError"
Case 30089,89 : SetupRetVal = "Unitialized"
Case 30090,90 : SetupRetVal = "UserCancel"
Case 30091,91 : SetupRetVal = "ExternalCommandFailed"
Case 30092,92 : SetupRetVal = "SPDatabaseOverSize"
Case 30093,93 : SetupRetVal = "IntegerTruncation"
'msiexec return values
Case 1259 : SetupRetVal = "APPHELP_BLOCK"
Case 1601 : SetupRetVal = "INSTALL_SERVICE_FAILURE"
Case 1602 : SetupRetVal = "INSTALL_USEREXIT"
Case 1603 : SetupRetVal = "INSTALL_FAILURE"
Case 1604 : SetupRetVal = "INSTALL_SUSPEND"
Case 1605 : SetupRetVal = "UNKNOWN_PRODUCT"
Case 1606 : SetupRetVal = "UNKNOWN_FEATURE"
Case 1607 : SetupRetVal = "UNKNOWN_COMPONENT"
Case 1608 : SetupRetVal = "UNKNOWN_PROPERTY"
Case 1609 : SetupRetVal = "INVALID_HANDLE_STATE"
Case 1610 : SetupRetVal = "BAD_CONFIGURATION"
Case 1611 : SetupRetVal = "INDEX_ABSENT"
Case 1612 : SetupRetVal = "INSTALL_SOURCE_ABSENT"
Case 1613 : SetupRetVal = "INSTALL_PACKAGE_VERSION"
Case 1614 : SetupRetVal = "PRODUCT_UNINSTALLED"
Case 1615 : SetupRetVal = "BAD_QUERY_SYNTAX"
Case 1616 : SetupRetVal = "INVALID_FIELD"
Case 1618 : SetupRetVal = "INSTALL_ALREADY_RUNNING"
Case 1619 : SetupRetVal = "INSTALL_PACKAGE_OPEN_FAILED"
Case 1620 : SetupRetVal = "INSTALL_PACKAGE_INVALID"
Case 1621 : SetupRetVal = "INSTALL_UI_FAILURE"
Case 1622 : SetupRetVal = "INSTALL_LOG_FAILURE"
Case 1623 : SetupRetVal = "INSTALL_LANGUAGE_UNSUPPORTED"
Case 1624 : SetupRetVal = "INSTALL_TRANSFORM_FAILURE"
Case 1625 : SetupRetVal = "INSTALL_PACKAGE_REJECTED"
Case 1626 : SetupRetVal = "FUNCTION_NOT_CALLED"
Case 1627 : SetupRetVal = "FUNCTION_FAILED"
Case 1628 : SetupRetVal = "INVALID_TABLE"
Case 1629 : SetupRetVal = "DATATYPE_MISMATCH"
Case 1630 : SetupRetVal = "UNSUPPORTED_TYPE"
Case 1631 : SetupRetVal = "CREATE_FAILED"
Case 1632 : SetupRetVal = "INSTALL_TEMP_UNWRITABLE"
Case 1633 : SetupRetVal = "INSTALL_PLATFORM_UNSUPPORTED"
Case 1634 : SetupRetVal = "INSTALL_NOTUSED"
Case 1635 : SetupRetVal = "PATCH_PACKAGE_OPEN_FAILED"
Case 1636 : SetupRetVal = "PATCH_PACKAGE_INVALID"
Case 1637 : SetupRetVal = "PATCH_PACKAGE_UNSUPPORTED"
Case 1638 : SetupRetVal = "PRODUCT_VERSION"
Case 1639 : SetupRetVal = "INVALID_COMMAND_LINE"
Case 1640 : SetupRetVal = "INSTALL_REMOTE_DISALLOWED"
Case 1641 : SetupRetVal = "SUCCESS_REBOOT_INITIATED"
Case 1642 : SetupRetVal = "PATCH_TARGET_NOT_FOUND"
Case 1643 : SetupRetVal = "PATCH_PACKAGE_REJECTED"
Case 1644 : SetupRetVal = "INSTALL_TRANSFORM_REJECTED"
Case 1645 : SetupRetVal = "INSTALL_REMOTE_PROHIBITED"
Case 1646 : SetupRetVal = "PATCH_REMOVAL_UNSUPPORTED"
Case 1647 : SetupRetVal = "UNKNOWN_PATCH"
Case 1648 : SetupRetVal = "PATCH_NO_SEQUENCE"
Case 1649 : SetupRetVal = "PATCH_REMOVAL_DISALLOWED"
Case 1650 : SetupRetVal = "INVALID_PATCH_XML"
Case 3010 : SetupRetVal = "SUCCESS_REBOOT_REQUIRED"
Case Else : SetupRetVal = "Unknown Return Value"
End Select
End Function 'SetupRetVal
'=======================================================================================================
Function GetProductID(sProdID)
Dim sReturn
Select Case sProdId
Case "000F" : sReturn = "MONDO"
Case "0010" : sReturn = "WEBFLDRS"
Case "0011" : sReturn = "PROPLUS"
Case "0012" : sReturn = "STANDARD"
Case "0013" : sReturn = "BASIC"
Case "0014" : sReturn = "PRO"
Case "0015" : sReturn = "ACCESS"
Case "0016" : sReturn = "EXCEL"
Case "0017" : sReturn = "SharePointDesigner"
Case "0018" : sReturn = "PowerPoint"
Case "0019" : sReturn = "Publisher"
Case "001A" : sReturn = "Outlook"
Case "001B" : sReturn = "Word"
Case "001C" : sReturn = "AccessRuntime"
Case "001F" : sReturn = "Proof"
Case "0020" : sReturn = "O2007CNV"
Case "0021" : sReturn = "VisualWebDeveloper"
Case "0026" : sReturn = "ExpressionWeb"
Case "0029" : sReturn = "Excel"
Case "002A" : sReturn = "Office64"
Case "002B" : sReturn = "Word"
Case "002C" : sReturn = "Proofing"
Case "002E" : sReturn = "Ultimate"
Case "002F" : sReturn = "HomeAndStudent"
Case "0028" : sReturn = "IME"
Case "0030" : sReturn = "Enterprise"
Case "0031" : sReturn = "ProfessionalHybrid"
Case "0033" : sReturn = "Personal"
Case "0035" : sReturn = "ProfessionalHybrid"
Case "0037" : sReturn = "PowerPoint"
Case "003A" : sReturn = "PrjStd"
Case "003B" : sReturn = "PrjPro"
Case "003D" : sReturn = "SINGLEIMAGE"
Case "0043" : sReturn = "OFFICE32"
Case "0044" : sReturn = "InfoPath"
Case "0045" : sReturn = "XWEB"
Case "0048" : sReturn = "OLC"
Case "0049" : sReturn = "ACADEMIC"
Case "004A" : sReturn = "OWC11"
Case "0051" : sReturn = "VISPRO"
Case "0052" : sReturn = "VisView"
Case "0053" : sReturn = "VisStd"
Case "0054" : sReturn = "VisMUI"
Case "0055" : sReturn = "VisMUI"
Case "0057" : sReturn = "VISIO"
Case "0061" : sReturn = "CLICK2RUN"
Case "0062" : sReturn = "CLICK2RUN"
Case "0066" : sReturn = "CLICK2RUN"
Case "006C" : sReturn = "CLICK2RUN"
Case "006D" : sReturn = "CLICK2RUN"
Case "006E" : sReturn = "Shared"
Case "006F" : sReturn = "OFFICE"
Case "0074" : sReturn = "STARTER"
Case "007C" : sReturn = "OLC" 'Outlook Connector
Case "007C" : sReturn = "OSCFB" 'Outlook Social Connector for FaceBook
Case "007D" : sReturn = "OSCWL" 'Outlook Social Connector for Windows Live Messenger
Case "008A" : sReturn = "RecentDocs"
Case "008B" : sReturn = "SmallBusinessBasics"
Case "00A1" : sReturn = "ONENOTE"
Case "00A3" : sReturn = "OneNoteHomeStudent"
Case "00A7" : sReturn = "CPAO"
Case "00A9" : sReturn = "InterConnect"
Case "00AF" : sReturn = "PPtView"
Case "00B0" : sReturn = "ExPdf"
Case "00B1" : sReturn = "ExXps"
Case "00B2" : sReturn = "ExPdfXps"
Case "00B4" : sReturn = "PrjMUI"
Case "00B5" : sReturn = "PrjtMUI"
Case "00B9" : sReturn = "AER"
Case "00BA" : sReturn = "Groove"
Case "00CA" : sReturn = "SmallBusiness"
Case "00E0" : sReturn = "Outlook"
Case "00D1" : sReturn = "ACE"
Case "0100" : sReturn = "OfficeMUI"
Case "0101" : sReturn = "OfficeXMUI"
Case "0103" : sReturn = "PTK"
Case "0114" : sReturn = "GrooveSetupMetadata"
Case "0115" : sReturn = "SharedSetupMetadata"
Case "0116" : sReturn = "SharedSetupMetadata"
Case "0117" : sReturn = "AccessSetupMetadata"
Case "011A" : sReturn = "SendASmile"
Case "011D" : sReturn = "ProPlusSubscription"
Case "011F" : sReturn = "OLConnect"
Case "1014" : sReturn = "STS"
Case "1015" : sReturn = "WSSMUI"
Case "1032" : sReturn = "PJSVRAPP"
Case "104B" : sReturn = "SPS"
Case "104E" : sReturn = "SPSMUI"
Case "107F" : sReturn = "OSrv"
Case "1080" : sReturn = "OSrv"
Case "1088" : sReturn = "lpsrvwfe"
Case "10D7" : sReturn = "IFS"
Case "10D8" : sReturn = "IFSMUI"
Case "10EB" : sReturn = "DLCAPP"
Case "10F5" : sReturn = "XLSRVAPP"
Case "10F6" : sReturn = "XlSrvWFE"
Case "10F7" : sReturn = "DLC"
Case "10F8" : sReturn = "SlSrvMui"
Case "10FB" : sReturn = "OSrchWFE"
Case "10FC" : sReturn = "OSRCHAPP"
Case "10FD" : sReturn = "OSrchMUI"
Case "1103" : sReturn = "DLC"
Case "1104" : sReturn = "LHPSRV"
Case "1105" : sReturn = "PIA"
Case "1106" : sReturn = "GRVMGMTSRV"
Case "1109" : sReturn = "GSERVERRELAY"
Case "110D" : sReturn = "OSERVER"
Case "110F" : sReturn = "PSERVER"
Case "1110" : sReturn = "WSS"
Case "1121" : sReturn = "SPSSDK"
Case "1122" : sReturn = "SPSDev"
Case Else : sReturn = sProdID
End Select 'sProdId
GetProductID = sReturn
End Function 'GetProductID
'=======================================================================================================
Sub Log (sLog)
wscript.echo sLog
LogStream.WriteLine sLog
End Sub 'Log
'=======================================================================================================
Sub LogOnly (sLog)
LogStream.WriteLine sLog
End Sub 'Log
'=======================================================================================================
Sub CheckError(sModule)
If Err <> 0 Then
LogOnly " " & Now & " - " & sModule & " - Source: " & Err.Source & "; Err# (Hex): " & Hex( Err ) & _
"; Err# (Dec): " & Err & "; Description : " & Err.Description
End If 'Err = 0
Err.Clear
End Sub
'=======================================================================================================
'Command line parser
Sub ParseCmdLine
Dim iCnt, iArgCnt
Dim arrArguments
Dim sArg0
iArgCnt = Wscript.Arguments.Count
If iArgCnt > 0 Then
If wscript.Arguments(0) = "UAC" Then
If wscript.arguments.count = 1 Then iArgCnt = 0
End If
End If
If iArgCnt = 0 Then
Select Case UCase(wscript.ScriptName)
Case Else
'Create the log
CreateLog
Log "No argument specified. Preparing user prompt" & vbCrLf
FindInstalledOProducts
If dicInstalledSku.Count > 0 Then sDefault = Join(RemoveDuplicates(dicInstalledSku.Items),",") Else sDefault = "CLIENTALL"
sDefault = InputBox("Enter a list of " & ONAME & " products to remove" & vbCrLf & vbCrLf & _
"Examples:" & vbCrLf & _
"CLIENTALL" & vbTab & "-> all Client products" & vbCrLf & _
"SERVER" & vbTab & "-> all Server products" & vbCrLf & _
"ALL" & vbTab & vbTab & "-> all Server & Client products" & vbCrLf & _
"ProPlus,PrjPro" & vbTab & "-> ProPlus and Project" & vbCrLf &_
"?" & vbTab & vbTab & "-> display Help", _
SCRIPTFILE & " - " & ONAME & " remover", _
sDefault)
If IsEmpty(sDefault) Then 'User cancelled
Log "User cancelled. CleanUp & Exit."
'Undo temporary entries created in ARP
TmpKeyCleanUp
SetError ERROR_USERCANCEL
SetRetVal iError
wscript.quit iError
End If 'IsEmpty(sDefault)
Log "Answer from prompt: " & sDefault & vbCrLf
sDefault = Trim(UCase(Trim(Replace(sDefault,Chr(34),""))))
arrArguments = Split(Trim(sDefault)," ")
If UBound(arrArguments) = -1 Then ReDim arrArguments(0)
End Select
Else
ReDim arrArguments(iArgCnt-1)
For iCnt = 0 To (iArgCnt-1)
arrArguments(iCnt) = UCase(Wscript.Arguments(iCnt))
Next 'iCnt
End If 'iArgCnt = 0
'Handle the SKU list
sArg0 = Replace(arrArguments(0),"/","")
sArg0 = Replace(sArg0,"-","")
Select Case UCase(sArg0)
Case "?"
ShowSyntax
Case "ALL"
fRemoveAll = True
fRemoveOse = False
Case "CLIENTSUITES"
fRemoveCSuites = True
fRemoveOse = False
Case "CLIENTSTANDALONE"
fRemoveCSingle = True
fRemoveOse = False
Case "CLIENTALL"
fRemoveCSuites = True
fRemoveCSingle = True
fRemoveOse = False
Case "SERVER"
fRemoveSrv = True
fRemoveOse = False
Case "ALL,OSE"
fRemoveAll = True
fRemoveOse = True
Case Else
fRemoveAll = False
fRemoveOse = False
sSkuRemoveList = sArg0
End Select
For iCnt = 0 To UBound(arrArguments)
Select Case arrArguments(iCnt)
Case "?","/?","-?"
ShowSyntax
Case "/B","/BYPASS"
If UBound(arrArguments)>iCnt Then
If InStr(arrArguments(iCnt+1),"1")>0 Then fBypass_Stage1 = True
If InStr(arrArguments(iCnt+1),"2")>0 Then fBypass_Stage2 = True
If InStr(arrArguments(iCnt+1),"3")>0 Then fBypass_Stage3 = True
If InStr(arrArguments(iCnt+1),"4")>0 Then fBypass_Stage4 = True
End If
Case "/D","/DELETEUSERSETTINGS"
fKeepUser = False
Case "/FR","/FASTREMOVE"
fBypass_Stage1 = True
fSkipSD = True
Case "/F","/FORCE"
fForce = True
Case "/K","/KEEPUSERSETTINGS"
fKeepUser = True
Case "/L","/LOG"
fLogInitialized = False
If UBound(arrArguments)>iCnt Then
If oFso.FolderExists(arrArguments(iCnt+1)) Then
sLogDir = arrArguments(iCnt+1)
Else
On Error Resume Next
oFso.CreateFolder(arrArguments(iCnt+1))
If Err <> 0 Then sLogDir = sScrubDir Else sLogDir = arrArguments(iCnt+1)
End If
End If
Case "/N","/NOCANCEL"
fNoCancel = True
Case "/O","/OSE"
fRemoveOse = True
Case "/P","/PREVIEW","/DETECTONLY"
fDetectOnly = True
Case "/Q","/QUIET"
fQuiet = True
Case "/QND"
fBypass_Stage1 = True
fBypass_Stage2 = True
fBypass_Stage3 = True
fRemoveOse = True
fRemoveOspp = True
fRemoveC2R = True
fRemoveAll = True
fSkipSD = True
fForce = True
Case "/S","/SKIPSD","/SKIPSHORTCUSTDETECTION"
fSkipSD = True
Case "/R","/RECONCILE"
fTryReconcile = True
Case Else
End Select
Next 'iCnt
If Not fLogInitialized Then CreateLog
End Sub 'ParseCmdLine
'=======================================================================================================
Sub CreateLog
Dim DateTime
Dim sLogName
On Error Resume Next
'Create the log file
Set DateTime = CreateObject("WbemScripting.SWbemDateTime")
DateTime.SetVarDate Now,True
sLogName = sLogDir & "\" & oWShell.ExpandEnvironmentStrings("%COMPUTERNAME%")
sLogName = sLogName & "_" & Left(DateTime.Value,14)
sLogName = sLogName & "_ScrubLog.txt"
Err.Clear
Set LogStream = oFso.CreateTextFile(sLogName,True,True)
If Err <> 0 Then
Err.Clear
sLogDir = sScrubDir
sLogName = sLogDir & "\" & oWShell.ExpandEnvironmentStrings("%COMPUTERNAME%")
sLogName = sLogName & "_" & Left(DateTime.Value,14)
sLogName = sLogName & "_ScrubLog.txt"
Set LogStream = oFso.CreateTextFile(sLogName,True,True)
End If
Log "Microsoft Customer Support Services - " & ONAME & " Removal Utility" & vbCrLf & vbCrLf & _
"Version: " & SCRIPTVERSION & vbCrLf & _
"64 bit OS: " & f64 & vbCrLf & _
"Start removal: " & Now & vbCrLf
fLogInitialized = True
End Sub 'CreateLog
'=======================================================================================================
Sub RelaunchAsCScript
Dim Argument
Dim sCmdLine
SetError ERROR_RELAUNCH
sCmdLine = "cmd.exe /k " & WScript.Path & "\cscript.exe //NOLOGO " & Chr(34) & WScript.scriptFullName & Chr(34)
If Wscript.Arguments.Count > 0 Then
For Each Argument in Wscript.Arguments
sCmdLine = sCmdLine & " " & chr(34) & Argument & chr(34)
Next 'Argument
End If
Wscript.Quit CLng(oWShell.Run(sCmdLine,1,True))
End Sub 'RelaunchAsCScript
'=======================================================================================================
Sub RelaunchElevated
Dim Argument,Process,Processes
Dim iParentProcessId,iSpawnedProcessId
Dim sCmdLine,sRetValFile
Dim oShell
SetError ERROR_RELAUNCH
' Shell object for relaunch
Set oShell = CreateObject("Shell.Application")
' build command line for relaunch
sCmdLine = Chr(34) & WScript.scriptFullName & Chr(34)
If Wscript.Arguments.Count > 0 Then
For Each Argument in Wscript.Arguments
Select Case UCase(Argument)
Case "/Q","/QUIET"
' Don't try to relaunch in quiet mode
Exit Sub
SetError ERROR_ELEVATION_FAILED
Case "UAC"
'Already tried elevated relaunch
SetError ERROR_ELEVATION_FAILED
Exit Sub
Case Else
sCmdLine = sCmdLine & " " & chr(34) & Argument & chr(34)
End Select
Next 'Argument
End If
' prep work to get the return value from the elevated process
iParentProcessId = GetMyProcessId
' launch the elevated instance
oShell.ShellExecute "cscript.exe", sCmdLine & " UAC", "", "runas", 1
' get the process id of the spawned instance
WScript.Sleep 500
Set Processes = oWmiLocal.ExecQuery("Select * From Win32_Process WHERE ParentProcessId='" & iParentProcessId & "'")
If Processes.Count > 0 Then
For Each Process in Processes
iSpawnedProcessId = Process.ProcessId
Exit For
Next 'Process
' monitor the tasklist to detect the end of the spawned process
While oWmiLocal.ExecQuery("Select * From Win32_Process WHERE ProcessId='" & iSpawnedProcessId & "'").Count > 0
WScript.Sleep 3000
Wend
' get the return value from the file
Wscript.Quit GetRetValFromFile
End If
' elevation failed (user declined)
SetError ERROR_ELEVATION_USERDECLINED
End Sub 'RelaunchElevated
'=======================================================================================================
'Show the expected syntax for the script usage
Sub ShowSyntax
TmpKeyCleanUp
Wscript.Echo sErr & vbCrLf & _
SCRIPTFILE & " V " & SCRIPTVERSION & vbCrLf & _
"Copyright (c) Microsoft Corporation. All Rights Reserved" & vbCrLf & vbCrLf & _
SCRIPTFILE & " helps to remove " & ONAME & " Server & Client products" & vbCrLf & _
"when a regular uninstall is no longer possible" & vbCrLf & vbCrLf & _
"Usage:" & vbTab & SCRIPTFILE & " [List of config ProductIDs] [Options]" & vbCrLf & vbCrLf & _
vbTab & "/? ' Displays this help"& vbCrLf &_
vbTab & "/Force ' Enforces file removal. May cause data loss!" & vbCrLf &_
vbTab & "/SkipShortcutDetection ' Does not search the local hard drives for shortcuts" & vbCrLf & _
vbTab & "/Log [LogfolderPath] ' Custom folder for log files" & vbCrLf & _
vbTab & "/NoCancel ' Setup.exe and Msiexec.exe have no Cancel button" & vbCrLf &_
vbTab & "/OSE ' Forces removal of the Office Source Engine service" & vbCrLf &_
vbTab & "/Quiet ' Setup.exe and Msiexec.exe run quiet with no UI" & vbCrLf &_
vbTab & "/Preview ' Run this script to preview what would get removed"& vbCrLf & vbCrLf & _
"Examples:"& vbCrLf & _
vbTab & SCRIPTFILE & " CLIENTALL ' Remove all " & ONAME & " Client products" & vbCrLf &_
vbTab & SCRIPTFILE & " SERVER ' Remove all " & ONAME & " Server products" & vbCrLf &_
vbTab & SCRIPTFILE & " ALL ' Remove all " & ONAME & " Server & Client products" & vbCrLf &_
vbTab & SCRIPTFILE & " ProPlus,PrjPro ' Remove ProPlus and Project" & vbCrLf
Wscript.Quit
End Sub 'ShowSyntax
'=======================================================================================================
'=======================================================================================================
' Name: OffScrub_O15msi.vbs
' Author: Microsoft Customer Support Services
' Copyright (c) 2011-2015 Microsoft Corporation
' Script to remove (scrub) Office 2013 MSI products
' when a regular uninstall is no longer possible
'=======================================================================================================
Option Explicit
Dim sDefault
'=======================================================================================================
'[INI] Section for script behavior customizations
'Pre-configure the SKU's to remove.
'Only for use without command line parameters
'Example: sDefault = "CLIENTALL"
sDefault = ""
'DO NOT CUSTOMIZE BELOW THIS LINE!
'=======================================================================================================
Const SCRIPTVERSION = "1.99"
Const SCRIPTFILE = "OffScrub_O15msi.vbs"
Const SCRIPTNAME = "OffScrub_O15msi"
Const OVERSION = "15.0"
Const OVERSIONMAJOR = "15"
Const OREF = "Office15"
Const OREGREF = "OFFICE15."
Const ONAME = "Office 2013 MSI"
Const OPACKAGE = "PackageRefs"
Const OFFICEID = "000000FF1CE}"
Const HKCR = &H80000000
Const HKCU = &H80000001
Const HKLM = &H80000002
Const HKU = &H80000003
Const FOR_WRITING = 2
Const PRODLEN = 12
Const COMPPERMANENT = "00000000000000000000000000000000"
Const UNCOMPRESSED = 38
Const SQUISHED = 20
Const COMPRESSED = 32
Const REG_ARP = "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\"
Const VB_YES = 6
Const MSIOPENDATABASEREADONLY = 0
Const LYNC_ALL = "{4A2C120F-307B-4400-B239-F29ADB54D3C6}{5CFD6599-10E5-4CF0-B6E1-BF39D30A64F8}{5CFD6599-10E5-4CF0-B6E1-BF39D30A64F8}{BF3AC8BA-1A0F-42AD-8B65-4250617AF682}{3475BF22-3564-4EF3-A633-C5F3F4582392}{263BA91B-7782-4EEB-A4FC-7BD554CAF1F3}{AA256AE1-6B6A-48E6-9957-B38F92CA614B}{D79732A1-BB17-4789-AE75-69D61261E305}{C7B887F2-07CA-4903-93A2-9B4E16E4EABD}{81BE0B17-563B-45D4-B198-5721E6C665CD}{11298539-8073-4D54-B6A0-88D4FA512E5C}{C192041D-2861-4E02-9F43-4041858A58F1}{7023C711-0E65-471E-8048-12C455968841}{58A013B1-1613-4978-881A-FCA43710C84A}{7FD6C049-9777-4B51-91FF-B19D79ADF439}{D3001D99-675B-44DF-A8EB-A7BB6F864DB7}{0C5EA724-8649-47FA-B505-75B35390378D}{13DE0C92-2AE4-48D0-8CC8-58D5E327BDCB}{E7EC16E6-C220-41C0-9C91-5E7702B8EC86}{1B10C75C-70E1-460E-B07B-D7DFF365D80F}{331977BC-B246-46B4-8829-1D52F41C8C7B}{D8255EF2-0BB2-4AF1-A662-5EBACD179475}{DD069437-C92B-4C1C-A992-14F6C7E12C2C}{E9E30DB3-8D72-43A0-B1B8-A6F8261D20D6}{545B7E32-E254-40E1-8935-91C61E3D02C2}{70409E9E-AFAE-4C05-AE57-F83B89819434}{1D6E3225-753D-41AD-A2C4-68684700F592}{217AA75D-82C0-4C49-9252-A0E6F9661688}{5AB81CD4-7C78-420C-AAAC-855C4BADBDDD}{AA595672-6515-4961-B81F-485F86627C76}{C9F2C38C-21F0-4687-8C7D-51AA02CE8C98}{DD80DED6-700D-4CC5-B2A9-C64A1AD155B9}{88257193-EC61-4152-8AB1-A5FB4BE638D7}{7D9109C3-58A9-4AFD-A1D3-47E7D811726E}{71C6D199-5B8E-41E7-BA36-D99F66E0072E}{1CFE7869-777D-4563-8161-2C75ED95B621}{FE25DDB2-5766-4A9E-86D2-2B709CC8F65D}{621F7793-1C51-45BA-899F-41557946B0E3}{B31017AA-FBF8-4003-8785-EC789C2AE0C2}{11849FBC-C416-4742-8279-17C3A2C85F72}{4F380D4B-A84D-45C7-AF58-59EA2AEDF35A}{81BE0B17-563B-45D4-B198-5721E6C665CD}"
'=======================================================================================================
Dim oFso, oMsi, oReg, oWShell, oWmiLocal, oShellApp
Dim ComputerItem, Item, LogStream, TmpKey
Dim arrTmpSKUs, arrDeleteFiles, arrDeleteFolders, arrMseFolders, arrVersion
Dim dictionaryKeepProd, dictionaryKeepLis, dicApps, dictionaryKeepFolder, dicDelRegKey, dictionaryKeepReg
Dim dicInstalledSku, dicRemoveSku, dictionaryKeepSku, dicSrv, dicCSuite, dicCSingle
Dim f64, fLegacyProductFound, fCScript
Dim sErr, sTmp, sSkuRemoveList, sWinDir, sWICacheDir, sMode
Dim sAppData, sTemp, sScrubDir, sProgramFiles, sProgramFilesX86, sCommonProgramFiles
Dim sAllusersProfile, sOSinfo, sOSVersion, sCommonProgramFilesX86, sProfilesDirectory
Dim sProgramData, sLocalAppData, sOInstallRoot, sScriptDir
Dim iVersionNT
'=======================================================================================================
'Main
'=======================================================================================================
'Configure defaults
Dim sLogDir : sLogDir = ""
Dim sMoveMessage: sMoveMessage = ""
Dim fRemoveOse : fRemoveOse = False
Dim fRemoveOspp : fRemoveOspp = False
Dim fRemoveAll : fRemoveAll = False
Dim fRemoveC2R : fRemoveC2R = False
Dim fRemoveAppV : fRemoveAppV = False
Dim fRemoveCSuites : fRemoveCSuites = False
Dim fRemoveCSingle : fRemoveCSingle = False
Dim fRemoveSrv : fRemoveSrv = False
Dim fRemoveLync : fRemoveLync = False
Dim fKeepUser : fKeepUser = True 'Default to keep per user settings
Dim fSkipSD : fSkipSD = False 'Default to not Skip the Shortcut Detection
Dim fKeepSG : fKeepSG = False 'Default to not override the SoftGrid detection
Dim fDetectOnly : fDetectOnly = False
Dim fQuiet : fQuiet = True
Dim fBasic : fBasic = False
Dim fNoCancel : fNoCancel = False
Dim fPassive : fPassive = True
Dim fNoReboot : fNoReboot = True 'Default to offer reboot prompt if needed
Dim fNoElevate : fNoElevate = False
Dim fElevated : fElevated = False
Dim fTryReconcile : fTryReconcile = False
Dim fC2rInstalled : fC2rInstalled = False
'CAUTION! -> "fForce" will kill running applications which can result in data loss! <- CAUTION
Dim fForce : fForce = True
'CAUTION! -> "fForce" will kill running applications which can result in data loss! <- CAUTION
Dim fLogInitialized : fLogInitialized = False
Dim fBypass_Stage1 : fBypass_Stage1 = True 'Component Detection
Dim fBypass_Stage2 : fBypass_Stage2 = False 'Setup
Dim fBypass_Stage3 : fBypass_Stage3 = False 'Msiexec
Dim fBypass_Stage4 : fBypass_Stage4 = False 'CleanUp
Dim fRebootRequired : fRebootRequired = False
'Create required objects
Set oWmiLocal = GetObject("winmgmts:{(Debug)}\\.\root\cimv2")
Set oWShell = CreateObject("Wscript.Shell")
Set oShellApp = CreateObject("Shell.Application")
Set oFso = CreateObject("Scripting.FileSystemObject")
Set oMsi = CreateObject("WindowsInstaller.Installer")
Set oReg = GetObject("winmgmts:\\.\root\default:StdRegProv")
'Get environment path info
sAppData = oWShell.ExpandEnvironmentStrings("%appdata%")
sLocalAppData = oWShell.ExpandEnvironmentStrings("%localappdata%")
sTemp = oWShell.ExpandEnvironmentStrings("%temp%")
sAllUsersProfile = oWShell.ExpandEnvironmentStrings("%allusersprofile%")
RegReadValue HKLM, "SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList", "ProfilesDirectory", sProfilesDirectory, "REG_EXPAND_SZ"
If NOT oFso.FolderExists(sProfilesDirectory) Then
sProfilesDirectory = oFso.GetParentFolderName(oWShell.ExpandEnvironmentStrings("%userprofile%"))
End If
sProgramFiles = oWShell.ExpandEnvironmentStrings("%programfiles%")
'Deferred until after architecture check
'sProgramFilesX86 = oWShell.ExpandEnvironmentStrings("%programfiles(x86)%")
sCommonProgramFiles = oWShell.ExpandEnvironmentStrings("%commonprogramfiles%")
'Deferred until after architecture check
'sCommonProgramFilesX86 = oWShell.ExpandEnvironmentStrings("%CommonProgramFiles(x86)%")
sProgramData = oWSHell.ExpandEnvironmentStrings("%programdata%")
sWinDir = oWShell.ExpandEnvironmentStrings("%windir%")
sWICacheDir = sWinDir & "\" & "Installer"
sScrubDir = sTemp & "\" & SCRIPTNAME
sScriptDir = wscript.ScriptFullName
sScriptDir = Left(sScriptDir, InStrRev(sScriptDir, "\"))
' Get current script host
fCScript = UCase(Mid(Wscript.FullName, Len(Wscript.Path) + 2, 1)) = "C"
'Detect if we're running on a 64 bit OS
Set ComputerItem = oWmiLocal.ExecQuery("Select * from Win32_ComputerSystem")
For Each Item In ComputerItem
f64 = Instr(Left(Item.SystemType,3),"64") > 0
If f64 Then Exit For
Next
If f64 Then sProgramFilesX86 = oWShell.ExpandEnvironmentStrings("%programfiles(x86)%")
If f64 Then sCommonProgramFilesX86 = oWShell.ExpandEnvironmentStrings("%CommonProgramFiles(x86)%")
'Get OS details and VersionNT
Set ComputerItem = oWmiLocal.ExecQuery("Select * from Win32_OperatingSystem")
For Each Item in ComputerItem
sOSinfo = sOSinfo & Item.Caption
sOSinfo = sOSinfo & Item.OtherTypeDescription
sOSinfo = sOSinfo & ", " & "SP " & Item.ServicePackMajorVersion
sOSinfo = sOSinfo & ", " & "Version: " & Item.Version
sOsVersion = Item.Version
sOSinfo = sOSinfo & ", " & "Codepage: " & Item.CodeSet
sOSinfo = sOSinfo & ", " & "Country Code: " & Item.CountryCode
sOSinfo = sOSinfo & ", " & "Language: " & Item.OSLanguage
Next
'Build the VersionNT number
arrVersion = Split(sOsVersion, Delimiter(sOsVersion))
iVersionNt = CInt(arrVersion (0)) * 100 + CInt(arrVersion (1))
'Check if we're running as 32 bit process on a 64 bit OS
If InStr(LCase(wscript.path), "syswow64") > 0 Then RelaunchAs64Host
fElevated = CheckRegPermissions
If NOT fElevated AND NOT fNoElevate Then
'Try to relaunch elevated
RelaunchElevated
'Can't relaunch. Exit out
If UCase(Mid(Wscript.FullName, Len(Wscript.Path) + 2, 1)) = "C" Then
If Not fLogInitialized Then CreateLog
Log "Insufficient registry access permissions - exiting"
End If
'Undo temporary entries created in ARP
TmpKeyCleanUp
wscript.quit 3
End If
'Create Dictionaries
Set dictionaryKeepProd = CreateObject("Scripting.Dictionary")
Set dicInstalledSku = CreateObject("Scripting.Dictionary")
Set dicRemoveSku = CreateObject("Scripting.Dictionary")
Set dictionaryKeepSku = CreateObject("Scripting.Dictionary")
Set dictionaryKeepLis = CreateObject("Scripting.Dictionary")
Set dictionaryKeepFolder = CreateObject("Scripting.Dictionary")
Set dicApps = CreateObject("Scripting.Dictionary")
Set dicDelRegKey = CreateObject("Scripting.Dictionary")
Set dictionaryKeepReg = CreateObject("Scripting.Dictionary")
Set dicSrv = CreateObject("Scripting.Dictionary")
Set dicCSuite = CreateObject("Scripting.Dictionary")
Set dicCSingle = CreateObject("Scripting.Dictionary")
'Create the temp folder
If Not oFso.FolderExists(sScrubDir) Then oFso.CreateFolder sScrubDir
'Set the default logging directory
sLogDir = sScrubDir
'Call the command line parser
ParseCmdLine
'Ensure CScript as engine
If NOT fCScript AND NOT fQuiet Then RelaunchAsCScript
'Get Office Install Folder
If NOT RegReadValue(HKLM,"SOFTWARE\Microsoft\Office\"&OVERSION&"\Common\InstallRoot","Path",sOInstallRoot,"REG_SZ") Then
sOInstallRoot = sProgramFiles & "\Microsoft Office\"&OREF
End If
'Ensure integrity of WI metadata which could fail used APIs otherwise
EnsureValidWIMetadata HKCU,"Software\Classes\Installer\Products",COMPRESSED
EnsureValidWIMetadata HKCR,"Installer\Products",COMPRESSED
EnsureValidWIMetadata HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products",COMPRESSED
EnsureValidWIMetadata HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components",COMPRESSED
EnsureValidWIMetadata HKCR,"Installer\Components",COMPRESSED
'Add initial known .exe files that might need to be closed
dicApps.Add "communicator.exe", "communicator.exe"
dicApps.Add "setup.exe", "setup.exe"
Select Case OVERSIONMAJOR
Case "12"
Case "14"
dicApps.Add "bcssync.exe","bcssync.exe"
dicApps.Add "officesas.exe","officesas.exe"
dicApps.Add "officesasscheduler.exe","officesasscheduler.exe"
dicApps.Add "msosync.exe","msosync.exe"
dicApps.Add "onenotem.exe","onenotem.exe"
Case "15"
Case Else
End Select
'-------------------
'Stage # 0 - Basics |
'-------------------
'Build a list with installed/registered Office products
sTmp = "Stage # 0 " & chr(34) & "Basics" & chr(34) & " (" & Time & ")"
Log vbCrLf & sTmp & vbCrLf & String(Len(sTmp),"=") & vbCrLf
FindInstalledOProducts
If dicInstalledSku.Count > 0 Then Log "Found registered product(s): " & Join(RemoveDuplicates(dicInstalledSku.Items),",") &vbCrLf
'Validate the list of products we got from the command line if applicable
ValidateRemoveSkuList
'Log detection results
If dicRemoveSku.Count > 0 Then Log "Product(s) to be removed: " & Join(RemoveDuplicates(dicRemoveSku.Items),",")
sMode = "Selected " & ONAME & " products"
If NOT dicRemoveSku.Count > 0 Then sMode = "Orphaned " & ONAME & " products"
If fRemoveAll Then sMode = "All " & ONAME & " products"
Log "Final removal mode: " & sMode
Log "Remove OSE service: " & fRemoveOse &vbCrLf
'Log preview mode if applicable
If fDetectOnly Then Log "*************************************************************************"
If fDetectOnly Then Log "* PREVIEW MODE *"
If fDetectOnly Then Log "* All uninstall and delete operations will only be logged not executed! *"
If fDetectOnly Then Log "*************************************************************************" & vbCrLf
'Check if there are legacy products installed
CheckForLegacyProducts
If fLegacyProductFound Then Log "Found legacy Office products that will not be removed." Else Log "No legacy Office products found."
'Cache .msi files
If dicRemoveSku.Count > 0 Then CacheMsiFiles
'Log Sku/Prod detection results
LogSkuResults
'UnPin Shortcuts
If NOT fSkipSD AND dicRemoveSku.Count > 0 Then
On Error Resume Next
Log " Searching for pinned shortcuts"
CleanShortcuts sAllUsersProfile, False, True
CleanShortcuts sProfilesDirectory, False, True
On Error Goto 0
End If 'NOT SkipSD
'--------------------------------
'Stage # 1 - Component Detection |
'--------------------------------
sTmp = "Stage # 1 " & chr(34) & "Component Detection" & chr(34) & " (" & Time & ")"
Log vbCrLf & sTmp & vbCrLf & String(Len(sTmp),"=") & vbCrLf
If Not fBypass_Stage1 OR fForce Then
'Build a list with files which are installed/registered to a product that's going to be removed
Log "Prepare for CleanUp stages."
Log "Identifying removable elements. This can take several minutes."
ScanComponents
Else
Log "Not running Component Detection in default removal."
End If
'End all running Office applications
If fForce OR fQuiet OR fPassive Then CloseOfficeApps
'----------------------
'Stage # 2 - Setup.exe |
'----------------------
sTmp = "Stage # 2 " & chr(34) & "Setup.exe" & chr(34) & " (" & Time & ")"
Log vbCrLf & sTmp & vbCrLf & String(Len(sTmp),"=") & vbCrLf
If Not fBypass_Stage2 Then
SetupExeRemoval
Else
Log "Skipping Setup.exe because bypass was requested."
End If
'------------------------
'Stage # 3 - Msiexec.exe |
'------------------------
sTmp = "Stage # 3 " & chr(34) & "Msiexec.exe" & chr(34) & " (" & Time & ")"
Log vbCrLf & sTmp & vbCrLf & String(Len(sTmp),"=") & vbCrLf
If Not fBypass_Stage3 Then
MsiexecRemoval
Else
Log "Skipping Msiexec.exe because bypass was requested."
End If
'--------------------
'Stage # 4 - CleanUp |
'--------------------
'Removal of files and registry settings
sTmp = "Stage # 4 " & chr(34) & "CleanUp" & chr(34) & " (" & Time & ")"
Log vbCrLf & sTmp & vbCrLf & String(Len(sTmp),"=") & vbCrLf
If Not fBypass_Stage4 Then
'Office Source Engine
If fRemoveOse Then RemoveOSE
'Local Installation Source (MSOCache)
WipeLIS
'Obsolete files
If fRemoveAll Then
FileWipeAll
Else
FileWipeIndividual
End If
'Empty Folders
DeleteEmptyFolders
'Restore Explorer if needed
If fForce OR fQuiet OR fPassive Then RestoreExplorer
'Registry data
RegWipe
'Wipe orphaned files from Windows Installer cache
MsiClearOrphanedFiles
'Temporary .msi files in scrubcache
DeleteMsiScrubCache
'Temporary files
DelScrubTmp
Else
Log "Skipping CleanUp because bypass was requested."
End If
If Not sMoveMessage = "" Then Log vbCrLf & "Please remove this folder after next reboot: " & sMoveMessage
'THE END
Log vbCrLf & "End removal: " & Now & vbCrLf
Log vbCrLf & "For detailed logging please refer to the log in folder " &chr(34)&sScrubDir&chr(34)&vbCrLf
If fRebootRequired Then
Log vbCrLf & "A restart is required to complete the operation!"
If NOT (fQuiet OR fPassive OR fNoReboot) Then
If MsgBox("Do you want to reboot now?",vbYesNo,"Reboot Required") = VB_YES Then
Dim colOS, oOS
Dim oWmiReboot
Set oWmiReboot = GetObject("winmgmts:{impersonationLevel=impersonate,(Shutdown)}!\\.\root\cimv2")
Set colOS = oWmiReboot.ExecQuery ("Select * from Win32_OperatingSystem")
For Each oOS in colOS
oOS.Reboot()
Next
End If
End If
End If
If NOT (fQuiet OR fPassive) Then
For Each Item in Wscript.Arguments
If Item = "UAC" Then
If fCscript Then wscript.stdout.write "Press <Enter> to close this window"
sTemp = wscript.stdin.read(1)
End If
Next 'Argument
End If
'=======================================================================================================
'=======================================================================================================
'Stage 0 - 4 Subroutines
'=======================================================================================================
'Office configuration products are listed with their configuration product name in the "Uninstall" key
'To identify an Office configuration product all of these condiditions have to be met:
' - "SystemComponent" does not have a value of "1" (DWORD)
' - "OPACKAGE" (see constant declaration) entry exists and is not empty
' - "DisplayVersion" exists and the 2 leftmost digits are "OVERSIONMAJOR"
Sub FindInstalledOProducts
Dim ArpItem, File
Dim sCurKey, sValue, sConfigName, sProdC, sCVHValue
Dim sProductCodeList, sProductCode
Dim arrKeys, arrMultiSzValues
Dim fSystemComponent0, fPackages, fDisplayVersion, fReturn, fCategorized
If dicInstalledSku.Count > 0 Then Exit Sub 'Already done from InputBox prompt
'Handle orphaned products to get them added to the detection scope
If fTryReconcile Then
For Each File in oFso.GetFolder(sWICacheDir).Files
If Len(File.Name)>3 Then
Select Case LCase(Right(File.Name, 4))
Case ".msi"
sProductCode = ""
sProductCode = GetMsiProductCode(File.Path)
If InScope(sProductCode) Then
If NOT RegKeyExists(HKLM, REG_ARP & sProductCode) Then
'Ensure the orphaned item is getting removed
If Len(sSkuRemoveList) > 0 Then
sSkuRemoveList = sSkuRemoveList & "," & GetProductID(Mid(sProductCode, 11, 4))
Else
sSkuRemoveList = GetProductID(Mid(sProductCode, 11, 4))
End If
'Add to ScrubDir
oFso.CopyFile File.Path,sScrubDir & "\" & sProductCode & ".msi",True
'Register the product with MSI
MsiRegisterProduct File.Path
End If 'NOT sProductCode
End If 'InScope
Case Else
End Select
End If '>3
Next 'File
End If 'fTryReconcile
'Locate standalone Office products that have no configuration product entry and create a
'temporary configuration entry
ReDim arrTmpSKUs(-1)
If RegEnumKey(HKLM, REG_ARP, arrKeys) Then
For Each ArpItem in arrKeys
If InScope(ArpItem) Then
sCurKey = REG_ARP & ArpItem & "\"
fSystemComponent0 = Not (RegReadValue(HKLM, sCurKey, "SystemComponent", sValue, "REG_DWORD") AND (sValue = "1"))
If fSystemComponent0 Then
RegReadValue HKLM, sCurKey, "DisplayVersion", sValue, "REG_SZ"
Redim arrMultiSzValues(0)
'Logic changed to drop the LCID identifier
'sConfigName = GetProductID(Mid(ArpItem,11,4)) & "_" & CInt("&h" & Mid(ArpItem,16,4))
sConfigName = OREGREF & GetProductID(Mid(ArpItem, 11, 4))
If NOT RegKeyExists(HKLM, REG_ARP&sConfigName) Then
'Create a new ARP item
ReDim Preserve arrTmpSKUs(UBound(arrTmpSKUs) + 1)
arrTmpSKUs(UBound(arrTmpSKUs)) = sConfigName
oReg.CreateKey HKLM, REG_ARP & sConfigName
arrMultiSzValues(0) = sConfigName
oReg.SetMultiStringValue HKLM, REG_ARP & sConfigName, OPACKAGE, arrMultiSzValues
arrMultiSzValues(0) = ArpItem
oReg.SetStringValue HKLM, REG_ARP & sConfigName, "Comment", "Temporary OffScrub generated key. Please delete this key!"
oReg.SetMultiStringValue HKLM, REG_ARP & sConfigName, "ProductCodes", arrMultiSzValues
oReg.SetStringValue HKLM, REG_ARP & sConfigName, "DisplayVersion", sValue
oReg.SetStringValue HKLM, REG_ARP & sConfigName, "DisplayName", SCRIPTNAME & "_" & sConfigName
oReg.SetDWordValue HKLM, REG_ARP & sConfigName, "SystemComponent", 0
Else
'Update the existing temporary ARP item
fReturn = RegReadValue(HKLM, REG_ARP&sConfigName, "ProductCodes", sProdC, "REG_MULTI_SZ")
If NOT InStr(sProdC, ArpItem) > 0 Then sProdC = sProdC & chr(34) & ArpItem
oReg.SetMultiStringValue HKLM, REG_ARP & sConfigName, "ProductCodes", Split(sProdC, Chr(34))
End If 'RegKeyExists
End If 'fSystemComponent0
End If 'InScope
Next 'ArpItem
End If 'RegEnumKey
'Find the configuration products
If RegEnumKey(HKLM, REG_ARP, arrKeys) Then
For Each ArpItem in arrKeys
sCurKey = REG_ARP & ArpItem & "\"
sValue = ""
fSystemComponent0 = NOT (RegReadValue(HKLM, sCurKey, "SystemComponent", sValue, "REG_DWORD") AND (sValue = "1"))
fPackages = RegReadValue(HKLM, sCurKey, OPACKAGE, sValue, "REG_MULTI_SZ")
fDisplayVersion = RegReadValue(HKLM, sCurKey, "DisplayVersion", sValue, "REG_SZ")
If fDisplayVersion Then
If Len(sValue) > 1 Then
fDisplayVersion = (Left(sValue, 2) = OVERSIONMAJOR)
Else
fDisplayVersion = False
End If
End If
'fSystemComponent0 filter causes issues if the ARP entries have been hidden
'If (fSystemComponent0 AND fPackages AND fDisplayVersion) Then
If (fPackages AND fDisplayVersion) Then
If InStr(ArpItem,".") > 0 Then sConfigName = UCase(Mid(ArpItem, InStr(ArpItem, ".") + 1)) Else sConfigName = UCase(ArpItem)
If NOT dicInstalledSku.Exists(sConfigName) Then dicInstalledSku.Add sConfigName, sConfigName
'Categorize the SKU
'Four categories are available: ClientSuite, ClientSingleProduct, Server, C2R
If RegReadValue(HKLM, REG_ARP & OREGREF & sConfigName, "ProductCodes", sProductCodeList, "REG_MULTI_SZ") Then
fCategorized = False
For Each sProductCode in Split(sProductCodeList, Chr(34))
If Len(sProductCode) = 38 Then
If Mid(sProductCode, 11, 1) = "1" Then
'Server product
If NOT dicSrv.Exists(UCase(sConfigName)) Then dicSrv.Add UCase(sConfigName), sConfigName
fCategorized = True
Exit For
Else
Select Case Mid(sProductCode, 11, 4)
'Client Suites
Case "000F","0011","0012","0013","0014","0015","0016","0017","0018","0019","001A","001B","0029","002B","002E","002F","0030","0031","0033","0035","0037","003D","0044","0049","0061","0062","0066","006C","006D","006F","0074","00A1","00A3","00A9","00BA","00CA","00E0","0100","0103","011A"
If NOT dicCSuite.Exists(UCase(sConfigName)) Then dicCSuite.Add UCase(sConfigName), sConfigName
fCategorized = True
Exit For
Case "007E", "008F", "008C", "24E1", "237A"
If NOT dictionaryKeepProd.Exists(sProductCode) Then dictionaryKeepProd.Add sProductCode, sConfigName
fC2rInstalled = True
Case Else
End Select
End If
End If 'Len 38
Next 'sProductCode
If NOT fCategorized Then
If NOT dicCSingle.Exists(UCase(sConfigName)) Then dicCSingle.Add UCase(sConfigName), sConfigName
End If 'fCategorized
End If 'RegReadValue "ProductCodes"
End If
Next 'ArpItem
End If 'RegEnumKey
End Sub 'FindInstalledOProducts
'=======================================================================================================
'Check if there are Office products from previous versions on the computer
Sub CheckForLegacyProducts
Const OLEGACY = "78E1-11D2-B60F-006097C998E7}.6000-11D3-8CFE-0050048383C9}.6000-11D3-8CFE-0150048383C9}.BDCA-11D1-B7AE-00C04FB92F3D}.6D54-11D4-BEE3-00C04F990354}"
Dim Product
'Set safe default
fLegacyProductFound = True
For Each Product in oMsi.Products
If Len(Product) = 38 Then
'Handle O09 - O11 Products
If InStr(OLEGACY, UCase(Right(Product, 28))) > 0 Then
'Found legacy Office product. Keep flag in default and exit
Exit Sub
End If
If UCase(Right(Product,PRODLEN)) = OFFICEID Then
Select Case Mid(Product,4,2)
Case "12", "14"
'Found legacy Office product. Keep flag in default and exit
Exit Sub
Case Else
End Select
End If
End If '38
Next 'Product
fLegacyProductFound = False
End Sub 'CheckForLegacyProducts
'=======================================================================================================
'Create clean list of Products to remove.
'Strip off bad & empty contents
Sub ValidateRemoveSkuList
Dim Sku, Key, sProductCode, sProductCodeList
Dim arrRemoveSKUs
If fRemoveAll Then
'Remove all mode
For Each Key in dicInstalledSku.Keys
dicRemoveSku.Add Key,dicInstalledSku.Item(Key)
Next 'Key
Else
'Remove individual products or preconfigured configurations mode
'Ensure to have a string with no unexpected contents
sSkuRemoveList = Replace(sSkuRemoveList,";",",")
sSkuRemoveList = Replace(sSkuRemoveList," ","")
sSkuRemoveList = Replace(sSkuRemoveList,Chr(34),"")
While InStr(sSkuRemoveList,",,")>0
sSkuRemoveList = Replace(sSkuRemoveList,",,",",")
Wend
'Prepare 'remove' and 'keep' dictionaries to determine what has to be removed
'Initial pre-fill of 'keep' dic
For Each Key in dicInstalledSku.Keys
dictionaryKeepSku.Add Key,dicInstalledSku.Item(Key)
Next 'Key
'Determine contents of keep and remove dic
'Individual products
arrRemoveSKUs = Split(UCase(sSkuRemoveList),",")
For Each Sku in arrRemoveSKUs
If Sku = "OSE" Then fRemoveOse = True
If dictionaryKeepSku.Exists(Sku) Then
'A Sku to remove has been passed in
'remove the item from the keep dic
dictionaryKeepSku.Remove(Sku)
'Now add it to the remove dic
If NOT dicRemoveSku.Exists(Sku) Then dicRemoveSku.Add Sku,Sku
End If
Next 'Sku
'Client Suite Category
If fRemoveCSuites Then
For Each Key in dicInstalledSku.Keys
If dicCSuite.Exists(Key) Then
If dictionaryKeepSku.Exists(Key) Then dictionaryKeepSku.Remove(Key)
If NOT dicRemoveSku.Exists(Key) Then dicRemoveSku.Add Key,Key
End If
Next 'Key
End If 'fRemoveCSuites
'Client Single/Standalone Category
If fRemoveCSingle Then
For Each Key in dicInstalledSku.Keys
If dicCSingle.Exists(Key) Then
If dictionaryKeepSku.Exists(Key) Then dictionaryKeepSku.Remove(Key)
If NOT dicRemoveSku.Exists(Key) Then dicRemoveSku.Add Key,Key
End If
Next 'Key
End If 'fRemoveCSingle
'Server Category
If fRemoveSrv Then
For Each Key in dicInstalledSku.Keys
If dicSrv.Exists(Key) Then
If dictionaryKeepSku.Exists(Key) Then dictionaryKeepSku.Remove(Key)
If NOT dicRemoveSku.Exists(Key) Then dicRemoveSku.Add Key,Key
End If
Next 'Key
End If 'fRemoveSrv
If NOT dictionaryKeepSku.Count > 0 Then fRemoveAll = True
End If 'fRemoveAll
'Fill the KeepProd dic
For Each Sku in dictionaryKeepSku.Keys
If RegReadValue(HKLM, REG_ARP & OREGREF & Sku, "ProductCodes", sProductCodeList, "REG_MULTI_SZ") Then
For Each sProductCode in Split(sProductCodeList, chr(34))
If Len(sProductCode) = 38 Then
If NOT dictionaryKeepProd.Exists(sProductCode) Then
dictionaryKeepProd.Add sProductCode, Sku
' also add the UpgradeCode
If Not dictionaryKeepProd.Exists(GetUpgradeCode(sProductCode)) Then dictionaryKeepProd.Add GetUpgradeCode(sProductCode), Sku & "_UpgradeCode"
End If
End If '38
Next 'sProductCode
End If
Next 'Sku
If fRemoveAll OR fRemoveOse Then CheckRemoveOSE
If fRemoveAll OR fRemoveOspp Then CheckRemoveOspp
End Sub 'ValidateRemoveSkuList
'=======================================================================================================
'Check if OSE service can be scrubbed
Sub CheckRemoveOSE
Const O11 = "6000-11D3-8CFE-0150048383C9}"
Dim Product
If fRemoveOse Then Exit Sub
For Each Product in oMsi.Products
If Len(Product) = 38 Then
If UCase(Right(Product, 28)) = O11 Then
'Found Office 2003 Product. Set flag to not remove the OSE service
Exit Sub
End If
If UCase(Right(Product, PRODLEN))= OFFICEID Then
Select Case Mid(Product, 4, 2)
Case "12","14","15","16","17"
If NOT Mid(Product, 4, 2) = OVERSIONMAJOR Then
'Found another Office product. Set flag to keep the OSE service
fRemoveOse = False
Exit Sub
End If
Case Else
End Select
End If
End If '38
Next 'Product
fRemoveOse = True
End Sub 'CheckRemoveOSE
'=======================================================================================================
'Check if OSPP service can be scrubbed
Sub CheckRemoveOSPP
Dim Product
If NOT CInt(OVERSIONMAJOR) > 12 Then
fRemoveOspp = False
Exit Sub
End If
If fRemoveOspp Then Exit Sub
If fC2rInstalled Then
fRemoveOspp = False
Exit Sub
End If
For Each Product in oMsi.Products
If Len(Product) = 38 Then
If UCase(Right(Product, PRODLEN)) = OFFICEID Then
Select Case Mid(Product, 4, 2)
Case "14","15","16","17"
If NOT Mid(Product, 4, 2) = OVERSIONMAJOR Then
'Found another Office product. Set flag to keep the OSPP service
fRemoveOspp = False
Exit Sub
End If
Case Else
End Select
End If
End If '38
Next 'Product
fRemoveOspp = True
End Sub 'CheckRemoveOSPP
'=======================================================================================================
'Cache .msi files for products that will be removed in case they are needed for later file detection
Sub CacheMsiFiles
Dim Product
Dim sMsiFile
'Non critical routine for failures.
'Errors will be logged but must not fail the execution
On Error Resume Next
Log " Cache .msi files to temporary Scrub folder"
'Cache the files
For Each Product in oMsi.Products
'Ensure valid GUID length
If CheckDeleteEx(Product) Then
CheckError "CacheMsiFiles"
sMsiFile = oMsi.ProductInfo(Product,"LocalPackage") : CheckError "CacheMsiFiles"
LogOnly " - " & Product & ".msi"
If oFso.FileExists(sMsiFile) Then oFso.CopyFile sMsiFile,sScrubDir & "\" & Product & ".msi",True
CheckError "CacheMsiFiles"
End If 'InScope
Next 'Product
Err.Clear
End Sub 'CacheMsiFiles
'=======================================================================================================
'Build a list of all files that will be deleted
Sub ScanComponents
Const MSIINSTALLSTATE_LOCAL = 3
Dim FileList, RegList, ComponentID, CompClient, Record, qView, MsiDb, CompVerbose
Dim Processes, Process, Prop, prod
Dim sQuery, sSubKeyName, sPath, sFile, sMsiFile, sCompClient, sComponent, sCompReg
Dim fRemoveComponent, fAffectedComponent, fIsPermanent, fIsFile, fIsFolder
Dim i, iProgress, iCompCnt, iRemCnt
Dim dicFLError, oDic, oFolderDic, dicCompPath
Dim hDefKey
'Logfile
Set FileList = oFso.OpenTextFile(sScrubDir & "\FileList.txt",FOR_WRITING,True,True)
Set RegList = oFso.OpenTextFile(sScrubDir & "\RegList.txt",FOR_WRITING,True,True)
Set CompVerbose = oFso.OpenTextFile(sScrubDir & "\CompVerbose.txt",FOR_WRITING,True,True)
'FileListError dic
Set dicFLError = CreateObject("Scripting.Dictionary")
Set oDic = CreateObject("Scripting.Dictionary")
Set oFolderDic = CreateObject("Scripting.Dictionary")
Set dicCompPath = CreateObject("Scripting.Dictionary")
'Prevent that API errors fail script execution
On Error Resume Next
iCompCnt = oMsi.Components.Count
If NOT Err = 0 Then
'API failure
Log "Error during components detection. Cannot complete this task."
Err.Clear
Exit Sub
End If
'Ensure to not divide by zero
If iCompCnt = 0 Then iCompCnt = 1
LogOnly " Scanning " & iCompCnt & " components"
'Enum all Components
For Each ComponentID In oMsi.Components
CompVerbose.WriteLine vbCrLf & "Checking Component: " & ComponentID
'Progress bar
i = i + 1
If iProgress < (i / iCompCnt) * 100 Then
If fCScript Then wscript.stdout.write "." : LogStream.Write "."
iProgress = iProgress + 1
If iProgress = 35 OR iProgress = 70 Then Log ""
End If
'Check if all ComponentClients will be removed
sCompClient = ""
iRemCnt = 0
fIsPermanent = False
fRemoveComponent = False 'Flag to track if the component will be completely removed
fAffectedComponent = False 'Flag to track if some clients remain installed who have a none shared location
dicCompPath.RemoveAll
Err.Clear
For Each CompClient In oMsi.ComponentClients(ComponentID)
CompVerbose.Write " CompClient " & CompClient & "-> "
If Err = 0 Then
'Ensure valid guid length
If Len(CompClient) = 38 Then
fRemoveComponent = InScope(CompClient)
If fRemoveComponent OR (CompClient = "{00000000-0000-0000-0000-000000000000}") Then
sPath = ""
sPath = LCase(oMsi.ComponentPath(CompClient,ComponentID))
sPath = Replace(sPath,"?",":")
'Scan for msidbComponentAttributesPermanent flag
If CompClient = "{00000000-0000-0000-0000-000000000000}" Then
fIsPermanent = True
iRemCnt = iRemCnt + 1
End If
If fRemoveComponent Then fRemoveComponent = CheckDelete(CompClient)
CompVerbose.Write "CheckDelete: " & fRemoveComponent & "; "
If fRemoveComponent Then
iRemCnt = iRemCnt + 1
fAffectedComponent = True
'Since the scope remains within one Office family the keypath for the component
'is assumed to be identical
If sCompClient = "" Then sCompClient = CompClient
' flag the CompClient entry for removal
sCompReg = "Installer\Components\"&GetCompressedGuid(ComponentID)&"\"&GetCompressedGuid(CompClient)
If NOT dicDelRegKey.Exists(sCompReg) Then
dicDelRegKey.Add sCompReg,HKCR
RegList.WriteLine HiveString(HKCR)&"\"&sCompReg
End If
sCompReg = "SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components\"&GetCompressedGuid(ComponentID)&"\"&GetCompressedGuid(CompClient)
If NOT dicDelRegKey.Exists(sCompReg) Then
dicDelRegKey.Add sCompReg,HKLM
RegList.WriteLine HiveString(HKCR)&"\"&sCompReg
End If
Else
If NOT dicCompPath.Exists(sPath) Then dicCompPath.Add sPath,CompClient
End If
CompVerbose.WriteLine "AffectedComponent: " & fAffectedComponent
CompVerbose.WriteLine " CompClient now set to: " & sCompClient
Else
CompVerbose.Write "InScope: " & fRemoveComponent & "; "
End If
Else
CompVerbose.WriteLine "Error: Invalid metadata"
If NOT dicFLError.Exists("Error: Invalid metadata found. ComponentID: "&ComponentID &", ComponentClient: "&CompClient) Then _
dicFLError.Add "Error: Invalid metadata found. ComponentID: "&ComponentID &", ComponentClient: "&CompClient, ComponentID
End If '38
Else
CompVerbose.WriteLine "Error: " & Err.number & " " & Err.Description
Err.Clear
End If 'Err = 0
Next 'CompClient
'Determine if the component resources go away
sPath = ""
fRemoveComponent = fAffectedComponent AND (iRemCnt = oMsi.ComponentClients(ComponentID).Count)
CompVerbose.WriteLine " Component goes away: " & fRemoveComponent
' This caused unintentional removals
' If NOT fRemoveComponent AND fAffectedComponent Then
' 'Flag as removable if component has a unique keypath
' sPath = LCase(oMsi.ComponentPath(sCompClient,ComponentID))
' sPath = Replace(sPath,"?",":")
' fRemoveComponent = NOT dicCompPath.Exists(sPath)
' End If
If fRemoveComponent Then
'Check msidbComponentAttributesPermanent flag
If fIsPermanent AND NOT fForce Then fRemoveComponent = False
CompVerbose.WriteLine " msidbComponentAttributesPermanent: " & NOT fRemoveComponent
End If
If fRemoveComponent Then
CompVerbose.WriteLine " RESULT: Component IN SCOPE for removal"
fIsFile = False : fIsFolder = False
'Component resources go away for this product
Err.Clear
'Add the component registration key to ensure removal
sCompReg = "Installer\Components\"&GetCompressedGuid(ComponentID)&"\"
If NOT dicDelRegKey.Exists(sCompReg) Then
dicDelRegKey.Add sCompReg,HKCR
RegList.WriteLine HiveString(HKCR)&"\"&sCompReg
End If
sCompReg = "SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components\"&GetCompressedGuid(ComponentID)&"\"
If NOT dicDelRegKey.Exists(sCompReg) Then
dicDelRegKey.Add sCompReg,HKLM
RegList.WriteLine HiveString(HKCR)&"\"&sCompReg
End If
'Get the component path
If sPath = "" Then
sPath = LCase(oMsi.ComponentPath(sCompClient,ComponentID))
sPath = Replace(sPath,"?",":")
End If
CompVerbose.WriteLine " Path: " & sPath
If Len(sPath) > 4 Then
If Left(sPath,1) = "0" Then
'Registry keypath
Select Case Left(sPath,2)
Case "00"
sPath = Mid(sPath,5)
hDefKey = HKCR
Case "01"
sPath = Mid(sPath,5)
hDefKey = HKCU
Case "02","22"
sPath = Mid(sPath,5)
hDefKey = HKLM
Case Else
'
End Select
'Go for the safe way and just reset the default entry
'compared to deleting the whole key
If Right(sPath,1) = "\" Then sPath = sPath & "(Default)"
If NOT dicDelRegKey.Exists(sPath) Then
dicDelRegKey.Add sPath,hDefKey
RegList.WriteLine HiveString(hDefKey)&"\"&sPath
End If
Else
'File or Folder
If oFso.FileExists(sPath) OR oFso.FolderExists(sPath) Then
If Right(sPath,1) = "\" Then
fIsFolder = True
CompVerbose.WriteLine " Folder check OK"
Else
fIsFile = True
CompVerbose.WriteLine " File check OK"
End If
If fIsFile Then sPath = oFso.GetFile(sPath).ParentFolder
If Not oFolderDic.Exists(sPath) Then
oFolderDic.Add sPath,sPath
FileList.WriteLine sPath & vbTab & "(FOLDER)"
End If
'Get the .msi file
If oFso.FileExists(sScrubDir & "\" & sCompClient & ".msi") Then
sMsiFile = sScrubDir & "\" & sCompClient & ".msi"
Else
sMsiFile = oMsi.ProductInfo(sCompClient,"LocalPackage")
End If
CompVerbose.WriteLine " Set msi file to : " & sMsiFile
If Not Err = 0 Then
CompVerbose.WriteLine " Error: Failed to obtain .msi file for product " & sCompClient
If NOT dicFLError.Exists("Failed to obtain .msi file for product "&sCompClient) Then _
dicFLError.Add "Failed to obtain .msi file for product "&sCompClient, ComponentID
Err.Clear
End If
CompVerbose.Write " Open .msi file for reading returned: "
Set MsiDb = oMsi.OpenDatabase(sMsiFile,MSIOPENDATABASEREADONLY)
If Err = 0 Then
CompVerbose.WriteLine " SUCCESS"
'Get the component name from the 'Component' table
sQuery = "SELECT `Component`,`ComponentId` FROM Component WHERE `ComponentId` = '" & ComponentID &"'"
Set qView = MsiDb.OpenView(sQuery) : qView.Execute
Set Record = qView.Fetch()
If Not Record Is Nothing Then sComponent = Record.Stringdata(1)
CompVerbose.WriteLine " Obtained ComponentId as: " & sComponent
'Get filenames from the 'File' table
sQuery = "SELECT `Component_`,`FileName` FROM File WHERE `Component_` = '" & sComponent &"'"
Set qView = MsiDb.OpenView(sQuery) : qView.Execute
Set Record = qView.Fetch()
Do Until Record Is Nothing
'Read the filename
sFile = Record.StringData(2)
If InStr(sFile,"|") > 0 Then sFile = Mid(sFile,InStr(sFile,"|")+1,Len(sFile))
'sFile = sPath & "\" & sFile
CompVerbose.WriteLine " File: " & sPath& "\" & sFile
If Not oDic.Exists(sPath & "\" & sFile) Then
'Exception handler
fAdd = True
Select Case UCase(sFile)
Case "FPERSON.DLL"
'Catch exception caused by changed .msi keypath authoring logic for smart tags
For Each prod in oMsi.Products
If NOT Checkdelete(prod) Then
If oMsi.FeatureState(prod, "MSTagPluginNamesFiles") = MSIINSTALLSTATE_LOCAL Then
fAdd = False
Exit For
End If
End If
Next 'prod
Case Else
End Select
If fAdd Then
CompVerbose.WriteLine " Added as new file to dictionary"
oDic.Add sPath & "\" & sFile,sFile
FileList.WriteLine sFile & vbTab & sPath & "\" & sFile
If Len(sFile)>4 Then
sFile = LCase(sFile)
If Right(sFile,4) = ".exe" Then
If NOT dicApps.Exists(sFile) Then
Select Case sFile
Case "setup.exe","ose.exe","osppsvc.exe","explorer.exe"
Case Else
dicApps.Add sFile,LCase(sPath) & "\" & sFile
CompVerbose.WriteLine " Added to the list of processes that need to be closed."
End Select
End If 'dicApps.Exists
End If '.exe
End If 'Len > 4
End If 'fAdd
End If 'oDic.Exists
Set Record = qView.Fetch()
Loop
Set Record = Nothing
qView.Close
Set qView = Nothing
Else
CompVerbose.WriteLine " Error: Could not read from .msi file"
If NOT dicFLError.Exists("Error: Could not read from .msi file: "&sMsiFile) Then _
dicFLError.Add "Error: Could not read from .msi file: "&sMsiFile, ComponentID
Err.Clear
End If 'Err = 0
Else
CompVerbose.WriteLine " Error: File check FAILED"
End If 'FileExists(sPath)
End If
End If 'Len(sPath) > 4
Else
CompVerbose.WriteLine " RESULT: Component NOT in scope for removal"
If fAffectedComponent Then
'Add the path to the 'Keep' dictionary
Err.Clear
For Each CompClient In oMsi.ComponentClients(ComponentID)
'Get the component path
sPath = "" : sPath = LCase(oMsi.ComponentPath(CompClient,ComponentID))
sPath = Replace(sPath,"?",":")
If Len(sPath) > 4 Then
If Left(sPath,1) = "0" Then
'Registry keypath
Select Case Left(sPath,2)
Case "00"
sPath = Mid(sPath,5)
hDefKey = HKCR
Case "01"
sPath = Mid(sPath,5)
hDefKey = HKCU
Case "02","22"
sPath = Mid(sPath,5)
hDefKey = HKLM
Case Else
'
End Select
If NOT dictionaryKeepReg.Exists(LCase(sPath)) Then
dictionaryKeepReg.Add LCase(sPath),hDefKey
End If
Else
'File keypath
If oFso.FileExists(sPath) Then
If NOT dictionaryKeepFolder.Exists(LCase(sPath)) Then dictionaryKeepFolder.Add LCase(sPath)
sPath = LCase(oFso.GetFile(sPath).ParentFolder) & "\"
If NOT dictionaryKeepFolder.Exists(sPath) Then AddKeepFolder sPath
End If
'Folder keypath
If oFso.FolderExists(sPath) Then AddKeepFolder sPath
End If 'Is Registry
End If 'sPath > 4
Next 'CompClient
End If 'fAffectedComponent
End If 'fRemoveComponent
Err.Clear
Next 'ComponentID
On Error Goto 0
Log " Done" & vbCrLf
If dicFLError.Count > 0 Then LogOnly Join(dicFLError.Keys,vbCrLf)
If Not oFolderDic.Count = 0 Then arrDeleteFolders = oFolderDic.Keys Else Set arrDeleteFolders = Nothing
If Not oDic.Count = 0 Then arrDeleteFiles = oDic.Keys Else Set arrDeleteFiles = Nothing
End Sub 'ScanComponents
'=======================================================================================================
'Try to remove the products by calling setup.exe
Sub SetupExeRemoval
Dim OseService, Service, TextStream
Dim iSetupCnt, RetVal
Dim Sku, sConfigFile, sUninstallCmd, sCatalyst, sDll, sDisplayLevel, sNoCancel
iSetupCnt = 0
If Not dicRemoveSku.Count > 0 Then
Log " Nothing to remove for Setup.exe"
Exit Sub
End If
'Ensure that the OSE service is *installed, *not disabled, *running under System context.
'If validation fails exit out of this sub.
Set OseService = oWmiLocal.Execquery("Select * From Win32_Service Where Name like 'ose%'")
If OseService.Count = 0 Then Exit Sub
For Each Service in OseService
If (Service.StartMode = "Disabled") AND (Not Service.ChangeStartMode("Manual")=0) Then Exit Sub
If (Not Service.StartName = "LocalSystem") AND (Service.Change( , , , , , , "LocalSystem", "")) Then Exit Sub
Next 'Service
For Each Sku in dicRemoveSku.Keys
If Sku="CLICK2RUN" Then
'Already done
Else
'Create an "unattended" config.xml file for uninstall
If fQuiet Then sDisplayLevel = "Basic" Else sDisplayLevel="Basic"
If fNoCancel Then sNoCancel="Yes" Else sNoCancel="No"
Set TextStream = oFso.OpenTextFile(sScrubDir & "\config.xml",FOR_WRITING,True,True)
TextStream.Writeline "<Configuration Product=""" & Sku & """>"
TextStream.Writeline "<Display Level=""" & sDisplayLevel & """ CompletionNotice=""No"" SuppressModal=""Yes"" NoCancel=""" & sNoCancel & """ AcceptEula=""Yes"" />"
TextStream.Writeline "<Logging Type=""Verbose"" Path=""" & sLogDir & """ Template=""Microsoft Office " & Sku & " Setup(*).txt"" />"
TextStream.Writeline "<Setting Id=""MSIRESTARTMANAGERCONTROL"" Value=""Disable"" />"
TextStream.Writeline "<Setting Id=""SETUP_REBOOT"" Value=""Never"" />"
TextStream.Writeline "</Configuration>"
TextStream.Close
Set TextStream = Nothing
'Ensure path to setup.exe is valid to prevent errors
sDll = ""
If RegReadValue(HKLM,REG_ARP & OREGREF & Sku,"UninstallString",sCatalyst,"REG_SZ") Then
If InStr(LCase(sCatalyst),"/dll")>0 Then sDll = Right(sCatalyst,Len(sCatalyst)-InStr(LCase(sCatalyst),"/dll")+2)
If InStr(sCatalyst,"/")>0 Then sCatalyst = Left(sCatalyst,InStr(sCatalyst,"/")-1)
sCatalyst = Trim(Replace(sCatalyst,Chr(34),""))
If NOT oFso.FileExists(sCatalyst) Then
sCatalyst = sCommonProgramFiles & "\" & OREF & "\Office Setup Controller\setup.exe"
If NOT oFso.FileExists(sCatalyst) AND f64 Then
sCatalyst = sCommonProgramFilesX86 & "" & OREF & "\Office Setup Controller\setup.exe"
End If
End If
If oFso.FileExists(sCatalyst) Then
sUninstallCmd = Chr(34) & sCatalyst & Chr(34) & " /uninstall " & Sku & " /config " & Chr(34) & sScrubDir & "\config.xml" & Chr(34) & sDll
iSetupCnt = iSetupCnt + 1
Log " - Calling Setup.exe to remove " & Sku '& vbCrLf & sUninstallCmd
If Not fDetectOnly Then
On Error Resume Next
' end other instances of setup
EndCurrentInstalls
' call uninstall
RetVal = oWShell.Run(sUninstallCmd,0,True) : CheckError "SetupExeRemoval"
Log " - Setup.exe returned: " & SetupRetVal(Retval) & " (" & RetVal & ")" & vbCrLf
fRebootRequired = fRebootRequired OR (RetVal = "3010")
On Error Goto 0
Else
Log " -> Removal suppressed in preview mode."
End If
Else
Log " Error: Office setup.exe appears to be missing"
End If 'RetVal = 0) AND oFso.FileExists
End If 'RegReadValue
End If
Next 'Sku
If iSetupCnt = 0 Then Log " Nothing to remove for setup."
End Sub 'SetupExeRemoval
'=======================================================================================================
'Invoke msiexec to remove individual .MSI packages
Sub MsiexecRemoval
Dim Product
Dim i
Dim sCmd, sReturn, sMsiProp
Dim fRegWipe
fRegWipe = False
Select Case OVERSIONMAJOR
Case "11"
sMsiProp = " REBOOT=ReallySuppress NOLOCALCACHEROLLBACK=1"
Case "12"
fRegWipe = True
sMsiProp = " REBOOT=ReallySuppress NOREMOVESPAWN=True"
Case "14"
fRegWipe = True
sMsiProp = " REBOOT=ReallySuppress NOREMOVESPAWN=True"
Case "15"
fRegWipe = True
sMsiProp = " REBOOT=ReallySuppress NOREMOVESPAWN=True"
Case Else
End Select
'Clear up ARP first to avoid possible custom action dependencies
If fRegWipe Then RegWipeARP
'Check MSI registered products
'Office System does only support per machine installation so it's sufficient to use Installer.Products
i = 0
sMsiProp = " MSIRESTARTMANAGERCONTROL=Disable" & sMsiProp
For Each Product in oMsi.Products
If CheckDeleteEx(Product) Then
i = i + 1
Log " Calling msiexec.exe to remove " & Product
sCmd = "msiexec.exe /x" & Product & sMsiProp
If fQuiet AND NOT fBasic Then
sCmd = sCmd & " /q"
Else
sCmd = sCmd & " /qb-"
End If
sCmd = sCmd & " /l*v+ "&chr(34)&sLogDir&"\Uninstall_"&Product&".log"&chr(34)
If NOT fDetectOnly Then
' end other instances of setup
EndCurrentInstalls
'Execute the uninstall
LogOnly " - Calling msiexec with '"&sCmd&"'"
sReturn = oWShell.Run(sCmd, 0, True)
Log " - msiexec returned: " & SetupRetVal(sReturn) & " (" & sReturn & ")" & vbCrLf
fRebootRequired = fRebootRequired OR (sReturn = "3010") OR (sReturn = "1618")
Else
Log " -> Removal suppressed in preview mode."
LogOnly " -> Command: "&sCmd
End If
End If 'InScope
Next 'Product
If i = 0 Then Log " Nothing to remove for msiexec"
End Sub 'MsiexecRemoval
'=======================================================================================================
'Remove the OSE (Office Source Engine) service
Sub RemoveOSE
On Error Resume Next
Log vbCrLf & "OSE CleanUp"
DeleteService "ose"
'Delete the folder
DeleteFolder sCommonProgramFiles & "\Microsoft Shared\Source Engine"
'Delete the registration
RegDeleteKey HKLM,"SYSTEM\CurrentControlSet\Services\ose\"
End Sub 'RemoveOSE
'=======================================================================================================
'File cleanup operations for the Local Installation Source (MSOCache)
Sub WipeLIS
Const LISROOT = "MSOCache\All Users\"
Dim LogicalDisks, Disk, Folder, SubFolder, MseFolder, File, Files
Dim arrSubFolders
Dim sFolder
Dim fRemoveFolder
Log vbCrLf & "LIS CleanUp"
'Search all hard disks
Set LogicalDisks = oWmiLocal.ExecQuery("Select * From Win32_LogicalDisk WHERE DriveType=3")
For Each Disk in LogicalDisks
If oFso.FolderExists(Disk.DeviceID & "\" & LISROOT) Then
Set Folder = oFso.GetFolder(Disk.DeviceID & "\" & LISROOT)
For Each Subfolder in Folder.Subfolders
If Len(Subfolder) > 37 Then
If fRemoveAll Then
If (Mid(Subfolder.Name,27,PRODLEN) = OFFICEID AND Mid(SubFolder.Name,4,2)=OVERSIONMAJOR) OR _
LCase(Right(Subfolder.Name,7)) = OVERSIONMAJOR &".data" Then DeleteFolder Subfolder.Path
Else
If (Mid(Subfolder.Name,27,PRODLEN) = OFFICEID AND Mid(SubFolder.Name,4,2)=OVERSIONMAJOR) AND _
CheckDelete(UCase(Left(Subfolder.Name,38))) AND _
UCase(Right(Subfolder,1))= UCase(Left(Disk.DeviceID,1))Then DeleteFolder Subfolder.Path
End If
End If 'Len > 37
Next 'Subfolder
If (Folder.Subfolders.Count = 0) AND (Folder.Files.Count = 0) Then
sFolder = Folder.Path
Set Folder = Nothing
SmartDeleteFolder sFolder
End If
End If 'oFso.FolderExists
Next 'Disk
'MSECache
If EnumFolders(sProgramFiles,arrSubFolders) Then
For Each SubFolder in arrSubFolders
If UCase(Right(SubFolder,9))="\MSECACHE" Then
ReDim arrMseFolders(-1)
Set Folder = oFso.GetFolder(SubFolder)
GetMseFolderStructure Folder
For Each MseFolder in arrMseFolders
If oFso.FolderExists(MseFolder) Then
fRemoveFolder = False
Set Folder = oFso.GetFolder(MseFolder)
Set Files = Folder.Files
For Each File in Files
If (LCase(Right(File.Name,4))=".msi") Then
If CheckDelete(ProductCode(File.Path)) Then
fRemoveFolder = True
Exit For
End If 'CheckDelete
End If
Next 'File
Set Files = Nothing
Set Folder = Nothing
If fRemoveFolder Then SmartDeleteFolder MseFolder
End If 'oFso.FolderExists(MseFolder)
Next 'MseFolder
End If
Next 'SubFolder
End If 'oFso.FolderExists
End Sub 'WipeLis
'=======================================================================================================
'Wipe files and folders as documented in KB 928218
Sub FileWipeAll
Dim sFolder
Dim Folder, Subfolder
If fForce OR fQuiet OR fPassive Then CloseOfficeApps
'Handle other services.
Select Case OVERSIONMAJOR
Case "11"
Case "12"
Case "14"
DeleteService "odserv"
DeleteService "Microsoft Office Groove Audit Service"
DeleteService "Microsoft SharePoint Workspace Audit Service"
Case "15"
Case Else
End Select
'User specific files
If NOT fKeepUser Then
'Delete files that should be backed up before deleting them
CopyAndDeleteFile sAppdata & "\Microsoft\Templates\Normal.dotm"
CopyAndDeleteFile sAppdata & "\Microsoft\Templates\Normalemail.dotm"
sFolder = sAppdata & "\microsoft\document building blocks"
If oFso.FolderExists(sFolder) Then
Set Folder = oFso.GetFolder(sFolder)
For Each Subfolder In Folder.Subfolders
If oFso.FileExists(Subfolder & "\blocks.dotx") Then CopyAndDeleteFile Subfolder & "\blocks.dotx"
Next 'Subfolder
Set Folder = Nothing
End If 'oFso.FolderExists(sFolder)
End If
'Run the individual filewipe from component detection first
FileWipeIndividual
If fC2rInstalled AND NOT fForce Then Exit Sub
'Take care of the rest
DeleteFolder sOInstallRoot
DeleteFolder sCommonProgramFiles & "\Microsoft Shared\" & OREF
DeleteFile sAllUsersProfile & "\Application Data\Microsoft\Office\Data\opa"&OVERSIONMAJOR&".dat"
DeleteFile sAllUsersProfile & "\Application Data\Microsoft\Office\Data\opa"&OVERSIONMAJOR&".bak"
DeleteFile sAllUsersProfile & "\Microsoft\Office\Data\opa"&OVERSIONMAJOR&".dat"
DeleteFile sAllUsersProfile & "\Microsoft\Office\Data\opa"&OVERSIONMAJOR&".bak"
If (fRemoveOspp OR fForce) AND CInt(OVERSIONMAJOR) > 12 Then
If CInt(OVERSIONMAJOR) = 15 Then CleanOSPP
DeleteService "osppsvc"
DeleteFolder sCommonProgramFiles & "\Microsoft Shared\OfficeSoftwareProtectionPlatform"
DeleteFolder sAllUsersProfile & "\Microsoft\OfficeSoftwareProtectionPlatform"
End If
Select Case OVERSIONMAJOR
Case "12"
Case "14"
DeleteFile oWShell.SpecialFolders("AllUsersStartup")&"\OfficeSAS.lnk"
DeleteFile oWShell.SpecialFolders("Startup")&"\OneNote 2010 Screen Clipper and Launcher.lnk"
Case "15"
Case Else
End Select
DeleteEmptyFolder sCommonProgramFiles & "\Microsoft Shared\" & OREF
DeleteEmptyFolder sCommonProgramFiles & "\Microsoft Shared\"
DeleteEmptyFolder sProgramFiles & "\Microsoft Office\" & OREF
DeleteEmptyFolder sProgramFiles & "\Microsoft Office\"
End Sub 'FileWipeAll
'=======================================================================================================
'Wipe individual files & folders related to SKU's that are no longer installed
Sub FileWipeIndividual
Dim LogicalDisks, Disk,sc
Dim File, Files, XmlFile, scFiles, oFile, Folder, SubFolder, Processes, Process, item
Dim sFile, sFolder, sPath, sConfigName, sContents, sProductCode, sLocalDrives,sScQuery
Dim sValue, sScRoots
Dim arrSubfolders, arrShortCutRoots
Dim fKeepFolder, fDeleteSC
Dim iRet,iCnt,iPos
Log vbCrLf & "File CleanUp"
If IsArray(arrDeleteFiles) Then
If fForce OR fQuiet Then
Log " Doing Action: StopOSE"
iRet = StopService("ose")
Set Processes = oWmiLocal.ExecQuery("Select * From Win32_Process Where Name like 'ose%.exe'")
For Each Process in Processes
LogOnly " - Running process : " & Process.Name
Log " -> Ending process: " & Process.Name
iRet = Process.Terminate()
Next 'Process
LogOnly " End Action: StopOSE"
CloseOfficeApps
End If
'Wipe individual files detected earlier
LogOnly " Removing left behind files"
For Each sFile in arrDeleteFiles
If oFso.FileExists(sFile) Then DeleteFile sFile
Next 'File
End If 'IsArray
'Wipe Catalyst in commonfiles
sFolder = sCommonProgramFiles & "\microsoft shared\"&OREF&"\Office Setup Controller\"
If EnumFolderNames(sFolder,arrSubFolders) Then
For Each SubFolder in arrSubFolders
sPath = sFolder & SubFolder
If InStr(SubFolder, ".") > 0 Then sConfigName = UCase(Left(SubFolder, InStr(SubFolder, ".") - 1)) Else sConfigName = UCase(Subfolder)
If GetFolderPath(sPath) Then
Set Folder = oFso.GetFolder(sPath)
Set Files = Folder.Files
fKeepFolder = False
For Each File In Files
If Len(File.Name)>3 Then
If (LCase(Right(File.Name,4))=".xml") Then
If Len(File.Name) >= Len(sConfigName) Then
If (UCase(Left(File.Name,Len(sConfigName)))=sConfigName) Then
Set XmlFile = oFso.OpenTextFile(File,1)
sContents = XmlFile.ReadAll
Set XmlFile = Nothing
sProductCode = ""
On Error Resume Next
sProductCode = Mid(sContents,InStr(sContents,"ProductCode=")+Len("ProductCode=")+1,38)
On Error Goto 0
If Len(sProductCode) = 38 Then
If CheckDelete(sProductCode) Then DeleteFile File.Path Else fKeepFolder = True
End If
End If 'sConfigName
End If 'Len >=
End If '.xml
End If 'Len(File.Name)>3
Next 'File
Set Files = Nothing
Set Folder = Nothing
If Not fKeepFolder Then DeleteFolder sPath
End If 'GetFolderPath
Next 'SubFolder
End If 'EnumFolderNames
'Wipe Shortcuts
If NOT fSkipSD Then
On Error Resume Next
Log " Searching for shortcuts"
CleanShortcuts sAllUsersProfile, True, False
CleanShortcuts sProfilesDirectory, True, False
On Error Goto 0
End If 'NOT SkipSD
Err.Clear
End Sub 'FileWipeIndividual
'=======================================================================================================
'-------------------------------------------------------------------------------
' CleanShortcuts
'
' Recursively search all profile folders for Office shortcuts in scope
'-------------------------------------------------------------------------------
Sub CleanShortcuts (sFolder, fDelete, fUnPin)
Dim oFolder, fld, file, sc, item
Dim fDeleteSC
Set oFolder = oFso.GetFolder(sFolder)
' exclude system protected link folders
If CBool(oFolder.Attributes AND 1024) Then Exit Sub
On Error Resume Next
For Each fld In oFolder.SubFolders
If Err <> 0 Then
CheckError "CleanShortcuts: " & vbTab & sFolder
Else
CleanShortcuts fld.Path, fDelete, fUnPin
End If
Next
For Each file In oFolder.Files
If LCase(Right(file.Path, 4)) = ".lnk" Then
fDeleteSC = False
LogOnly " check file: " & file.Path
set sc = oWShell.CreateShortcut(file.Path)
If Err <> 0 Then
CheckError "CleanShortcutsSC: " & vbTab & sFolder
Else
'Compare if the shortcut target is in the list of executables that will be removed
'LogOnly " - SC.TargetPath: " & sc.TargetPath
If Len(sc.TargetPath) > 0 Then
If InStr(sc.TargetPath,"{") > 0 Then
'Handle Windows Installer shortcuts
If Len(sc.TargetPath) >= InStr(sc.TargetPath,"{") + 37 Then
If CheckDelete(Mid(sc.TargetPath, InStr(sc.TargetPath,"{"), 38)) Then fDeleteSC = True
End If
Else
'Handle regular shortcuts
If NOT fBypass_Stage1 Then
' Compare against results from component scan
For Each item in dicApps.Items
If LCase(sc.TargetPath) = item Then
LogOnly " - removing shortcut per match from component detection: " & file.Path
fDeleteSC = True
Exit For
End If
Next 'item
Else
End If
If NOT oFso.FileExists(sc.TargetPath) Then
' Shortcut target does not exist
If InStr (sc.TargetPath, OREF) > 0 Then
LogOnly " - removing Office shortcut with non-existent target: " & file.Path & " - " & sc.TargetPath
fDeleteSC = True
Else
'LogOnly " - keep orphaned SC as target is not in scope: " & sc.TargetPath
End If
Else
'LogOnly " - keep SC as shortcut target does still exist: " & sc.TargetPath
End If
End If
End If
End If
If fDeleteSC Then
If Not IsArray(arrDeleteFolders) Then ReDim arrDeleteFolders(0)
sFolder = file.Drive & file.Path
If Not arrDeleteFolders(UBound(arrDeleteFolders)) = sFolder Then
ReDim Preserve arrDeleteFolders(UBound(arrDeleteFolders) + 1)
arrDeleteFolders(UBound(arrDeleteFolders)) = sFolder
End If
If fUnPin Then UnPin file
If fDelete Then
If fBypass_Stage1 Then UnPin file
DeleteFile file.Path
End If
fDeleteSC = False
End If 'fDeleteSC
End If
Next
On Error Goto 0
End Sub 'CleanShortcuts
'-------------------------------------------------------------------------------
' UnPin
'
' Unpins a shortcut from the taskbar or start menu
'-------------------------------------------------------------------------------
Sub UnPin(file)
Dim fldItem, verb
On Error Resume Next
Set fldItem = oShellApp.NameSpace(file.ParentFolder.Path).ParseName(file.Name)
For Each verb in fldItem.Verbs
Select Case Replace(verb, "&", "")
Case "Unpin from Taskbar", "Von Taskleiste lösen", "Détacher du barre des tâches", "Détacher de la barre des tâches", "Desanclar de la barra de tareas", "Ta bort från Aktivitetsfältet", "タスク バーに表示しない(K)", "작업 표시줄에서 제거(K)", "Открепить от панели задач"
verb.DoIt
Case "Unpin from Start Menu", "Vom Startmenü lösen", "Détacher du menu Démarrer", "Détacher de la menu Démarrer"
If iVersionNT = 601 Then verb.DoIt
End Select
Next
On Error Goto 0
End Sub
'=======================================================================================================
Sub DelScrubTmp
On Error Resume Next
If oFso.FolderExists(sScrubDir & "\ScrubTmp") Then oFso.DeleteFolder sScrubDir & "\ScrubTmp",True
End Sub 'DelScrubTmp
'=======================================================================================================
'Ensure there are no unexpected .msi files in the scrub folder
Sub DeleteMsiScrubCache
Dim Folder, File, Files
Log vbCrLf & "ScrubCache CleanUp"
Set Folder = oFso.GetFolder(sScrubDir) : CheckError "DeleteMsiScrubCache"
Set Files = Folder.Files
For Each File in Files
CheckError "DeleteMsiScrubCache"
If LCase(Right(File.Name,4))=".msi" Then
CheckError "DeleteMsiScrubCache"
DeleteFile File.Path : CheckError "DeleteMsiScrubCache"
End If
Next 'File
End Sub 'DeleteMsiScrubCache
'=======================================================================================================
Sub MsiClearOrphanedFiles
Const USERSIDEVERYONE = "s-1-1-0"
Const MSIINSTALLCONTEXT_ALL = 7
Const MSIPATCHSTATE_ALL = 15
'Error handling inlined
On Error Resume Next
Dim Patch, AllPatches, Product, AllProducts
Dim File, Files, Folder
Dim sFName, sLocalMsp, sLocalMsi, sPatchList, sMsiList
Set Folder = oFso.GetFolder(sWinDir & "\Installer")
Set Files = Folder.Files
Log vbCrLf & "Windows Installer cache CleanUp"
'Get a complete list of patches
Err.Clear
Set AllPatches = oMsi.PatchesEx("",USERSIDEVERYONE,MSIINSTALLCONTEXT_ALL,MSIPATCHSTATE_ALL)
If Err <> 0 Then
CheckError "MsiClearOrphanedFiles (msp)"
Else
'Fill a comma separated stringlist with all .msp patchfiles
For Each Patch in AllPatches
sLocalMsp = "" : sLocalMsp = LCase(Patch.Patchproperty("LocalPackage")) : CheckError "MsiClearOrphanedFiles (msp)"
sPatchList = sPatchList & sLocalMsp & ","
Next 'Patch
'Delete all non referenced .msp files from %windir%\installer
For Each File in Files
sFName = "" : sFName = LCase(File.Path)
If LCase(Right(sFName,4)) = ".msp" Then
If Not InStr(sPatchList,sFName) > 0 Then
'While this is an orphaned file keep the scope of Office only
If InStr(UCase(MspTargets(File.Path)),OFFICEID)>0 Then DeleteFile File.Path
End If
End If 'LCase(Right(sFName,4))
Next 'File
End If 'Err=0
'Get a complete list products
Err.Clear
Set AllProducts = oMsi.ProductsEx("",USERSIDEVERYONE,MSIINSTALLCONTEXT_ALL)
If Err <> 0 Then
CheckError "MsiClearOrphanedFiles (msi)"
Else
'Fill a comma separated stringlist with all .msi files
For Each Product in AllProducts
sLocalMsi = "" : sLocalMsi = LCase(Product.InstallProperty("LocalPackage")) : CheckError "MsiClearOrphanedFiles (msi)"
sMsiList = sMsiList & sLocalMsi & ","
Next 'Product
'Delete all non referenced .msi files from %windir%\installer
For Each File in Files
sFName = "" : sFName = LCase(File.Path)
If LCase(Right(sFName,4)) = ".msi" Then
If Not InStr(sMsiList,sFName) > 0 Then
'While this is an orphaned file keep the scope of Office only
If UCase(Right(ProductCode(File.Path),PRODLEN))=OFFICEID Then DeleteFile File.Path
End If
End If 'LCase(Right(sFName,4)) = ".msi"
Next 'File
End If 'Err=0
End Sub 'MsiClearOrphanedFiles
'=======================================================================================================
Sub RegWipe
Dim Item, Name, Sku, key
Dim hDefKey, sSubKeyName, sCurKey, value, sValue, sGuid
Dim fkeep, fSystemComponent0, fPackages, fDisplayVersion
Dim arrKeys, arrNames, arrTypes, arrMultiSzValues, arrMultiSzNewValues
Dim arrTestNames, arrTestTypes
Dim i, iLoopCnt, iPos
Dim fDelReg
Log vbCrLf & "Registry CleanUp"
'Wipe registry data
'User Profile settings
Log " - User Policies"
RegDeleteKey HKCU,"Software\Policies\Microsoft\Office\" & OVERSION & "\"
If NOT fKeepUser Then
RegDeleteKey HKCU,"Software\Microsoft\Office\" & OVERSION & "\"
Log " - User Settings"
End If 'fKeepUser
'Computer specific settings
If (fRemoveAll AND NOT fC2rInstalled) OR (fRemoveAll AND fForce) Then
Log " - Machine Settings"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Office\" & OVERSION & "\"
If fRemoveOse OR fForce Then
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Office Test\"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Office\Common\","LastAccessInstall", False
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Office\Common\","MID", False
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Office\Excel\Addins\Microsoft.PerformancePoint.Planning.Client.Excel\"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Office\InfoPath\Converters\Import\InfoPath.DesignerExcelImport\Versions\",OVERSION, False
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Office\InfoPath\Converters\Import\InfoPath.DesignerWordImport\Versions\",OVERSION, False
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Office\Outlook\"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Shared Tools\Text Converters\Export\MEWord12\"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Shared Tools\Text Converters\Export\Word12\"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Shared Tools\Text Converters\Export\Word97\"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Shared Tools\Text Converters\Import\MEWord12\"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Shared Tools\Text Converters\Import\Word12\"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Shared Tools\Text Converters\Import\Word97\"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Run\","GrooveMonitor", False
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Run\","LobiServer", False
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Run\","BCSSync", False
RegDeleteKey HKLM,"SYSTEM\CurrentControlSet\Services\Outlook\"
End If
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Office\Common\OffDiag\Location\",OVERSIONMAJOR, False
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Terminal Server\Install\Software\Microsoft\Office\" & OVERSION & "\"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Office\Common\OffDiag\Location\",OVERSIONMAJOR, False
RegDeleteKey HKLM,"SOFTWARE\Microsoft\OfficeCustomizeWizard\" & OVERSION & "\"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Terminal Server\Install\SOFTWARE\Microsoft\OfficeCustomizeWizard\" & OVERSION & "\"
Select Case OVERSIONMAJOR
Case "11"
'Jet_Replication
sValue = ""
If RegReadValue(HKCR,"CLSID\{CC2C83A6-9BE4-11D0-98E7-00C04FC2CAF5}\InprocServer32","SystemDB",sValue,"REG_SZ") Then
If Len(sValue) > Len(sOInstallRoot) Then
If LCase(Left(sValue,Len(sOInstallRoot))) = LCase(sOInstallRoot) Then RegDeleteKey HKCR,"CLSID\{CC2C83A6-9BE4-11D0-98E7-00C04FC2CAF5}\InprocServer32\"
End If
End If
Case "12"
Case "14"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\OfficeSoftwareProtectionPlatform_Test\"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Office\Common\ActiveX Compatibility\{00024512-0000-0000-C000-000000000046}\"
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Office\OneNote\Adapters\","{456B0D0E-49DD-4C95-8DB6-175F54DE69A3}", False
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{42042206-2D85-11D3-8CFF-005004838597}", False
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{993BE281-6695-4BA5-8A2A-7AACBFAAB69E}", False
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{0006F045-0000-0000-C000-000000000046}", False
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{C41662BB-1FA0-4CE0-8DC5-9B7F8279FF97}", False
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{7CCA70DB-DE7A-4FB7-9B2B-52E2335A3B5A}", False
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{506F4668-F13E-4AA1-BB04-B43203AB3CC0}", False
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{D66DC78C-4F61-447F-942B-3FB6980118CF}", False
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Browser Helper Objects\{B4F3A835-0E21-4959-BA22-42B3008E02FF}\"
'Groove Extensions
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellExecuteHooks\","{B5A7F190-DDA6-4420-B3BA-52453494E6CD}", False
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{99FD978C-D287-4F50-827F-B2C658EDA8E7}", False
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{AB5C5600-7E6E-4B06-9197-9ECEF74D31CC}", False
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{920E6DB1-9907-4370-B3A0-BAFC03D81399}", False
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{16F3DD56-1AF5-4347-846D-7C10C4192619}", False
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{2916C86E-86A6-43FE-8112-43ABE6BF8DCC}", False
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{72853161-30C5-4D22-B7F9-0BBC1D38A37E}", False
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{6C467336-8281-4E60-8204-430CED96822D}", False
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{2A541AE1-5BF6-4665-A8A3-CFA9672E4291}", False
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{B5A7F190-DDA6-4420-B3BA-52453494E6CD}", False
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{A449600E-1DC6-4232-B948-9BD794D62056}", False
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{3D60EDA7-9AB4-4DA8-864C-D9B5F2E7281D}", False
RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{387E725D-DC16-4D76-B310-2C93ED4752A0}", False
RegDeleteKey HKLM,"SOFTWARE\Classes\*\shellex\ContextMenuHandlers\XXX Groove GFS Context Menu Handler XXX\"
RegDeleteKey HKLM,"SOFTWARE\Classes\AllFilesystemObjects\shellex\ContextMenuHandlers\XXX Groove GFS Context Menu Handler XXX\"
RegDeleteKey HKLM,"SOFTWARE\Classes\Directory\shellex\ContextMenuHandlers\XXX Groove GFS Context Menu Handler XXX\"
RegDeleteKey HKLM,"SOFTWARE\Classes\Folder\ShellEx\ContextMenuHandlers\XXX Groove GFS Context Menu Handler XXX\"
RegDeleteKey HKLM,"SOFTWARE\Classes\Directory\Background\shellex\ContextMenuHandlers\XXX Groove GFS Context Menu Handler XXX\"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers\Groove Explorer Icon Overlay 1 (GFS Unread Stub)\"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers\Groove Explorer Icon Overlay 2 (GFS Stub)\"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers\Groove Explorer Icon Overlay 2.5 (GFS Unread Folder)\"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers\Groove Explorer Icon Overlay 3 (GFS Folder)\"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers\Groove Explorer Icon Overlay 4 (GFS Unread Mark)\"
RegDeleteKey HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Browser Helper Objects\{72853161-30C5-4D22-B7F9-0BBC1D38A37E}\"
Case 15
Case Else
End Select
'Win32Assemblies
Log " - Win32Assemblies"
If RegEnumKey(HKCR,"Installer\Win32Assemblies\",arrKeys) Then
For Each Item in arrKeys
If InStr(UCase(Item),OREF)>0 Then RegDeleteKey HKCR,"Installer\Win32Assemblies\"&Item & "\"
Next 'Item
End If 'RegEnumKey
'Groove blocks reinstall if it locates groove.exe over this key
If RegKeyExists(HKCR,"GrooveFile\Shell\Open\Command\") Then
sValue = ""
RegReadValue HKCR,"GrooveFile\Shell\Open\Command\","",sValue,"REG_SZ"
If InStr(sValue,"\"&OREF&"\")>0 Then RegDeleteKey HKCR,"GrooveFile\"
End If 'RegKeyExists
End If 'fRemoveAll
Select Case OVERSIONMAJOR
Case "11"
For iLoopCnt = 1 to 3
Select Case iLoopCnt
Case 1
'CIW - HKCU
sSubKeyName = "Software\Microsoft\OfficeCustomizeWizard\" & OVERSION & "\RegKeyPaths\"
hDefKey = HKCU
Case 2
'CIW - HKLM
sSubKeyName = "SOFTWARE\Microsoft\OfficeCustomizeWizard\" & OVERSION & "\RegKeyPaths\"
hDefKey = HKLM
Case 3
'Add/Remove Programs
sSubKeyName = REG_ARP
hDefKey = HKLM
End Select
If RegEnumKey(hDefKey,sSubKeyName,arrKeys) Then
For Each Item in arrKeys
'OFFICEID id
If Len(Item)>37 Then
sGuid = UCase(Left(Item,38))
If Right(sGuid,PRODLEN)=OFFICEID Then
If CheckDelete(sGuid) Then
RegDeleteKey hDefKey, sSubKeyName & Item & "\"
End If
End If 'Right(Item,PRODLEN)=OFFICEID
End If 'Len(Item)>37
Next 'Item
If iLoopCnt < 3 Then
If RegEnumValues(hDefKey,sSubKeyName,arrNames,arrTypes) Then
i = 0
For Each Name in arrNames
If RegReadValue(hDefKey,sSubKeyName,Name,sValue,arrTypes(i)) Then
If sValue = sGuid Then RegDeleteValue hDefKey,sSubKeyName,Name, False
End If
i = i + 1
Next
End If
End If
End If
If NOT RegEnumKey(hDefKey,sSubKeyName,arrKeys) Then RegDeleteKey hDefKey,"Software\Microsoft\OfficeCustomizeWizard\11.0\"
If NOT RegEnumKey(hDefKey,"Software\Microsoft\OfficeCustomizeWizard\11.0\",arrKeys) Then RegDeleteKey hDefKey,"Software\Microsoft\OfficeCustomizeWizard\"
Next 'iLoopCnt
Case "12"
'Add/Remove Programs
RegWipeARP
Case "14"
'Add/Remove Programs
RegWipeARP
Case Else
End Select
'UpgradeCodes, WI config, WI global config
For iLoopCnt = 1 to 5
Select Case iLoopCnt
Case 1
Log " - HKLM UpgradeCodes"
sSubKeyName = "SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UpgradeCodes\"
hDefKey = HKLM
Case 2
Log " - HKCR UpgradeCodes"
sSubKeyName = "Installer\UpgradeCodes\"
hDefKey = HKCR
Case 3
Log " - HKLM Products"
sSubKeyName = "SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\"
hDefKey = HKLM
Case 4
Log " - HKCR Features"
sSubKeyName = "Installer\Features\"
hDefKey = HKCR
Case 5
Log " - HKCR Products"
sSubKeyName = "Installer\Products\"
hDefKey = HKCR
Case Else
sSubKeyName = ""
hDefKey = ""
End Select
If RegEnumKey(hDefKey,sSubKeyName,arrKeys) Then
For Each Item in arrKeys
'Ensure we have the expected length for a compressed GUID
If Len(Item)=32 Then
'Expand the GUID
sGuid = GetExpandedGuid(Item)
'Check if it's an Office key
If CheckDeleteEx (sGuid) Then
RegDeleteKey hDefKey,sSubKeyName & Item & "\"
Else
If iLoopCnt < 3 Then
'Enum all entries
RegEnumValues hDefKey,sSubKeyName & Item,arrNames,arrTypes
If IsArray(arrNames) Then
'Delete entries within removal scope
For Each Name in arrNames
If Len(Name)=32 Then
sGuid = GetExpandedGuid(Name)
If CheckDelete(sGuid) Then RegDeleteValue hDefKey, sSubKeyName & Item & "\", Name, True
Else
'Invalid data -> delete the value
RegDeleteValue hDefKey, sSubKeyName & Item & "\", Name, True
End If
Next 'Name
End If 'IsArray(arrNames)
'If all entries were removed - delete the key
RegEnumValues hDefKey,sSubKeyName & Item,arrNames,arrTypes
If Not IsArray(arrNames) Then RegDeleteKey hDefKey, sSubKeyName & Item & "\"
Else 'iLoopCnt >= 3
If CheckDelete(sGuid) Then RegDeleteKey hDefKey, sSubKeyName & Item & "\"
End If 'iLoopCnt < 3
End If 'fRemoveAll
End If 'Len(Item)=32
Next 'Item
End If 'RegEnumKey
Next 'iLoopCnt
'Components
Log " - Global Components"
sSubKeyName = "SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components\"
If RegEnumKey(HKLM,sSubKeyName,arrKeys) Then
For Each Item in arrKeys
'Ensure we have the expected length for a compressed GUID
If Len(Item)=32 Then
If RegEnumValues(HKLM,sSubKeyName & Item,arrNames,arrTypes) Then
If IsArray(arrNames) Then
For Each Name in arrNames
If Len(Name)=32 Then
sGuid = GetExpandedGuid(Name)
If CheckDelete(sGuid) Then
RegDeleteValue HKLM, sSubKeyName & Item & "\", Name, False
'Check if the key is now empty
If NOT RegEnumValues(HKLM,sSubKeyName & Item,arrTestNames,arrTestTypes) Then
If NOT dicDelRegKey.Exists(sSubKeyName&Item&"\") Then dicDelRegKey.Add sSubKeyName&Item&"\",HKCR
End If
End If
End If '32
Next 'Name
End If 'IsArray
End If 'RegEnumValues
End If '32
Next 'Item
End If 'RegEnumKey
'Published Components
Log " - Published Components"
sSubKeyName = "Installer\Components\"
If RegEnumKey(HKCR,sSubKeyName,arrKeys) Then
For Each Item in arrKeys
'Ensure we have the expected length for a compressed GUID
If Len(Item)=32 Then
If RegEnumValues(HKCR,sSubKeyName & Item,arrNames,arrTypes) Then
If IsArray(arrNames) Then
For Each Name in arrNames
If RegReadValue (HKCR,sSubKeyName & Item, Name, sValue,"REG_MULTI_SZ") Then
arrMultiSzValues = Split(sValue,chr(34))
If IsArray(arrMultiSzValues) Then
i = -1
ReDim arrMultiSzNewValues(-1)
fDelReg = False
For Each value in arrMultiSzValues
If Len(value) > 19 Then
sGuid = ""
If GetDecodedGuid(Left(value,SQUISHED),sGuid) Then
If CheckDelete(sGuid) Then
fDelReg = True
Else
i = i + 1
ReDim Preserve arrMultiSzNewValues(i)
arrMultiSzNewValues(i) = value
End If 'CheckDelete
End If 'decode
End If '19
Next 'Value
If NOT (i = -1) Then
If NOT fDetectOnly Then
If NOT UBound(arrMultiSzValues) = i Then oReg.SetMultiStringValue HKCR,sSubKeyName & Item,Name,arrMultiSzNewValues
End If
Else
If fDelReg Then
RegDeleteValue HKCR,sSubKeyName & Item & "\", Name, False
'Check if the key is now empty
If NOT RegEnumValues(HKCR,sSubKeyName & Item,arrTestNames,arrTestTypes) Then
If NOT dicDelRegKey.Exists(sSubKeyName&Item&"\") Then dicDelRegKey.Add sSubKeyName&Item&"\",HKCR
End If
End If 'DelReg
End If
End If 'IsArray
End If
Next 'Name
End If 'IsArray
End If 'RegEnumValues
End If '32
Next 'Item
End If 'RegEnumKey
'Delivery
Log " - Delivery"
hDefKey = HKLM
sSubKeyName = "SOFTWARE\Microsoft\Office\Delivery\SourceEngine\Downloads\"
If RegEnumKey(HKLM,sSubKeyName,arrKeys) Then
For Each Item in arrKeys
If Len(Item) > 37 Then
If fRemoveAll Then
If (Mid(Item,27,PRODLEN)=OFFICEID AND Mid(Item,4,2)=OVERSIONMAJOR) OR _
LCase(Right(Item,7))=OVERSIONMAJOR&".data" Then RegDeleteKey HKLM,sSubKeyName & Item & "\"
Else
If (Mid(Item,27,PRODLEN)=OFFICEID AND Mid(Item,4,2)=OVERSIONMAJOR) AND _
CheckDelete(UCase(Left(Item,38))) Then RegDeleteKey HKLM,sSubKeyName & Item & "\"
End If
End If '37
Next 'Item
End If 'RegEnumKey
'Registration
Log " - HKLM Registration"
hDefKey = HKLM
sSubKeyName = "SOFTWARE\Microsoft\Office\"&OVERSION&"\Registration\"
If RegEnumKey(HKLM,sSubKeyName,arrKeys) Then
For Each Item in arrKeys
If Len(Item)>37 Then
If CheckDelete(UCase(Left(Item,38))) Then RegDeleteKey HKLM,sSubKeyName & Item & "\"
End If
Next 'Item
End If 'RegEnumKey
'User Preconfigurations
Log " - HKLM User Preconfigurations"
hDefKey = HKLM
sSubKeyName = "SOFTWARE\Microsoft\Office\"&OVERSION&"\User Settings\"
If RegEnumKey(HKLM,sSubKeyName,arrKeys) Then
For Each Item in arrKeys
If Len(Item)>37 Then
If CheckDelete(UCase(Left(Item,38))) Then RegDeleteKey HKLM,sSubKeyName & Item & "\"
End If
Next 'Item
End If 'RegEnumKey
'Known Keypath settings
Log " - Detcted KeyPath settings"
For Each key in dicDelRegKey.Keys
If Right(key,1) = "\" Then
RegDeleteKey dicDelRegKey.Item(key),key
Else
iPos = InStrRev(Key,"\")
If iPos > 0 Then RegDeleteValue dicDelRegKey.Item(key), Left(key,iPos - 1), Mid(key,iPos+1), False
End If
Next
'Temporary entries in ARP
TmpKeyCleanUp
End Sub 'RegWipe
'=======================================================================================================
'Clean up Add/Remove Programs registry
Sub RegWipeARP
Dim Item, Name, Sku, key
Dim sSubKeyName, sCurKey, sValue, sGuid
Dim fkeep, fSystemComponent0, fPackages, fDisplayVersion
Dim arrKeys
'Add/Remove Programs
sSubKeyName = REG_ARP
If RegEnumKey(HKLM,sSubKeyName,arrKeys) Then
For Each Item in arrKeys
'*0FF1CE*
If Len(Item)>37 Then
sGuid = UCase(Left(Item,38))
If CheckDeleteEx(sGuid) Then RegDeleteKey HKLM, sSubKeyName & Item
End If 'Len(Item)>37
'Config entries
sCurKey = sSubKeyName & Item & "\"
fSystemComponent0 = Not (RegReadValue(HKLM,sCurKey,"SystemComponent",sValue,"REG_DWORD") AND (sValue = "1"))
fPackages = RegReadValue(HKLM,sCurKey,OPACKAGE,sValue,"REG_MULTI_SZ")
fDisplayVersion = RegReadValue(HKLM,sCurKey,"DisplayVersion",sValue,"REG_SZ")
If fDisplayVersion AND Len(sValue) > 1 Then
fDisplayVersion = (Left(sValue,2) = OVERSIONMAJOR)
End If
If (fSystemComponent0 AND fPackages AND fDisplayVersion) Then
fKeep = False
If Not fRemoveAll Then
For Each Sku in dictionaryKeepSku.Keys
If UCase(Item) = OREGREF & Sku Then
fkeep = True
Exit For
End If
Next 'Sku
End If
If Not fkeep Then RegDeleteKey HKLM, sSubKeyName & Item
End If
Next 'Item
End If 'RegEnumKey
End Sub 'RegWipeARP
'=======================================================================================================
'Clean up temporary registry keys
Sub TmpKeyCleanUp
Dim TmpKey
If fLogInitialized Then Log " - temporary OffScrub registry entries"
If IsArray(arrTmpSKUs) Then
For Each TmpKey in arrTmpSKUs
oReg.DeleteKey HKLM, REG_ARP & TmpKey
Next 'Item
End If 'IsArray
End Sub 'TmpKeyCleanUp
'=======================================================================================================
' Helper Functions
'=======================================================================================================
'Create a log with the results of the SKU detection
Sub LogSkuResults
Dim SkuLog, SkuKey , p
On Error Resume Next 'Don't fail on logging
Set SkuLog = oFso.OpenTextFile(sScrubDir & "\SkuLog.txt",FOR_WRITING,True,True)
SkuLog.WriteLine "Installed SKUs (All):"
SkuLog.WriteLine "====================="
For Each SkuKey in dicInstalledSku.Keys
SkuLog.WriteLine " - " & SkuKey
Next 'Key
SkuLog.WriteLine vbCrLf & "Server SKUs:"
SkuLog.WriteLine "============"
For Each SkuKey in dicSrv.Keys
SkuLog.WriteLine " - " & SkuKey
Next 'Key
SkuLog.WriteLine vbCrLf & "Client Suite SKUs:"
SkuLog.WriteLine "=================="
For Each SkuKey in dicCSuite.Keys
SkuLog.WriteLine " - " & SkuKey
Next 'Key
SkuLog.WriteLine vbCrLf & "Client Standalone SKUs:"
SkuLog.WriteLine "======================="
For Each SkuKey in dicCSingle.Keys
SkuLog.WriteLine " - " & SkuKey
Next 'Key
SkuLog.WriteLine vbCrLf & "Installed Products (All):"
SkuLog.WriteLine "========================="
For Each p in oMsi.Products
If InScope(p) Then
SkuLog.Write " - " & p & " - "
SkuLog.Write oMsi.ProductInfo(p, "ProductName")
SkuLog.WriteLine " "
End If
Next 'Product
SkuLog.WriteLine vbCrLf & "***************************************************************************************************" & vbCrLf
SkuLog.WriteLine vbCrLf & "SKUs to keep:"
SkuLog.WriteLine "============="
For Each SkuKey in dictionaryKeepSku.Keys
SkuLog.WriteLine " - " & SkuKey
Next 'Key
SkuLog.WriteLine vbCrLf & "Products to keep:"
SkuLog.WriteLine "================="
For Each p in dictionaryKeepProd.Keys
SkuLog.Write " - " & p & " - "
SkuLog.Write oMsi.ProductInfo(p, "ProductName")
SkuLog.WriteLine " "
Next 'Key
SkuLog.WriteLine vbCrLf & "***************************************************************************************************" & vbCrLf
SkuLog.WriteLine vbCrLf & "SKUs to remove:"
SkuLog.WriteLine "==============="
For Each SkuKey in dicRemoveSku.Keys
SkuLog.WriteLine " - " & SkuKey
Next 'Key
SkuLog.WriteLine vbCrLf & "Products to remove:"
SkuLog.WriteLine "==================="
For Each p in oMsi.Products
If CheckDeleteEx(p) Then
SkuLog.Write " - " & p & " - "
SkuLog.Write oMsi.ProductInfo(p, "ProductName")
SkuLog.WriteLine " "
End If 'InScope
Next 'Product
SkuLog.Close
Set SkuLog = Nothing
End Sub 'LogSkuResults
'=======================================================================================================
'End all running instances of applications that will be removed
Sub CloseOfficeApps
Dim Processes, Process, prop
Dim fWait
Dim iRet
On Error Resume Next
fWait = False
Log " Doing Action: CloseOfficeApps"
Set Processes = oWmiLocal.ExecQuery("Select * From Win32_Process")
For Each Process in Processes
If dicApps.Exists(LCase(Process.Name)) Then
Log " - End process " & Process.Name
iRet = Process.Terminate()
CheckError "CloseOfficeApps: " & "Process.Name"
Else
For Each prop in Process.Properties_
If prop.Name = "ExecutablePath" Then
If InStr(UCase(prop.Value), UCase(sOInstallRoot)) > 0 Then
Log " - End process '" & Process.Name
iRet = Process.Terminate()
CheckError "CloseOfficeApps: " & "Process.Name"
fWait = True
End If
End If 'ExcecutablePath
Next 'prop
End If
Next 'Process
If fWait Then
wscript.sleep 10000
End If
LogOnly " End Action: CloseOfficeApps"
End Sub 'CloseOfficeApps
'=======================================================================================================
'Ensure Windows Explorer is restarted if needed
Sub RestoreExplorer
Dim Processes, Result, oAT, DateTime, JobID
Dim sCmd
'Non critical routine. Don't fail on error
On Error Resume Next
wscript.sleep 1000
Set Processes = oWmiLocal.ExecQuery("Select * From Win32_Process Where Name='explorer.exe'")
If Processes.Count < 1 Then
oWShell.Run "explorer.exe"
'To handle this in case of System context, schedule and run as interactive task
If iVersionNT > 502 Then
'Vista and later
oWShell.Run "SCHTASKS /Create /TN OffScrEx /TR explorer /SC ONCE /ST 12:00 /IT",0,True
oWShell.Run "SCHTASKS /Run /TN OffScrEx", 0, True
oWShell.Run "SCHTASKS /Delete /TN OffScrEx /F", 0, False
Else
Set oAT = oWmiLocal.Get("Win32_ScheduledJob")
Set DateTime = CreateObject("WbemScripting.SWbemDateTime")
DateTime.SetVarDate DateAdd("n",1,Now),True
Result = oAT.Create("explorer.exe", DateTime.Value, , , , True, JobID)
End If 'iVersionNT
End If
End Sub 'RestoreExploer
'=======================================================================================================
'Returns the delimiter for a passed in string
Function Delimiter (sVersion)
Dim iCnt, iAsc
Delimiter = " "
For iCnt = 1 To Len(sVersion)
iAsc = Asc(Mid(sVersion, iCnt, 1))
If Not (iASC >= 48 And iASC <= 57) Then
Delimiter = Mid(sVersion, iCnt, 1)
Exit Function
End If
Next 'iCnt
End Function
'=======================================================================================================
'Check registry access permissions. Failure will terminate the script
Function CheckRegPermissions
Const KEY_QUERY_VALUE = &H0001
Const KEY_SET_VALUE = &H0002
Const KEY_CREATE_SUB_KEY = &H0004
Const DELETE = &H00010000
Dim sSubKeyName
Dim fReturn
CheckRegPermissions = True
sSubKeyName = "Software\Microsoft\Windows\"
oReg.CheckAccess HKLM, sSubKeyName, KEY_QUERY_VALUE, fReturn
If Not fReturn Then CheckRegPermissions = False
oReg.CheckAccess HKLM, sSubKeyName, KEY_SET_VALUE, fReturn
If Not fReturn Then CheckRegPermissions = False
oReg.CheckAccess HKLM, sSubKeyName, KEY_CREATE_SUB_KEY, fReturn
If Not fReturn Then CheckRegPermissions = False
oReg.CheckAccess HKLM, sSubKeyName, DELETE, fReturn
If Not fReturn Then CheckRegPermissions = False
End Function 'CheckRegPermissions
'=======================================================================================================
'Check if a product will be removed
Function CheckDeleteEx (sProductCode)
CheckDeleteEx = False
If CheckDelete (sProductCode) Then
CheckDeleteEx = True
Exit Function
End If
If (fRemoveAll AND NOT fC2rInstalled) OR (fRemoveAll AND fForce) Then
CheckDeleteEx = InScope(sProductCode) AND NOT dictionaryKeepProd.Exists(UCase(sProductCode))
End If
End Function 'CheckDelete
'=======================================================================================================
'Check if an Office product is still registered with a SKU that stays on the computer
Function CheckDelete (sProductCode)
'Ensure valid GUID length
If NOT Len(sProductCode) = 38 Then
CheckDelete = False
Exit Function
End If
'If it's a non Office ProductCode exit with false right away
CheckDelete = InScope(sProductCode)
If Not CheckDelete Then Exit Function
If dictionaryKeepProd.Exists(UCase(sProductCode)) Then CheckDelete = False
End Function 'CheckDelete
'=======================================================================================================
'Check if ProductCode is in scope
Function InScope(sProductCode)
Dim fInScope
Dim sProd
fInScope = False
If Len(sProductCode) = 38 Then
sProd = UCase(sProductCode)
Select Case OVERSIONMAJOR
Case "11"
If Right(sProd,PRODLEN)=OFFICEID Then fInScope = True
Case "12"
If Right(sProd,PRODLEN)=OFFICEID AND Mid(sProd,4,2) = OVERSIONMAJOR Then fInScope = True
Case "14"
If Right(sProd,PRODLEN)=OFFICEID AND Mid(sProd,4,2) = OVERSIONMAJOR Then fInScope = True
Case "15"
If Right(sProd,PRODLEN)=OFFICEID AND Mid(sProd,4,2) = OVERSIONMAJOR Then
Select Case Mid(sProd, 11, 4)
Case "007E", "008F", "008C", "24E1", "237A"
' C2R products - keep them
Case Else
fInScope = True
End Select
End If
Case Else
End Select
End If '38
InScope = fInScope
End Function 'InScope
'=======================================================================================================
'Register an orphaned .msi product as installed for MSI
Sub MsiRegisterProduct (sMsiFile)
Dim sDisplayVersion, sCurKey, sDisplayName, sLang, sProductCode, sTmpKey
Dim iCnt
'Create a temporary keys to simulate an installed product
sProductCode = ""
sProductCode = GetMsiProductCode(sMsiFile)
sDisplayVersion = GetMsiProductVersion(sMsiFile)
If sDisplayVersion = "" Then sDisplayVersion = OVERSION & ".0000.0000"
sDisplayName = GetMsiProductName(sMsiFile)
If sDisplayName = "" Then sDisplayName = sProductCode
Select Case OVERSIONMAJOR
Case "9","10","11"
sLang = CInt("&h" & Mid(sProductCode,6,4))
Case "12","14"
sLang = CInt("&h" & Mid(sProductCode,16,4))
Case Else
End Select
For iCnt = 1 To 3
Select Case iCnt
Case 1
sCurKey = REG_ARP & sProductCode
oReg.CreateKey HKLM,sCurKey
Case 2
sCurKey = "SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\" & GetCompressedGuid(sProductCode)
oReg.CreateKey HKLM,sCurKey
oReg.CreateKey HKLM,sCurKey & "\Features"
oReg.CreateKey HKLM,sCurKey & "\InstallProperties"
oReg.CreateKey HKLM,sCurKey & "\Patches"
oReg.CreateKey HKLM,sCurKey & "\Usage"
sCurKey = sCurKey & "\InstallProperties"
oReg.SetStringValue HKLM,sCurKey,"LocalPackage",sMsiFile
Case 3
sCurKey = "Installer\Products\" & GetCompressedGuid(sProductCode)
sTmpKey = sCurKey
oReg.CreateKey HKCR,sCurKey
oReg.SetDWordValue HKCR,sCurKey,"AdvertiseFlags",388
oReg.SetDWordValue HKCR,sCurKey,"Assignment",1
oReg.SetDWordValue HKCR,sCurKey,"AuthorizedLUAApp",0
oReg.SetStringValue HKCR,sCurKey,"Clients",":"
oReg.SetDWordValue HKCR,sCurKey,"DeploymentFlags",3
oReg.SetDWordValue HKCR,sCurKey,"InstanceType",0
oReg.SetDWordValue HKCR,sCurKey,"Language",sLang
oReg.SetStringValue HKCR,sCurKey,"PackageCode",GetMsiPackageCode(sMsiFile)
oReg.SetStringValue HKCR,sCurKey,"ProductName",sDisplayName
oReg.SetDWordValue HKCR,sCurKey,"VersionMinor",0
sCurKey = sTmpKey & "\SourceList"
oReg.CreateKey HKCR,sCurKey
oReg.SetExpandedStringValue HKCR,sCurKey,"LastUsedSource",sScrubDir
oReg.SetStringValue HKCR,sCurKey,"PackageName",Mid(sMsiFile,InstrRev(sMsiFile,"\")+1)
sCurKey = sTmpKey & "\SourceList\Media"
oReg.CreateKey HKCR,sCurKey
oReg.SetStringValue HKCR,sCurKey,"1",OREF & ";1"
oReg.SetStringValue HKCR,sCurKey,"DiskPrompt",sDisplayName
sCurKey = sTmpKey & "\SourceList\Net"
oReg.CreateKey HKCR,sCurKey
oReg.SetExpandedStringValue HKCR,sCurKey,"1",sScrubDir
Case Else
End Select
If iCnt <3 Then
oReg.SetStringValue HKLM,sCurKey,"Comments",""
oReg.SetStringValue HKLM,sCurKey,"Contact",""
oReg.SetStringValue HKLM,sCurKey,"DisplayName",sDisplayName
oReg.SetStringValue HKLM,sCurKey,"DisplayVersion",sDisplayVersion
oReg.SetDWordValue HKLM,sCurKey,"EstimatedSize",0
oReg.SetStringValue HKLM,sCurKey,"HelpLink",""
oReg.SetStringValue HKLM,sCurKey,"HelpTelephone",""
oReg.SetStringValue HKLM,sCurKey,"InstallDate","20100101"
If f64 Then
oReg.SetStringValue HKLM,sCurKey,"InstallLocation",sProgramFilesX86
Else
oReg.SetStringValue HKLM,sCurKey,"InstallLocation",sProgramFiles
End If
oReg.SetStringValue HKLM,sCurKey,"InstallSource",sScrubDir
oReg.SetDWordValue HKLM,sCurKey,"Language",sLang
oReg.SetExpandedStringValue HKLM,sCurKey,"ModifyPath","MsiExec.exe /X" & sProductCode
oReg.SetDWordValue HKLM,sCurKey,"NoModify",1
oReg.SetStringValue HKLM,sCurKey,"Publisher","Microsoft Corporation"
oReg.SetStringValue HKLM,sCurKey,"Readme",""
oReg.SetStringValue HKLM,sCurKey,"Size",""
oReg.SetDWordValue HKLM,sCurKey,"SystemComponent",0
oReg.SetExpandedStringValue HKLM,sCurKey,"UninstallString","MsiExec.exe /X" & sProductCode
oReg.SetStringValue HKLM,sCurKey,"URLInfoAbout",""
oReg.SetStringValue HKLM,sCurKey,"URLUpdateInfo",""
oReg.SetDWordValue HKLM,sCurKey,"Version",0
oReg.SetDWordValue HKLM,sCurKey,"VersionMajor",OVERSIONMAJOR
oReg.SetDWordValue HKLM,sCurKey,"VersionMinor",0
oReg.SetDWordValue HKLM,sCurKey,"WindowsInstaller",1
End If '< 3
Next 'iCnt
End Sub 'MsiRegisterProduct
'=======================================================================================================
'Obtain the ProductCode (GUID) from a .msi package
'The function will open the .msi database and query the 'Property' table to retrieve the ProductCode
Function GetMsiProductCode(sMsiFile)
Dim MsiDb,Record
Dim qView
On Error Resume Next
GetMsiProductCode = ""
Set Record = Nothing
Set MsiDb = oMsi.OpenDatabase(sMsiFile,MSIOPENDATABASEREADONLY)
Set qView = MsiDb.OpenView("SELECT `Value` FROM Property WHERE `Property` = 'ProductCode'")
qView.Execute
Set Record = qView.Fetch
GetMsiProductCode = Record.StringData(1)
qView.Close
End Function 'GetMsiProductCode
'=======================================================================================================
'Obtain the ProductVersion from a .msi package
'The function will open the .msi database and query the 'Property' table to retrieve the ProductCode
Function GetMsiProductVersion(sMsiFile)
Dim MsiDb,Record
Dim qView
On Error Resume Next
GetMsiProductVersion = ""
Set Record = Nothing
Set MsiDb = oMsi.OpenDatabase(sMsiFile,MSIOPENDATABASEREADONLY)
Set qView = MsiDb.OpenView("SELECT `Value` FROM Property WHERE `Property` = 'ProductVersion'")
qView.Execute
Set Record = qView.Fetch
GetMsiProductVersion = Record.StringData(1)
qView.Close
End Function 'GetMsiProductVersion
'=======================================================================================================
'Obtain the ProductVersion from a .msi package
'The function will open the .msi database and query the 'Property' table to retrieve the ProductCode
Function GetMsiProductName(sMsiFile)
Dim MsiDb,Record
Dim qView
On Error Resume Next
GetMsiProductName = ""
Set Record = Nothing
Set MsiDb = oMsi.OpenDatabase(sMsiFile,MSIOPENDATABASEREADONLY)
Set qView = MsiDb.OpenView("SELECT `Value` FROM Property WHERE `Property` = 'ProductName'")
qView.Execute
Set Record = qView.Fetch
GetMsiProductName = Record.StringData(1)
qView.Close
End Function 'GetMsiProductVersion
'=======================================================================================================
'Obtain the PackageCode (GUID) from a .msi package
'The function will the .msi'S SummaryInformation stream
Function GetMsiPackageCode(sMsiFile)
On Error Resume Next
Const PID_REVNUMBER = 9
GetMsiPackageCode = ""
GetMsiPackageCode = GetCompressedGuid(oMsi.SummaryInformation(sMsiFile,MSIOPENDATABASEREADONLY).Property(PID_REVNUMBER))
End Function 'GetMsiPackageCode
'=======================================================================================================
'Returns a string with a list of ProductCodes from the summary information stream
Function MspTargets (sMspFile)
Const MSIOPENDATABASEMODE_PATCHFILE = 32
Const PID_TEMPLATE = 7
Dim Msp
'Non critical routine. Don't fail on error
On Error Resume Next
MspTargets = ""
If oFso.FileExists(sMspFile) Then
Set Msp = Msi.OpenDatabase(WScript.Arguments(0),MSIOPENDATABASEMODE_PATCHFILE)
If Err = 0 Then MspTargets = Msp.SummaryInformation.Property(PID_TEMPLATE)
End If 'oFso.FileExists(sMspFile)
End Function 'MspTargets
'=======================================================================================================
'Return the ProductCode {GUID} from a .MSI package
Function ProductCode(sMsi)
Const MSIUILEVELNONE = 2 'No UI
Dim MsiSession
On Error Resume Next
'Non critical routine. Don't fail on error
If oFso.FileExists(sMsi) Then
oMsi.UILevel = MSIUILEVELNONE
Set MsiSession = oMsi.OpenPackage(sMsi,1)
ProductCode = MsiSession.ProductProperty("ProductCode")
Set MsiSession = Nothing
Else
ProductCode = ""
End If 'oFso.FileExists(sMsi)
End Function 'ProductCode
'=======================================================================================================
Function GetUpgradeCode(sGuid)
'Ensure Valid Length
If NOT Len(sGuid) = 38 Then Exit Function
GetUpgradeCode = "{00" & Mid(sGuid, 4, 2) & "0000-" & Mid(sGuid, 11, 4) & "-0000-" & Mid(sGuid, 21, 1) & "000-" & Mid(sGuid, 26, 1) & "000000FF1CE}"
End Function 'GetUpgradeCode
'=======================================================================================================
Function GetExpandedGuid (sGuid)
Dim i
'Ensure valid length
If NOT Len(sGuid) = 32 Then Exit Function
GetExpandedGuid = "{" & StrReverse(Mid(sGuid,1,8)) & "-" & _
StrReverse(Mid(sGuid,9,4)) & "-" & _
StrReverse(Mid(sGuid,13,4))& "-"
For i = 17 To 20
If i Mod 2 Then
GetExpandedGuid = GetExpandedGuid & mid(sGuid,(i + 1),1)
Else
GetExpandedGuid = GetExpandedGuid & mid(sGuid,(i - 1),1)
End If
Next
GetExpandedGuid = GetExpandedGuid & "-"
For i = 21 To 32
If i Mod 2 Then
GetExpandedGuid = GetExpandedGuid & mid(sGuid,(i + 1),1)
Else
GetExpandedGuid = GetExpandedGuid & mid(sGuid,(i - 1),1)
End If
Next
GetExpandedGuid = GetExpandedGuid & "}"
End Function
'=======================================================================================================
'Converts a GUID into the compressed format
Function GetCompressedGuid (sGuid)
Dim sCompGUID
Dim i
'Ensure Valid Length
If NOT Len(sGuid) = 38 Then Exit Function
sCompGUID = StrReverse(Mid(sGuid,2,8)) & _
StrReverse(Mid(sGuid,11,4)) & _
StrReverse(Mid(sGuid,16,4))
For i = 21 To 24
If i Mod 2 Then
sCompGUID = sCompGUID & Mid(sGuid, (i + 1), 1)
Else
sCompGUID = sCompGUID & Mid(sGuid, (i - 1), 1)
End If
Next
For i = 26 To 37
If i Mod 2 Then
sCompGUID = sCompGUID & Mid(sGuid, (i - 1), 1)
Else
sCompGUID = sCompGUID & Mid(sGuid, (i + 1), 1)
End If
Next
GetCompressedGuid = sCompGUID
End Function
'=======================================================================================================
'Unsquish GUID
Function GetDecodedGuid(sEncGuid, sGuid)
Dim sDecode, sTable, sHex, iChr
Dim arrTable
Dim i, iAsc, pow85, decChar
Dim lTotal
Dim fFailed
fFailed = False
sTable = "0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff," & _
"0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff," & _
"0xff,0x00,0xff,0xff,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0xff," & _
"0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0xff,0xff,0xff,0x16,0xff,0x17," & _
"0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27," & _
"0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x30,0x31,0x32,0x33,0xff,0x34,0x35,0x36," & _
"0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x40,0x41,0x42,0x43,0x44,0x45,0x46," & _
"0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,0x50,0x51,0x52,0xff,0x53,0x54,0xff"
arrTable = Split(sTable,",")
lTotal = 0 : pow85 = 1
For i = 0 To 19
fFailed = True
If i Mod 5 = 0 Then
lTotal = 0 : pow85 = 1
End If ' i Mod 5 = 0
iAsc = Asc(Mid(sEncGuid,i+1,1))
sHex = arrTable(iAsc)
If iAsc >=128 Then Exit For
If sHex = "0xff" Then Exit For
iChr = CInt("&h"&Right(sHex,2))
lTotal = lTotal + (iChr * pow85)
If i Mod 5 = 4 Then sDecode = sDecode & DecToHex(lTotal)
pow85 = pow85 * 85
fFailed = False
Next 'i
If NOT fFailed Then sGuid = "{"&Mid(sDecode,1,8)&"-"& _
Mid(sDecode,13,4)&"-"& _
Mid(sDecode,9,4)&"-"& _
Mid(sDecode,23,2) & Mid(sDecode,21,2)&"-"& _
Mid(sDecode,19,2) & Mid(sDecode,17,2) & Mid(sDecode,31,2) & Mid(sDecode,29,2) & Mid(sDecode,27,2) & Mid(sDecode,25,2) &"}"
GetDecodedGuid = NOT fFailed
End Function 'GetDecodedGuid
'=======================================================================================================
'Convert a long decimal to hex
Function DecToHex(lDec)
Dim sHex
Dim iLen
Dim lVal, lExp
Dim arrChr
arrChr = Array("0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F")
sHex = ""
lVal = lDec
lExp = 16^10
While lExp >= 1
If lVal >= lExp Then
sHex = sHex & arrChr(Int(lVal / lExp))
lVal = lVal - lExp * Int(lVal / lExp)
Else
sHex = sHex & "0"
If sHex = "0" Then sHex = ""
End If
lExp = lExp / 16
Wend
iLen = 8 - Len(sHex)
If iLen > 0 Then sHex = String(iLen,"0") & sHex
DecToHex = sHex
End Function
'=======================================================================================================
'Ensures that only valid metadata entries exist to avoid API failures
Sub EnsureValidWIMetadata (hDefKey,sKey,iValidLength)
Dim arrKeys
Dim SubKey
If Len(sKey) > 1 Then
If Right(sKey,1) = "\" Then sKey = Left(sKey,Len(sKey)-1)
End If
If RegEnumKey(hDefKey,sKey,arrKeys) Then
For Each SubKey in arrKeys
If NOT Len(SubKey) = iValidLength Then
RegDeleteKey hDefKey,sKey & "\" & SubKey & "\"
End If
Next 'SubKey
End If
End Sub 'EnsureValidWIMetadata
'=======================================================================================================
'-------------------------------------------------------------------------------
' CleanOSPP
'
' Clean out licenses from the Office Software Protection Platform
'-------------------------------------------------------------------------------
Sub CleanOSPP
Dim oProductInstances, pi
Dim sCleanOSPP, sCmd, sRetVal
CONST OfficeAppId = "0ff1ce15-a989-479d-af46-f275c6370663" 'Office 2013
sCleanOSPP = "x64\CleanOSPP.exe"
If Not f64 Then sCleanOSPP = "x86\CleanOSPP.exe"
If oFso.FileExists(sScriptDir & sCleanOSPP) Then
sCmd = sScriptDir & sCleanOSPP
Log " Running: " & sCmd
On Error Resume Next
sRetVal = oWShell.Run(sCmd, 0, True)
Log " Return value: " & sRetVal
On Error Goto 0
Exit Sub
End If
On Error Resume Next
' Initialize the software protection platform object with a filter on Office 2013 products
If iVersionNT > 601 Then
Set oProductInstances = oWmiLocal.ExecQuery("SELECT ID, ApplicationId, PartialProductKey, Name, ProductKeyID FROM SoftwareLicensingProduct WHERE ApplicationId = '" & OfficeAppId & "' " & "AND PartialProductKey <> NULL")
Else
Set oProductInstances = oWmiLocal.ExecQuery("SELECT ID, ApplicationId, PartialProductKey, Name, ProductKeyID FROM OfficeSoftwareProtectionProduct WHERE ApplicationId = '" & OfficeAppId & "' " & "AND PartialProductKey <> NULL")
End If
' Remove all licenses
For Each pi in oProductInstances
If NOT IsNull(pi) Then
pi.UninstallProductKey( pi.ProductKeyID)
End If
Next 'pi
End Sub 'CleanOSPP
'=======================================================================================================
'Create a backup copy of the file in the ScrubDir then delete the file
Sub CopyAndDeleteFile(sFile)
Dim File
'Error handling inlined
On Error Resume Next
If oFso.FileExists(sFile) Then
Set File = oFso.GetFile(sFile)
If Not oFso.FolderExists(sScrubDir & "\" & File.ParentFolder.Name) Then oFso.CreateFolder sScrubDir & "\" & File.ParentFolder.Name
If Not fDetectOnly Then
LogOnly " - Backing up file: " & sFile
oFso.CopyFile sFile,sScrubDir & "\" & File.ParentFolder.Name & "\" & File.Name,True : CheckError "CopyAndDeleteFile"
Set File = Nothing
DeleteFile(sFile)
Else
LogOnly " - Simulate CopyAndDelete file: " & sFile
End If
End If 'oFso.FileExists
End Sub 'CopyAndDeleteFile
'=======================================================================================================
'Wrapper to delete a file
Sub DeleteFile(sFile)
Dim File
Dim sFileName, sNewPath
On Error Resume Next
If dictionaryKeepFolder.Exists(LCase(sFile)) Then
If NOT fForce Then
LogOnly " - Disallowing the delete of still required keypath element: " & sFile
Exit Sub
Else
LogOnly " - Enforced delete of still required keypath element: " & sFile
LogOnly " Remaining applications will need a repair!"
End If
End If
If f64 Then
If dictionaryKeepFolder.Exists(LCase(Wow64Folder(sFile))) Then
If NOT fForce Then
LogOnly " - Disallowing the delete of still required keypath element: " & sFile
Exit Sub
Else
LogOnly " - Enforced delete of still required keypath element: " & sFile
LogOnly " Remaining applications will need a repair!"
End If
End If
End If
If oFso.FileExists(sFile) Then
LogOnly " - Delete file: " & sFile
If Not fDetectOnly Then oFso.DeleteFile sFile,True
If Err <> 0 Then
CheckError "DeleteFile"
If fForce Then
'Try to move the file and delete from there
Set File = oFso.GetFile(sFile)
sFileName = File.Name
sNewPath = sScrubDir & "\ScrubTmp"
Set File = Nothing
If Not oFso.FolderExists(sNewPath) Then oFso.CreateFolder(sNewPath)
'Move the file
LogOnly " - Move file to: " & sNewPath & "\" & sFileName
oFso.MoveFile sFile,sNewPath & "\" & sFileName
If Err <> 0 Then
CheckError "DeleteFile (move)"
End If 'Err <> 0
Else
fRebootRequired = True
End If 'fForce
End If 'Err <> 0
End If 'oFso.FileExists
End Sub 'DeleteFile
'=======================================================================================================
'64 bit aware wrapper to return the requested folder
Function GetFolderPath(sPath)
GetFolderPath = True
If oFso.FolderExists(sPath) Then Exit Function
If f64 AND oFso.FolderExists(Wow64Folder(sPath)) Then
sPath = Wow64Folder(sPath)
Exit Function
End If
GetFolderPath = False
End Function 'GetFolderPath
'=======================================================================================================
'Enumerates subfolder names of a folder and returns True if subfolders exist
Function EnumFolderNames (sFolder, arrSubFolders)
Dim Folder, Subfolder
Dim sSubFolders
If oFso.FolderExists(sFolder) Then
Set Folder = oFso.GetFolder(sFolder)
For Each Subfolder in Folder.Subfolders
sSubFolders = sSubFolders & Subfolder.Name & ","
Next 'Subfolder
End If
If f64 AND oFso.FolderExists(Wow64Folder(sFolder)) Then
Set Folder = oFso.GetFolder(Wow64Folder(sFolder))
For Each Subfolder in Folder.Subfolders
sSubFolders = sSubFolders & Subfolder.Name & ","
Next 'Subfolder
End If
If Len(sSubFolders)>0 Then arrSubFolders = RemoveDuplicates(Split(Left(sSubFolders,Len(sSubFolders)-1),","))
EnumFolderNames = Len(sSubFolders)>0
End Function 'EnumFolderNames
'=======================================================================================================
'Enumerates subfolders of a folder and returns True if subfolders exist
Function EnumFolders (sFolder, arrSubFolders)
Dim Folder, Subfolder
Dim sSubFolders
If oFso.FolderExists(sFolder) Then
Set Folder = oFso.GetFolder(sFolder)
For Each Subfolder in Folder.Subfolders
sSubFolders = sSubFolders & Subfolder.Path & ","
Next 'Subfolder
End If
If f64 AND oFso.FolderExists(Wow64Folder(sFolder)) Then
Set Folder = oFso.GetFolder(Wow64Folder(sFolder))
For Each Subfolder in Folder.Subfolders
sSubFolders = sSubFolders & Subfolder.Path & ","
Next 'Subfolder
End If
If Len(sSubFolders)>0 Then arrSubFolders = RemoveDuplicates(Split(Left(sSubFolders,Len(sSubFolders)-1),","))
EnumFolders = Len(sSubFolders)>0
End Function 'EnumFolders
'=======================================================================================================
Sub GetMseFolderStructure (Folder)
Dim SubFolder
For Each SubFolder in Folder.SubFolders
ReDim Preserve arrMseFolders(UBound(arrMseFolders)+1)
arrMseFolders(UBound(arrMseFolders)) = SubFolder.Path
GetMseFolderStructure SubFolder
Next 'SubFolder
End Sub 'GetMseFolderStructure
'=======================================================================================================
'Wrapper to delete a folder
Sub DeleteFolder(sFolder)
Dim Folder
Dim sDelFolder, sFolderName, sNewPath
'Ensure trailing "\"
sFolder = sFolder & "\"
While InStr(sFolder,"\\")>0
sFolder = Replace(sFolder,"\\","\")
Wend
If dictionaryKeepFolder.Exists(LCase(sFolder)) Then
If NOT fForce Then
LogOnly " - Disallowing the delete of still required keypath element: " & sFolder
Exit Sub
Else
LogOnly " - Enforced delete of still required keypath element: " & sFolder
LogOnly " Remaining applications will need a repair!"
End If
End If
If f64 Then
If dictionaryKeepFolder.Exists(LCase(Wow64Folder(sFolder))) Then
If NOT fForce Then
LogOnly " - Disallowing the delete of still required keypath element: " & sFolder
Exit Sub
Else
LogOnly " - Enforced delete of still required keypath element: " & sFolder
LogOnly " Remaining applications will need a repair!"
End If
End If
End If
'Strip trailing "\"
If Len(sFolder) > 1 Then
sFolder = Left(sFolder,Len(sFolder)-1)
End If
On Error Resume Next
If oFso.FolderExists(sFolder) Then
sDelFolder = sFolder
ElseIf f64 AND oFso.FolderExists(Wow64Folder(sFolder)) Then
sDelFolder = Wow64Folder(sFolder)
Else
Exit Sub
End If
If Not fDetectOnly Then
LogOnly " - Delete folder: " & sDelFolder
oFso.DeleteFolder sDelFolder,True
Else
LogOnly " - Simulate delete folder: " & sDelFolder
End If
If Err <> 0 Then
CheckError "DeleteFolder"
'Try to move the folder and delete from there
Set Folder = oFso.GetFolder(sDelFolder)
sFolderName = Folder.Name
sNewPath = sScrubDir & "\ScrubTmp"
Set Folder = Nothing
'Ensure we stay within the same drive
If Not oFso.FolderExists(sNewPath) Then oFso.CreateFolder(sNewPath)
'Move the folder
LogOnly " - Moving folder to: " & sNewPath & "\" & sFolderName
oFso.MoveFolder sFolder,sNewPath & "\" & sFolderName
If Err <> 0 Then
CheckError "DeleteFolder (move)"
End If 'Err <> 0
End If 'Err <> 0
End Sub 'DeleteFolder
'=======================================================================================================
'Delete empty folder structures
Sub DeleteEmptyFolders
Dim Folder
Dim sFolder
' cosmetic' task don't fail on error
On Error Resume Next
If Not IsArray(arrDeleteFolders) Then Exit Sub
Log vbCrLf & "Empty Folder Cleanup"
For Each sFolder in arrDeleteFolders
If oFso.FolderExists(sFolder) Then
Set Folder = oFso.GetFolder(sFolder)
If CBool(Folder.Attributes AND 1024) Then
'exclude protected folder
Else
If (Folder.Subfolders.Count = 0) AND (Folder.Files.Count = 0) Then
Set Folder = Nothing
SmartDeleteFolder sFolder
End If
End If
End If
Next 'sFolder
CheckError "DeleteEmptyFolders"
On Error Goto 0
End Sub 'DeleteEmptyFolders
'=======================================================================================================
'Delete indivdual empty folder structures
Sub DeleteEmptyFolder (sFolder)
Dim Folder
' cosmetic' task don't fail on error
On Error Resume Next
If oFso.FolderExists(sFolder) Then
Set Folder = oFso.GetFolder(sFolder)
If CBool(Folder.Attributes AND 1024) Then
'exclude protected folder
Else
If (Folder.Subfolders.Count = 0) AND (Folder.Files.Count = 0) Then
Set Folder = Nothing
SmartDeleteFolder sFolder
End If
End If
End If
CheckError "DeleteEmptyFolder"
On Error Goto 0
End Sub 'DeleteEmptyFolder
'=======================================================================================================
'Wrapper to delete a folder and remove the empty parent folder structure
Sub SmartDeleteFolder(sFolder)
If oFso.FolderExists(sFolder) Then
If Not fDetectOnly Then
LogOnly " Request SmartDelete for folder: " & sFolder
SmartDeleteFolderEx sFolder
Else
LogOnly " Simulate request SmartDelete for folder: " & sFolder
End If
End If
If f64 AND oFso.FolderExists(Wow64Folder(sFolder)) Then
If Not fDetectOnly Then
LogOnly "Request SmartDelete for folder: " & Wow64Folder(sFolder)
SmartDeleteFolderEx Wow64Folder(sFolder)
Else
LogOnly "Simulate request SmartDelete for folder: " & Wow64Folder(sFolder)
End If
End If
End Sub 'SmartDeleteFolder
'=======================================================================================================
'Executes the folder delete operation
Sub SmartDeleteFolderEx(sFolder)
Dim Folder
On Error Resume Next
DeleteFolder sFolder : CheckError "SmartDeleteFolderEx"
On Error Goto 0
Set Folder = oFso.GetFolder(oFso.GetParentFolderName(sFolder))
If (Folder.Subfolders.Count = 0) AND (Folder.Files.Count = 0) Then SmartDeleteFolderEx(Folder.Path)
End Sub 'SmartDeleteFolderEx
'=======================================================================================================
'Adds the folder structure to the 'KeepFolder' dictionary
Sub AddKeepFolder(sPath)
Dim Folder
'Ensure trailing "\"
sPath = LCase(sPath) & "\"
While InStr(sPath,"\\")>0
sPath = Replace(sPath,"\\","\")
Wend
If NOT dictionaryKeepFolder.Exists (sPath) Then
dictionaryKeepFolder.Add sPath,sPath
Else
Exit Sub
End If
sPath = LCase(oFso.GetParentFolderName(sPath)) & "\"
If oFso.FolderExists(sPath) Then AddKeepFolder(sPath)
End Sub
'=======================================================================================================
'Handles additional folder-path operations on 64 bit environments
Function Wow64Folder(sFolder)
If LCase(Left(sFolder,Len(sWinDir & "\System32"))) = LCase(sWinDir & "\System32") Then
Wow64Folder = sWinDir & "\syswow64" & Right(sFolder,Len(sFolder)-Len(sSys32Dir))
ElseIf LCase(Left(sFolder,Len(sProgramFiles))) = LCase(sProgramFiles) Then
Wow64Folder = sProgramFilesX86 & Right(sFolder,Len(sFolder)-Len(sProgramFiles))
Else
Wow64Folder = "?" 'Return invalid string to ensure the folder cannot exist
End If
End Function 'Wow64Folder
'=======================================================================================================
Function HiveString(hDefKey)
On Error Resume Next
Select Case hDefKey
Case HKCR : HiveString = "HKEY_CLASSES_ROOT"
Case HKCU : HiveString = "HKEY_CURRENT_USER"
Case HKLM : HiveString = "HKEY_LOCAL_MACHINE"
Case HKU : HiveString = "HKEY_USERS"
Case Else : HiveString = hDefKey
End Select
End Function
'=======================================================================================================
Function RegKeyExists(hDefKey,sSubKeyName)
Dim arrKeys
RegKeyExists = False
If oReg.EnumKey(hDefKey,sSubKeyName,arrKeys) = 0 Then RegKeyExists = True
End Function
'=======================================================================================================
Function RegValExists(hDefKey,sSubKeyName,sName)
Dim arrValueTypes, arrValueNames
Dim i
RegValExists = False
If Not RegKeyExists(hDefKey,sSubKeyName) Then Exit Function
If sName = "" Then
RegValExists = True
Exit Function
End If
If oReg.EnumValues(hDefKey,sSubKeyName,arrValueNames,arrValueTypes) = 0 AND IsArray(arrValueNames) Then
For i = 0 To UBound(arrValueNames)
If LCase(arrValueNames(i)) = Trim(LCase(sName)) Then RegValExists = True
Next
End If 'oReg.EnumValues
End Function
'=======================================================================================================
'Read the value of a given registry entry
Function RegReadValue(hDefKey, sSubKeyName, sName, sValue, sType)
Dim RetVal
Dim Item
Dim arrValues
Select Case UCase(sType)
Case "1","REG_SZ"
RetVal = oReg.GetStringValue(hDefKey,sSubKeyName,sName,sValue)
If Not RetVal = 0 AND f64 Then RetVal = oReg.GetStringValue(hDefKey,Wow64Key(hDefKey, sSubKeyName),sName,sValue)
Case "2","REG_EXPAND_SZ"
RetVal = oReg.GetExpandedStringValue(hDefKey,sSubKeyName,sName,sValue)
If Not RetVal = 0 AND f64 Then RetVal = oReg.GetExpandedStringValue(hDefKey,Wow64Key(hDefKey, sSubKeyName),sName,sValue)
Case "7","REG_MULTI_SZ"
RetVal = oReg.GetMultiStringValue(hDefKey,sSubKeyName,sName,arrValues)
If Not RetVal = 0 AND f64 Then RetVal = oReg.GetMultiStringValue(hDefKey,Wow64Key(hDefKey, sSubKeyName),sName,arrValues)
If RetVal = 0 Then sValue = Join(arrValues,chr(34))
Case "4","REG_DWORD"
RetVal = oReg.GetDWORDValue(hDefKey,sSubKeyName,sName,sValue)
If Not RetVal = 0 AND f64 Then
RetVal = oReg.GetDWORDValue(hDefKey,Wow64Key(hDefKey, sSubKeyName),sName,sValue)
End If
Case "3","REG_BINARY"
RetVal = oReg.GetBinaryValue(hDefKey,sSubKeyName,sName,sValue)
If Not RetVal = 0 AND f64 Then RetVal = oReg.GetBinaryValue(hDefKey,Wow64Key(hDefKey, sSubKeyName),sName,sValue)
Case "11","REG_QWORD"
RetVal = oReg.GetQWORDValue(hDefKey,sSubKeyName,sName,sValue)
If Not RetVal = 0 AND f64 Then RetVal = oReg.GetQWORDValue(hDefKey,Wow64Key(hDefKey, sSubKeyName),sName,sValue)
Case Else
RetVal = -1
End Select 'sValue
RegReadValue = (RetVal = 0)
End Function 'RegReadValue
'=======================================================================================================
'Enumerate a registry key to return all values
Function RegEnumValues(hDefKey,sSubKeyName,arrNames, arrTypes)
Dim RetVal, RetVal64
Dim arrNames32, arrNames64, arrTypes32, arrTypes64
If f64 Then
RetVal = oReg.EnumValues(hDefKey,sSubKeyName,arrNames32,arrTypes32)
RetVal64 = oReg.EnumValues(hDefKey,Wow64Key(hDefKey, sSubKeyName),arrNames64,arrTypes64)
If (RetVal = 0) AND (Not RetVal64 = 0) AND IsArray(arrNames32) AND IsArray(arrTypes32) Then
arrNames = arrNames32
arrTypes = arrTypes32
End If
If (Not RetVal = 0) AND (RetVal64 = 0) AND IsArray(arrNames64) AND IsArray(arrTypes64) Then
arrNames = arrNames64
arrTypes = arrTypes64
End If
If (RetVal = 0) AND (RetVal64 = 0) AND IsArray(arrNames32) AND IsArray(arrNames64) AND IsArray(arrTypes32) AND IsArray(arrTypes64) Then
arrNames = RemoveDuplicates(Split((Join(arrNames32,"\") & "\" & Join(arrNames64,"\")),"\"))
arrTypes = RemoveDuplicates(Split((Join(arrTypes32,"\") & "\" & Join(arrTypes64,"\")),"\"))
End If
Else
RetVal = oReg.EnumValues(hDefKey,sSubKeyName,arrNames,arrTypes)
End If 'f64
RegEnumValues = ((RetVal = 0) OR (RetVal64 = 0)) AND IsArray(arrNames) AND IsArray(arrTypes)
End Function 'RegEnumValues
'=======================================================================================================
'Enumerate a registry key to return all subkeys
Function RegEnumKey(hDefKey,sSubKeyName,arrKeys)
Dim RetVal, RetVal64
Dim arrKeys32, arrKeys64
If f64 Then
RetVal = oReg.EnumKey(hDefKey,sSubKeyName,arrKeys32)
RetVal64 = oReg.EnumKey(hDefKey,Wow64Key(hDefKey, sSubKeyName),arrKeys64)
If (RetVal = 0) AND (Not RetVal64 = 0) AND IsArray(arrKeys32) Then arrKeys = arrKeys32
If (Not RetVal = 0) AND (RetVal64 = 0) AND IsArray(arrKeys64) Then arrKeys = arrKeys64
If (RetVal = 0) AND (RetVal64 = 0) Then
If IsArray(arrKeys32) AND IsArray (arrKeys64) Then
arrKeys = RemoveDuplicates(Split((Join(arrKeys32,"\") & "\" & Join(arrKeys64,"\")),"\"))
ElseIf IsArray(arrKeys64) Then
arrKeys = arrKeys64
Else
arrKeys = arrKeys32
End If
End If
Else
RetVal = oReg.EnumKey(hDefKey,sSubKeyName,arrKeys)
End If 'f64
RegEnumKey = ((RetVal = 0) OR (RetVal64 = 0)) AND IsArray(arrKeys)
End Function 'RegEnumKey
'=======================================================================================================
'Wrapper around oReg.DeleteValue to handle 64 bit
Sub RegDeleteValue(hDefKey, sSubKeyName, sName, fRegMultiSZ)
Dim sWow64Key,sRealName
Dim iRetVal
sRealName = sName
If UCase(sName) = "(DEFAULT)" Then sRealName = ""
If dictionaryKeepReg.Exists(LCase(sSubKeyName & sName)) Then
If NOT fForce Then
LogOnly " - Disallowing the delete of still required keypath element: " & HiveString(hDefKey) & "\" & sSubKeyName & sName
Exit Sub
Else
LogOnly " - Enforced delete of still required keypath element. Remaining applications will need a repair!"
End If
End If
If f64 Then
If dictionaryKeepReg.Exists(LCase(Wow64Key(hDefKey, sSubKeyName) & sName)) Then
If NOT fForce Then
LogOnly " - Disallowing the delete of still required keypath element: " & HiveString(hDefKey) & "\" & sSubKeyName & sName
Exit Sub
Else
LogOnly " - Enforced delete of still required keypath element. Remaining applications will need a repair!"
End If
End If
End If
If RegValExists(hDefKey,sSubKeyName,sRealName) Then
On Error Resume Next
If RegReadValue(hDefKey,sSubKeyName,sName,sValue,"REG_MULTI_SZ") Then
LogOnly " - Disallowing unsafe delete of REG_MULTI_SZ: " & HiveString(hDefKey) & "\" & sSubKeyName & sName
Exit Sub
End If
If Not fDetectOnly Then
LogOnly " - Delete registry value: " & HiveString(hDefKey) & "\" & sSubKeyName & " -> " & sName
iRetVal = 0
iRetVal = oReg.DeleteValue(hDefKey, sSubKeyName, sRealName)
CheckError "RegDeleteValue"
If NOT (iRetVal=0) Then LogOnly " Delete failed. Return value: "&iRetVal
Else
LogOnly " - Simulate delete registry value: " & HiveString(hDefKey) & "\" & sSubKeyName & " -> " & sName
End If
On Error Goto 0
End If 'RegValExists
If f64 Then
sWow64Key = Wow64Key(hDefKey, sSubKeyName)
If RegValExists(hDefKey,sWow64Key,sRealName) Then
On Error Resume Next
If RegReadValue(hDefKey,sSubKeyName,sName,sValue,"REG_MULTI_SZ") Then
LogOnly " - Disallowing unsafe delete of REG_MULTI_SZ: " & HiveString(hDefKey) & "\" & sSubKeyName & sName
Exit Sub
End If
If Not fDetectOnly Then
LogOnly " - Delete registry value: " & HiveString(hDefKey) & "\" & sWow64Key & " -> " & sName
iRetVal = 0
iRetVal = oReg.DeleteValue(hDefKey, sWow64Key, sRealName)
CheckError "RegDeleteValue"
If NOT (iRetVal=0) Then LogOnly " Delete failed. Return value: "&iRetVal
Else
LogOnly " - Simulate delete registry value: " & HiveString(hDefKey) & "\" & sWow64Key & " -> " & sName
End If
On Error Goto 0
End If 'RegKeyExists
End If
End Sub 'RegDeleteValue
'=======================================================================================================
'Wrappper around RegDeleteKeyEx to handle 64bit scenrios
Sub RegDeleteKey(hDefKey, sSubKeyName)
Dim sWow64Key
'Ensure trailing "\"
sSubKeyName = sSubKeyName & "\"
While InStr(sSubKeyName,"\\")>0
sSubKeyName = Replace(sSubKeyName,"\\","\")
Wend
If dictionaryKeepReg.Exists(LCase(sSubKeyName)) Then
If NOT fForce Then
LogOnly " - Disallowing the delete of still required keypath element: " & HiveString(hDefKey) & "\" & sSubKeyName
Exit Sub
Else
LogOnly " - Enforced delete of still required keypath element. Remaining applications will need a repair!"
End If
End If
If f64 Then
If dictionaryKeepReg.Exists(LCase(Wow64Key(hDefKey, sSubKeyName))) Then
If NOT fForce Then
LogOnly " - Disallowing the delete of still required keypath element: " & HiveString(hDefKey) & "\" & sSubKeyName
Exit Sub
Else
LogOnly " - Enforced delete of still required keypath element. Remaining applications will need a repair!"
End If
End If
End If
If Len(sSubKeyName) > 1 Then
'Strip of trailing "\"
sSubKeyName = Left(sSubKeyName,Len(sSubKeyName)-1)
End If
If RegKeyExists(hDefKey, sSubKeyName) Then
If Not fDetectOnly Then
LogOnly " - Delete registry key: " & HiveString(hDefKey) & "\" & sSubKeyName
On Error Resume Next
RegDeleteKeyEx hDefKey, sSubKeyName
On Error Goto 0
Else
LogOnly " - Simulate delete registry key: " & HiveString(hDefKey) & "\" & sSubKeyName
End If
End If 'RegKeyExists
If f64 Then
sWow64Key = Wow64Key(hDefKey, sSubKeyName)
If RegKeyExists(hDefKey,sWow64Key) Then
If Not fDetectOnly Then
LogOnly " - Delete registry key: " & HiveString(hDefKey) & "\" & sWow64Key
On Error Resume Next
RegDeleteKeyEx hDefKey, sWow64Key
On Error Goto 0
Else
LogOnly " - Simulate delete registry key: " & HiveString(hDefKey) & "\" & sWow64Key
End If
End If 'RegKeyExists
End If
End Sub 'RegDeleteKey
'=======================================================================================================
'Recursively delete a registry structure
Sub RegDeleteKeyEx(hDefKey, sSubKeyName)
Dim arrSubkeys
Dim sSubkey
Dim iRetVal
On Error Resume Next
oReg.EnumKey hDefKey, sSubKeyName, arrSubkeys
If IsArray(arrSubkeys) Then
For Each sSubkey In arrSubkeys
RegDeleteKeyEx hDefKey, sSubKeyName & "\" & sSubkey
Next
End If
If Not fDetectOnly Then
iRetVal = 0
iRetVal = oReg.DeleteKey(hDefKey,sSubKeyName)
If NOT (iRetVal=0) Then LogOnly " Delete failed. Return value: "&iRetVal
End If
End Sub 'RegDeleteKeyEx
'=======================================================================================================
'Return the alternate regkey location on 64bit environment
Function Wow64Key(hDefKey, sSubKeyName)
Dim iPos
Select Case hDefKey
Case HKCU
If Left(sSubKeyName,17) = "Software\Classes\" Then
Wow64Key = Left(sSubKeyName,17) & "Wow6432Node\" & Right(sSubKeyName,Len(sSubKeyName)-17)
Else
iPos = InStr(sSubKeyName,"\")
Wow64Key = Left(sSubKeyName,iPos) & "Wow6432Node\" & Right(sSubKeyName,Len(sSubKeyName)-iPos)
End If
Case HKLM
If Left(sSubKeyName,17) = "Software\Classes\" Then
Wow64Key = Left(sSubKeyName,17) & "Wow6432Node\" & Right(sSubKeyName,Len(sSubKeyName)-17)
Else
iPos = InStr(sSubKeyName,"\")
Wow64Key = Left(sSubKeyName,iPos) & "Wow6432Node\" & Right(sSubKeyName,Len(sSubKeyName)-iPos)
End If
Case Else
Wow64Key = "Wow6432Node\" & sSubKeyName
End Select 'hDefKey
End Function 'Wow64Key
'=======================================================================================================
'Remove duplicate entries from a one dimensional array
Function RemoveDuplicates(Array)
Dim Item
Dim oDic
Set oDic = CreateObject("Scripting.Dictionary")
For Each Item in Array
If Not oDic.Exists(Item) Then oDic.Add Item,Item
Next 'Item
RemoveDuplicates = oDic.Keys
End Function 'RemoveDuplicates
'=======================================================================================================
'End running instances of setup
Sub EndCurrentInstalls ()
Dim Processes, Process
Dim iRet
Set Processes = oWmiLocal.ExecQuery("Select * From Win32_Process Where Name like '%setup%' OR Name like '%install%'")
For Each Process in Processes
Log " - End process " & Process.Name
iRet = Process.Terminate()
CheckError "EndCurrentInstalls: " & "Process.Name"
Next 'Process
StopService "msiserver"
End Sub 'EndCurrentInstalls
'=======================================================================================================
'Uses WMI to stop a service
Function StopService(sService)
Dim Services, Service
Dim sQuery
Dim iRet
On Error Resume Next
iRet = 0
sQuery = "Select * From Win32_Service Where Name='" & sService & "'"
Set Services = oWmiLocal.Execquery(sQuery)
'Stop the service
For Each Service in Services
If UCase(Service.State) = "STARTED" Then iRet = Service.StopService
If UCase(Service.State) = "RUNNING" Then iRet = Service.StopService
Next 'Service
StopService = (iRet = 0)
End Function 'StopService
'=======================================================================================================
'Delete a service
Sub DeleteService(sService)
Dim Services, Service, Processes, Process
Dim sQuery, sStates
Dim iRet
On Error Resume Next
sStates = "STARTED;RUNNING"
sQuery = "Select * From Win32_Service Where Name='" & sService & "'"
Set Services = oWmiLocal.Execquery(sQuery)
'Stop and delete the service
For Each Service in Services
Log " Found service " & sService & " in state " & Service.State
If InStr(sStates,UCase(Service.State))>0 Then iRet = Service.StopService()
'Ensure no more instances of the service are running
Set Processes = oWmiLocal.ExecQuery("Select * From Win32_Process Where Name='" & sService & ".exe'")
For Each Process in Processes
iRet = Process.Terminate()
Next 'Process
If Not fDetectOnly Then
Log " - Deleting Service -> " & sService
iRet = Service.Delete()
Else
Log " - Simulate deleting Service -> " & sService
End If
Next 'Service
Set Services = Nothing
Err.Clear
End Sub 'DeleteService
'=======================================================================================================
'Translation for setup.exe error codes
Function SetupRetVal(RetVal)
Select Case RetVal
Case 0 : SetupRetVal = "Success"
Case 30001,1 : SetupRetVal = "AbstractMethod"
Case 30002,2 : SetupRetVal = "ApiProhibited"
Case 30003,3 : SetupRetVal = "AlreadyImpersonatingAUser"
Case 30004,4 : SetupRetVal = "AlreadyInitialized"
Case 30005,5 : SetupRetVal = "ArgumentNullException"
Case 30006,6 : SetupRetVal = "AssertionFailed"
Case 30007,7 : SetupRetVal = "CABFileAddFailed"
Case 30008,8 : SetupRetVal = "CommandFailed"
Case 30009,9 : SetupRetVal = "ConcatenationFailed"
Case 30010,10 : SetupRetVal = "CopyFailed"
Case 30011,11 : SetupRetVal = "CreateEventFailed"
Case 30012,12 : SetupRetVal = "CustomizationPatchNotFound"
Case 30013,13 : SetupRetVal = "CustomizationPatchNotApplicable"
Case 30014,14 : SetupRetVal = "DuplicateDefinition"
Case 30015,15 : SetupRetVal = "ErrorCodeOnly - Passthrough for Win32 error"
Case 30016,16 : SetupRetVal = "ExceptionNotThrown"
Case 30017,17 : SetupRetVal = "FailedToImpersonateUser"
Case 30018,18 : SetupRetVal = "FailedToInitializeFlexDataSource"
Case 30019,19 : SetupRetVal = "FailedToStartClassFactories"
Case 30020,20 : SetupRetVal = "FileNotFound"
Case 30021,21 : SetupRetVal = "FileNotOpen"
Case 30022,22 : SetupRetVal = "FlexDialogAlreadyInitialized"
Case 30023,23 : SetupRetVal = "HResultOnly - Passthrough for HRESULT errors"
Case 30024,24 : SetupRetVal = "HWNDNotFound"
Case 30025,25 : SetupRetVal = "IncompatibleCacheAction"
Case 30026,26 : SetupRetVal = "IncompleteProductAddOns"
Case 30027,27 : SetupRetVal = "InstalledProductStateCorrupt"
Case 30028,28 : SetupRetVal = "InsufficientBuffer"
Case 30029,29 : SetupRetVal = "InvalidArgument"
Case 30030,30 : SetupRetVal = "InvalidCDKey"
Case 30031,31 : SetupRetVal = "InvalidColumnType"
Case 30032,31 : SetupRetVal = "InvalidConfigAddLanguage"
Case 30033,33 : SetupRetVal = "InvalidData"
Case 30034,34 : SetupRetVal = "InvalidDirectory"
Case 30035,35 : SetupRetVal = "InvalidFormat"
Case 30036,36 : SetupRetVal = "InvalidInitialization"
Case 30037,37 : SetupRetVal = "InvalidMethod"
Case 30038,38 : SetupRetVal = "InvalidOperation"
Case 30039,39 : SetupRetVal = "InvalidParameter"
Case 30040,40 : SetupRetVal = "InvalidProductFromARP"
Case 30041,41 : SetupRetVal = "InvalidProductInConfigXml"
Case 30042,42 : SetupRetVal = "InvalidReference"
Case 30043,43 : SetupRetVal = "InvalidRegistryValueType"
Case 30044,44 : SetupRetVal = "InvalidXMLProperty"
Case 30045,45 : SetupRetVal = "InvalidMetadataFile"
Case 30046,46 : SetupRetVal = "LogNotInitialized"
Case 30047,47 : SetupRetVal = "LogAlreadyInitialized"
Case 30048,48 : SetupRetVal = "MissingXMLNode"
Case 30049,49 : SetupRetVal = "MsiTableNotFound"
Case 30050,50 : SetupRetVal = "MsiAPICallFailure"
Case 30051,51 : SetupRetVal = "NodeNotOfTypeElement"
Case 30052,52 : SetupRetVal = "NoMoreGraceBoots"
Case 30053,53 : SetupRetVal = "NoProductsFound"
Case 30054,54 : SetupRetVal = "NoSupportedCulture"
Case 30055,55 : SetupRetVal = "NotYetImplemented"
Case 30056,56 : SetupRetVal = "NotAvailableCulture"
Case 30057,57 : SetupRetVal = "NotCustomizationPatch"
Case 30058,58 : SetupRetVal = "NullReference"
Case 30059,59 : SetupRetVal = "OCTPatchForbidden"
Case 30060,60 : SetupRetVal = "OCTWrongMSIDll"
Case 30061,61 : SetupRetVal = "OutOfBoundsIndex"
Case 30062,62 : SetupRetVal = "OutOfDiskSpace"
Case 30063,63 : SetupRetVal = "OutOfMemory"
Case 30064,64 : SetupRetVal = "OutOfRange"
Case 30065,65 : SetupRetVal = "PatchApplicationFailure"
Case 30066,66 : SetupRetVal = "PreReqCheckFailure"
Case 30067,67 : SetupRetVal = "ProcessAlreadyStarted"
Case 30068,68 : SetupRetVal = "ProcessNotStarted"
Case 30069,69 : SetupRetVal = "ProcessNotFinished"
Case 30070,70 : SetupRetVal = "ProductAlreadyDefined"
Case 30071,71 : SetupRetVal = "ResourceAlreadyTracked"
Case 30072,72 : SetupRetVal = "ResourceNotFound"
Case 30073,73 : SetupRetVal = "ResourceNotTracked"
Case 30074,74 : SetupRetVal = "SQLAlreadyConnected"
Case 30075,75 : SetupRetVal = "SQLFailedToAllocateHandle"
Case 30076,76 : SetupRetVal = "SQLFailedToConnect"
Case 30077,77 : SetupRetVal = "SQLFailedToExecuteStatement"
Case 30078,78 : SetupRetVal = "SQLFailedToRetrieveData"
Case 30079,79 : SetupRetVal = "SQLFailedToSetAttribute"
Case 30080,80 : SetupRetVal = "StorageNotCreated"
Case 30081,81 : SetupRetVal = "StreamNameTooLong"
Case 30082,82 : SetupRetVal = "SystemError"
Case 30083,83 : SetupRetVal = "ThreadAlreadyStarted"
Case 30084,84 : SetupRetVal = "ThreadNotStarted"
Case 30085,85 : SetupRetVal = "ThreadNotFinished"
Case 30086,86 : SetupRetVal = "TooManyProducts"
Case 30087,87 : SetupRetVal = "UnexpectedXMLNodeType"
Case 30088,88 : SetupRetVal = "UnexpectedError"
Case 30089,89 : SetupRetVal = "Unitialized"
Case 30090,90 : SetupRetVal = "UserCancel"
Case 30091,91 : SetupRetVal = "ExternalCommandFailed"
Case 30092,92 : SetupRetVal = "SPDatabaseOverSize"
Case 30093,93 : SetupRetVal = "IntegerTruncation"
'msiexec return values
Case 1259 : SetupRetVal = "APPHELP_BLOCK"
Case 1601 : SetupRetVal = "INSTALL_SERVICE_FAILURE"
Case 1602 : SetupRetVal = "INSTALL_USEREXIT"
Case 1603 : SetupRetVal = "INSTALL_FAILURE"
Case 1604 : SetupRetVal = "INSTALL_SUSPEND"
Case 1605 : SetupRetVal = "UNKNOWN_PRODUCT"
Case 1606 : SetupRetVal = "UNKNOWN_FEATURE"
Case 1607 : SetupRetVal = "UNKNOWN_COMPONENT"
Case 1608 : SetupRetVal = "UNKNOWN_PROPERTY"
Case 1609 : SetupRetVal = "INVALID_HANDLE_STATE"
Case 1610 : SetupRetVal = "BAD_CONFIGURATION"
Case 1611 : SetupRetVal = "INDEX_ABSENT"
Case 1612 : SetupRetVal = "INSTALL_SOURCE_ABSENT"
Case 1613 : SetupRetVal = "INSTALL_PACKAGE_VERSION"
Case 1614 : SetupRetVal = "PRODUCT_UNINSTALLED"
Case 1615 : SetupRetVal = "BAD_QUERY_SYNTAX"
Case 1616 : SetupRetVal = "INVALID_FIELD"
Case 1618 : SetupRetVal = "INSTALL_ALREADY_RUNNING"
Case 1619 : SetupRetVal = "INSTALL_PACKAGE_OPEN_FAILED"
Case 1620 : SetupRetVal = "INSTALL_PACKAGE_INVALID"
Case 1621 : SetupRetVal = "INSTALL_UI_FAILURE"
Case 1622 : SetupRetVal = "INSTALL_LOG_FAILURE"
Case 1623 : SetupRetVal = "INSTALL_LANGUAGE_UNSUPPORTED"
Case 1624 : SetupRetVal = "INSTALL_TRANSFORM_FAILURE"
Case 1625 : SetupRetVal = "INSTALL_PACKAGE_REJECTED"
Case 1626 : SetupRetVal = "FUNCTION_NOT_CALLED"
Case 1627 : SetupRetVal = "FUNCTION_FAILED"
Case 1628 : SetupRetVal = "INVALID_TABLE"
Case 1629 : SetupRetVal = "DATATYPE_MISMATCH"
Case 1630 : SetupRetVal = "UNSUPPORTED_TYPE"
Case 1631 : SetupRetVal = "CREATE_FAILED"
Case 1632 : SetupRetVal = "INSTALL_TEMP_UNWRITABLE"
Case 1633 : SetupRetVal = "INSTALL_PLATFORM_UNSUPPORTED"
Case 1634 : SetupRetVal = "INSTALL_NOTUSED"
Case 1635 : SetupRetVal = "PATCH_PACKAGE_OPEN_FAILED"
Case 1636 : SetupRetVal = "PATCH_PACKAGE_INVALID"
Case 1637 : SetupRetVal = "PATCH_PACKAGE_UNSUPPORTED"
Case 1638 : SetupRetVal = "PRODUCT_VERSION"
Case 1639 : SetupRetVal = "INVALID_COMMAND_LINE"
Case 1640 : SetupRetVal = "INSTALL_REMOTE_DISALLOWED"
Case 1641 : SetupRetVal = "SUCCESS_REBOOT_INITIATED"
Case 1642 : SetupRetVal = "PATCH_TARGET_NOT_FOUND"
Case 1643 : SetupRetVal = "PATCH_PACKAGE_REJECTED"
Case 1644 : SetupRetVal = "INSTALL_TRANSFORM_REJECTED"
Case 1645 : SetupRetVal = "INSTALL_REMOTE_PROHIBITED"
Case 1646 : SetupRetVal = "PATCH_REMOVAL_UNSUPPORTED"
Case 1647 : SetupRetVal = "UNKNOWN_PATCH"
Case 1648 : SetupRetVal = "PATCH_NO_SEQUENCE"
Case 1649 : SetupRetVal = "PATCH_REMOVAL_DISALLOWED"
Case 1650 : SetupRetVal = "INVALID_PATCH_XML"
Case 3010 : SetupRetVal = "SUCCESS_REBOOT_REQUIRED"
Case Else : SetupRetVal = "Unknown Return Value"
End Select
End Function 'SetupRetVal
'=======================================================================================================
Function GetProductID(sProdID)
Dim sReturn
Select Case sProdId
Case "000F" : sReturn = "MONDO"
Case "0010" : sReturn = "WEBFLDRS"
Case "0011" : sReturn = "PROPLUS"
Case "0012" : sReturn = "STANDARD"
Case "0013" : sReturn = "BASIC"
Case "0014" : sReturn = "PRO"
Case "0015" : sReturn = "ACCESS"
Case "0016" : sReturn = "EXCEL"
Case "0017" : sReturn = "SharePointDesigner"
Case "0018" : sReturn = "PowerPoint"
Case "0019" : sReturn = "Publisher"
Case "001A" : sReturn = "Outlook"
Case "001B" : sReturn = "Word"
Case "001C" : sReturn = "AccessRuntime"
Case "001F" : sReturn = "Proof"
Case "0020" : sReturn = "O2007CNV"
Case "0021" : sReturn = "VisualWebDeveloper"
Case "0026" : sReturn = "ExpressionWeb"
Case "0029" : sReturn = "Excel"
Case "002A" : sReturn = "Office64"
Case "002B" : sReturn = "Word"
Case "002C" : sReturn = "Proofing"
Case "002E" : sReturn = "Ultimate"
Case "002F" : sReturn = "HomeAndStudent"
Case "0028" : sReturn = "IME"
Case "0030" : sReturn = "Enterprise"
Case "0031" : sReturn = "ProfessionalHybrid"
Case "0033" : sReturn = "Personal"
Case "0035" : sReturn = "ProfessionalHybrid"
Case "0037" : sReturn = "PowerPoint"
Case "0038" : sReturn = "OlTimeZoneTool"
Case "003A" : sReturn = "PrjStd"
Case "003B" : sReturn = "PrjPro"
Case "003D" : sReturn = "SINGLEIMAGE"
Case "0043" : sReturn = "OFFICE32"
Case "0044" : sReturn = "InfoPath"
Case "0045" : sReturn = "XWEB"
Case "0048" : sReturn = "OLC"
Case "0049" : sReturn = "ACADEMIC"
Case "004A" : sReturn = "OWC11"
Case "0051" : sReturn = "VISPRO"
Case "0052" : sReturn = "VisView"
Case "0053" : sReturn = "VisStd"
Case "0054" : sReturn = "VisMUI"
Case "0055" : sReturn = "VisMUI"
Case "0057" : sReturn = "VISIO"
Case "0061" : sReturn = "CLICK2RUN"
Case "0062" : sReturn = "CLICK2RUN"
Case "0066" : sReturn = "CLICK2RUN"
Case "006C" : sReturn = "CLICK2RUN"
Case "006D" : sReturn = "CLICK2RUN"
Case "006E" : sReturn = "Shared"
Case "006F" : sReturn = "OFFICE"
Case "0070" : sReturn = "OOBE"
Case "0074" : sReturn = "STARTER"
Case "007A" : sReturn = "OLC" 'Outlook Connector
Case "007C" : sReturn = "OSCFB" 'Outlook Social Connector for FaceBook
Case "007D" : sReturn = "OSCWL" 'Outlook Social Connector for Windows Live Messenger
Case "007F" : sReturn = "OLC" 'Outlook Social Connector
Case "008A" : sReturn = "RecentDocs"
Case "008B" : sReturn = "SmallBusinessBasics"
Case "00A1" : sReturn = "ONENOTE"
Case "00A3" : sReturn = "OneNoteHomeStudent"
Case "00A4" : sReturn = "OWC11"
Case "00A7" : sReturn = "CPAO"
Case "00A9" : sReturn = "InterConnect"
Case "00AF" : sReturn = "PPtView"
Case "00B0" : sReturn = "ExPdf"
Case "00B1" : sReturn = "ExXps"
Case "00B2" : sReturn = "ExPdfXps"
Case "00B4" : sReturn = "PrjMUI"
Case "00B5" : sReturn = "PrjtMUI"
Case "00B9" : sReturn = "AER"
Case "00BA" : sReturn = "Groove"
Case "00CA" : sReturn = "SmallBusiness"
Case "00E0" : sReturn = "Outlook"
Case "00D1" : sReturn = "ACE"
Case "0100" : sReturn = "OfficeMUI"
Case "0101" : sReturn = "OfficeXMUI"
Case "0103" : sReturn = "PTK"
Case "0114" : sReturn = "GrooveSetupMetadata"
Case "0115" : sReturn = "SharedSetupMetadata"
Case "0116" : sReturn = "SharedSetupMetadata"
Case "0117" : sReturn = "AccessSetupMetadata"
Case "011A" : sReturn = "SendASmile"
Case "011D" : sReturn = "ProPlusSubscription"
Case "011F" : sReturn = "OLConnect"
Case "0126" : sReturn = "WWLIBCXM"
Case "1014" : sReturn = "STS"
Case "1015" : sReturn = "WSSMUI"
Case "1032" : sReturn = "PJSVRAPP"
Case "104B" : sReturn = "SPS"
Case "104E" : sReturn = "SPSMUI"
Case "107F" : sReturn = "OSrv"
Case "1080" : sReturn = "OSrv"
Case "1088" : sReturn = "lpsrvwfe"
Case "10D7" : sReturn = "IFS"
Case "10D8" : sReturn = "IFSMUI"
Case "10EB" : sReturn = "DLCAPP"
Case "10F5" : sReturn = "XLSRVAPP"
Case "10F6" : sReturn = "XlSrvWFE"
Case "10F7" : sReturn = "DLC"
Case "10F8" : sReturn = "SlSrvMui"
Case "10FB" : sReturn = "OSrchWFE"
Case "10FC" : sReturn = "OSRCHAPP"
Case "10FD" : sReturn = "OSrchMUI"
Case "1103" : sReturn = "DLC"
Case "1104" : sReturn = "LHPSRV"
Case "1105" : sReturn = "PIA"
Case "1106" : sReturn = "GRVMGMTSRV"
Case "1109" : sReturn = "GSERVERRELAY"
Case "110D" : sReturn = "OSERVER"
Case "110F" : sReturn = "PSERVER"
Case "1110" : sReturn = "WSS"
Case "1121" : sReturn = "SPSSDK"
Case "1122" : sReturn = "SPSDev"
Case "1163" : sReturn = "SCC" 'SharePoint Client Components
Case Else : sReturn = sProdID
End Select 'sProdId
GetProductID = sReturn
End Function 'GetProductID
'=======================================================================================================
Sub Log (sLog)
If NOT fQuiet AND fCScript Then wscript.echo sLog
LogStream.WriteLine sLog
End Sub 'Log
'=======================================================================================================
Sub LogOnly (sLog)
LogStream.WriteLine sLog
End Sub 'Log
'=======================================================================================================
Sub CheckError(sModule)
If Err <> 0 Then
LogOnly " " & Now & " - " & sModule & " - Source: " & Err.Source & "; Err# (Hex): " & Hex( Err ) & _
"; Err# (Dec): " & Err & "; Description : " & Err.Description
End If 'Err = 0
Err.Clear
End Sub
'=======================================================================================================
'Command line parser
Sub ParseCmdLine
Dim iCnt, iArgCnt
Dim arrArguments
Dim sArg0, sArguments
iArgCnt = Wscript.Arguments.Count
If iArgCnt > 1 Then
If wscript.Arguments(1) = "UAC" Then
If wscript.arguments.count = 2 Then iArgCnt = 0
End If
End If
sArguments = ""
If iArgCnt = 0 Then
If sDefault = "" Then
'Create the log
CreateLog
Log "No argument specified. Preparing user prompt" & vbCrLf
fPassive = False
FindInstalledOProducts
If dicInstalledSku.Count > 0 Then sDefault = Join(RemoveDuplicates(dicInstalledSku.Items),",") Else sDefault = "CLIENTALL"
sDefault = InputBox("Enter a list of " & ONAME & " products to remove" & vbCrLf & vbCrLf & _
"Examples:" & vbCrLf & _
"CLIENTALL" & vbTab & "-> all Client products" & vbCrLf & _
"SERVER" & vbTab & "-> all Server products" & vbCrLf & _
"ALL" & vbTab & vbTab & "-> all Server & Client products" & vbCrLf & _
"ProPlus,PrjPro" & vbTab & "-> ProPlus and Project" & vbCrLf &_
"?" & vbTab & vbTab & "-> display Help", _
SCRIPTFILE & " - " & ONAME & " remover", _
sDefault)
If IsEmpty(sDefault) Then 'User cancelled
Log "User cancelled. CleanUp & Exit."
'Undo temporary entries created in ARP
TmpKeyCleanUp
wscript.quit 1602
End If 'IsEmpty(sDefault)
Log "Answer from prompt: " & sDefault & vbCrLf
End If
sDefault = Trim(UCase(Trim(Replace(sDefault, Chr(34), ""))))
arrArguments = Split(Trim(sDefault), " ")
If UBound(arrArguments) = -1 Then ReDim arrArguments(0)
Else
ReDim arrArguments(iArgCnt-1)
For iCnt = 0 To (iArgCnt-1)
sArguments = sArguments & arrArguments(iCnt) & " "
arrArguments(iCnt) = UCase(Wscript.Arguments(iCnt))
Next 'iCnt
End If 'iArgCnt = 0
'Handle the SKU list
sArg0 = Replace(arrArguments(0), "/", "")
If Left(sArg0, 1) = "-" Then sArg0 = Mid(sArg0, 2)
Select Case UCase(sArg0)
Case "?"
ShowSyntax
Case "ALL"
fRemoveAll = True
fRemoveOse = False
Case "CLIENTSUITES"
fRemoveCSuites = True
fRemoveOse = False
Case "CLIENTSTANDALONE"
fRemoveCSingle = True
fRemoveOse = False
Case "CLIENTALL"
fRemoveCSuites = True
fRemoveCSingle = True
fRemoveOse = False
Case "SERVER"
fRemoveSrv = True
fRemoveOse = False
Case "ALL,OSE"
fRemoveAll = True
fRemoveOse = True
Case Else
fRemoveAll = False
fRemoveOse = False
sSkuRemoveList = sArg0
End Select
For iCnt = 0 To UBound(arrArguments)
Select Case arrArguments(iCnt)
Case "?", "/?", "-?"
ShowSyntax
Case "/B", "/BYPASS"
If UBound(arrArguments) > iCnt Then
If InStr(arrArguments(iCnt + 1), "1") > 0 Then fBypass_Stage1 = True
If InStr(arrArguments(iCnt + 1), "2") > 0 Then fBypass_Stage2 = True
If InStr(arrArguments(iCnt + 1), "3") > 0 Then fBypass_Stage3 = True
If InStr(arrArguments(iCnt + 1), "4") > 0 Then fBypass_Stage4 = True
End If
Case "/D", "/DELETEUSERSETTINGS"
fKeepUser = False
Case "/FR", "/FASTREMOVE"
fBypass_Stage1 = True
fSkipSD = True
Case "/F", "/FORCE"
fForce = True
Case "/K", "/KEEPUSERSETTINGS"
fKeepUser = True
Case "/KEEPSG", "/KEEPSOFTGRID"
fKeepSG = True
Case "/KEEPLYNC"
fRemoveLync = False
Case "/REMOVELYNC"
fRemoveLync = True
Case "/L", "/LOG"
fLogInitialized = False
If UBound(arrArguments) > iCnt Then
If oFso.FolderExists(arrArguments(iCnt + 1)) Then
sLogDir = arrArguments(iCnt + 1)
Else
On Error Resume Next
oFso.CreateFolder(arrArguments(iCnt + 1))
If Err <> 0 Then sLogDir = sScrubDir Else sLogDir = arrArguments(iCnt + 1)
End If
End If
Case "/NOCANCEL"
fNoCancel = True
Case "/NOREBOOT"
fNoReboot = True
Case "/NE", "/NOELEVATE"
fNoElevate = True
Case "/O", "/OSE"
fRemoveOse = True
Case "/P", "/PREVIEW", "/DETECTONLY"
fDetectOnly = True
Case "/PASSIVE", "/QB-"
fPassive = True
Case "/Q", "/QUIET"
fQuiet = True
Case "/QB"
fQuiet = True
fBasic = True
Case "/QND"
fBypass_Stage1 = True
fBypass_Stage2 = True
fBypass_Stage3 = True
fRemoveOse = True
fRemoveOspp = True
fRemoveAll = True
fSkipSD = True
fForce = True
Case "/R", "/RECONCILE"
fTryReconcile = True
Case "/REMOVEOSPP" , "/CLEANOSPP"
fRemoveOspp = True
Case "/S", "/SKIPSD", "/SKIPSHORTCUTDETECTION"
fSkipSD = True
Case "/SC", "/SCANCOMPONENTS"
fBypass_Stage1 = False
Case Else
End Select
Next 'iCnt
If Not fLogInitialized Then CreateLog
LogOnly "Arguments: " & sArguments & vbCrLf
End Sub 'ParseCmdLine
'=======================================================================================================
Sub CreateLog
Dim DateTime
Dim sLogName
On Error Resume Next
'Create the log file
Set DateTime = CreateObject("WbemScripting.SWbemDateTime")
DateTime.SetVarDate Now,True
sLogName = sLogDir & "\" & oWShell.ExpandEnvironmentStrings("%COMPUTERNAME%")
sLogName = sLogName & "_" & Left(DateTime.Value,14)
sLogName = sLogName & "_ScrubLog.txt"
Err.Clear
Set LogStream = oFso.CreateTextFile(sLogName,True,True)
If Err <> 0 Then
Err.Clear
sLogDir = sScrubDir
sLogName = sLogDir & "\" & oWShell.ExpandEnvironmentStrings("%COMPUTERNAME%")
sLogName = sLogName & "_" & Left(DateTime.Value,14)
sLogName = sLogName & "_ScrubLog.txt"
Set LogStream = oFso.CreateTextFile(sLogName,True,True)
End If
Log "Microsoft Customer Support Services - " & ONAME & " Removal Utility" & vbCrLf & vbCrLf & _
"OffScrub Version: " & SCRIPTVERSION & vbCrLf & _
"64 bit OS: " & f64
LogOnly "OS Details: " & sOSinfo & vbCrLf
Log "Start removal: " & Now & vbCrLf
fLogInitialized = True
End Sub 'CreateLog
'=======================================================================================================
'-------------------------------------------------------------------------------
' RelaunchAs64Host
'
' Relaunch self with 64 bit WScript host
'-------------------------------------------------------------------------------
Sub RelaunchAs64Host
Dim Argument, sCmd
sCmd = Replace (LCase(wscript.Path), "syswow64", "sysnative") & "\cscript.exe " & Chr(34) & WScript.scriptFullName & Chr(34)
If fQuiet AND NOT fCScript Then sCmd = Replace (sCmd, "\cscript.exe", "\wscript.exe")
If Wscript.Arguments.Count > 0 Then
For Each Argument in Wscript.Arguments
sCmd = sCmd & " " & chr(34) & Argument & chr(34)
Next 'Argument
Else
If Not sDefault = "" Then sCmd = sCmd & " " & chr(34) & sDefault & chr(34)
End If
sCmd = sCmd & " /ChangedHostBitness"
Wscript.Quit oWShell.Run(sCmd, 1, True)
End Sub 'RelaunchAs64Host
'=======================================================================================================
Sub RelaunchAsCScript
Dim Argument
Dim sCmdLine
sCmdLine = "cmd.exe /c " & WScript.Path & "\cscript.exe //NOLOGO " & Chr(34) & WScript.scriptFullName & Chr(34)
If Wscript.Arguments.Count > 0 Then
For Each Argument in Wscript.Arguments
sCmdLine = sCmdLine & " " & chr(34) & Argument & chr(34)
Next 'Argument
End If
Log "Relaunching with CScript as host. Full command: " & sCmdLine
Wscript.Quit oWShell.Run(sCmdLine, 1, True)
End Sub 'RelaunchAsCScript
'=======================================================================================================
Sub RelaunchElevated
Dim Argument
Dim sCmdLine
Dim oShell
Set oShell = CreateObject("Shell.Application")
sCmdLine = Chr(34) & WScript.scriptFullName & Chr(34)
If Wscript.Arguments.Count > 0 Then
For Each Argument in Wscript.Arguments
Select Case UCase(Argument)
Case "/Q","/QUIET"
'Don't try to relaunch in quiet mode
Exit Sub
Case "UAC"
'Already tried elevated relaunch
Exit Sub
Case Else
sCmdLine = sCmdLine & " " & chr(34) & Argument & chr(34)
End Select
Next 'Argument
Else
If Not sDefault = "" Then sCmdLine = sCmdLine & " " & chr(34) & sDefault & chr(34)
End If
oShell.ShellExecute "cscript.exe", sCmdLine & " /NoElevate UAC", "", "runas", 1
Wscript.Quit 5
End Sub 'RelaunchElevated
'=======================================================================================================
'Show the expected syntax for the script usage
Sub ShowSyntax
TmpKeyCleanUp
Wscript.Echo sErr & vbCrLf & _
SCRIPTFILE & " V " & SCRIPTVERSION & vbCrLf & _
"Copyright (c) Microsoft Corporation. All Rights Reserved" & vbCrLf & vbCrLf & _
SCRIPTFILE & " helps to remove " & ONAME & " Server & Client products" & vbCrLf & _
"when a regular uninstall is no longer possible" & vbCrLf & vbCrLf & _
"Usage:" & vbTab & SCRIPTFILE & " [List of config ProductIDs] [Options]" & vbCrLf & vbCrLf & _
vbTab & "/? ' Displays this help"& vbCrLf &_
vbTab & "/Log [LogfolderPath] ' Custom folder for log files" & vbCrLf & _
vbTab & "/SkipSD ' Skips the ShortcutDetection in local profiles" & vbCrLf & _
vbTab & "/NoCancel ' Setup.exe and Msiexec.exe have no Cancel button" & vbCrLf &_
vbTab & "/Quiet ' Script, Setup.exe and Msiexec.exe run quiet with no UI" & vbCrLf &_
vbTab & "/Preview ' Run this script to preview what would get removed"& vbCrLf & vbCrLf & _
"Examples:"& vbCrLf & _
vbTab & SCRIPTFILE & " CLIENTSUITES ' Remove all " & ONAME & " Client suites/products" & vbCrLf &_
vbTab & SCRIPTFILE & " CLIENTALL ' Remove all " & ONAME & " Client products" & vbCrLf &_
vbTab & SCRIPTFILE & " SERVER ' Remove all " & ONAME & " Server products" & vbCrLf &_
vbTab & SCRIPTFILE & " ALL ' Remove all " & ONAME & " Server & Client products" & vbCrLf &_
vbTab & SCRIPTFILE & " ProPlus,PrjPro ' Remove ProPlus and Project" & vbCrLf
Wscript.Quit
End Sub 'ShowSyntax
'=======================================================================================================
'*******************************************************************************
' Name: OffScrubC2R.vbs
' Author: Microsoft Customer Support Services
' Copyright (c) 2014 - 2015 Microsoft Corporation
' Script to remove Office Click To Run (C2R) products
' when a regular uninstall is no longer possible
'
' Scope: Office 2013, 2016 and O365 C2R products
'*******************************************************************************
Option Explicit
'-------------------------------------------------------------------------------
'
' Declaration of constants
'-------------------------------------------------------------------------------
Const SCRIPTVERSION = "2.00"
Const SCRIPTFILE = "OffScrubC2R.vbs"
Const SCRIPTNAME = "OffScrubC2R"
Const RETVALFILE = "ScrubRetValFile.txt"
Const ONAME = "Office C2R / O365"
Const HKCR = &H80000000
Const HKCU = &H80000001
Const HKLM = &H80000002
Const HKU = &H80000003
Const PRODLEN = 13
Const SQUISHED = 20
Const COMPRESSED = 32
Const REG_ARP = "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\"
Const VB_YES = 6
Const VB_NO = 7
Const ERROR_SUCCESS = 0 'Bit #1. 0 indicates Success. Script completed successfully
Const ERROR_FAIL = 1 'Bit #1. Failure bit. Indicates an overall script failure.
'RESERVED bit! Returned when process is killed from task manager
Const ERROR_REBOOT_REQUIRED = 2 'Bit #2. Reboot bit. If set a reboot is required
Const ERROR_USERCANCEL = 4 'Bit #3. User Cancel bit. Controlled cancel from script UI
Const ERROR_STAGE1 = 8 'Bit #4. Informational. Msiexec based install was not possible
Const ERROR_STAGE2 = 16 'Bit #5. Critical. Not all of the intended cleanup operations could be applied
Const ERROR_INCOMPLETE = 32 'Bit #6. Pending file renames (del on reboot) - OR - Removal needs to run again after a system reboot.
Const ERROR_DCAF_FAILURE = 64 'Bit #7. Critical. Da capo al fine (second attempt) still failed.
Const ERROR_ELEVATION_USERDECLINED = 128 'Bit #8. Critical script error. User declined to allow mandatory script elevation
Const ERROR_ELEVATION = 256 'Bit #9. Critical script error. The attempt to elevate the process did not succeed
Const ERROR_SCRIPTINIT = 512 'Bit #10. Critical script error. Initialization failed
Const ERROR_RELAUNCH = 1024'Bit #11. Critical script error. This is a temporary value and must not be the final return code
Const ERROR_UNKNOWN = 2048'Bit #12 Critical script error. Script did not complete in a well defined state
Const ERROR_ALL = 4095'Full BitMask
Const ERROR_USER_ABORT = &HC000013A 'RESERVED. Dec -1073741510. Critical error. Returned when user aborts with <Ctrl>+<Break> or closes the cmd window
Const ERROR_SUCCESS_CONFIG_COMPLETE = 1728
Const ERROR_SUCCESS_REBOOT_REQUIRED = 3010
'-------------------------------------------------------------------------------
'
' Declaration of variables
'-------------------------------------------------------------------------------
Dim oFso, oMsi, oReg, oWShell, oWmiLocal, oShellApp
Dim ComputerItem, Key, Item, LogStream, TmpKey
Dim arrVersion
Dim dictionaryKeepLis, dicApps, dictionaryKeepFolder, dicDelRegKey, dictionaryKeepReg
Dim dicInstalledSku, dicRemoveSku, dictionaryKeepSku, dicC2RSuite, dicDelInUse
Dim dicDelFolder
Dim sAppData, sScrubDir, sProgramFiles, sProgramFilesX86, sCommonProgramFiles
Dim sAllusersProfile, sOSVersion, sWinDir, sWICacheDir, sCommonProgramFilesX86
Dim sProgramData, sPackageFolder, sLocalAppData, sOInstallRoot, sSkuRemoveList
Dim sOSinfo, sDefault, sTemp, sTmp, sCmd, sLogDir, sPrompt, sProfilesDirectory
Dim sRetVal, sScriptDir, sPackageGuid, sValue, sActiveConfiguration
Dim iVersionNT, iError, iProcCloseCnt
Dim f64, fLogInitialized, fNoCancel, fRemoveOse, fDetectOnly, fQuiet, fForce
Dim fC2R, fRemoveAll, fRebootRequired, fRerun, fSetRunOnce, fTestRerun
Dim fIsElevated, fNoElevate, fUserConsent, fCScript
'-------------------------------------------------------------------------------
' Main
'
' Main section of script
'-------------------------------------------------------------------------------
' initialize required settings and objects
' ----------------------------------------
Initialize
' call the command line parser
'-----------------------------
ParseCmdLine
'-----------------------------
' Stage # 0 - Basic detection |
'-----------------------------
LogH "Stage # 0 " & chr(34) & "Basic detection" & chr(34)
' ensure integrity of WI metadata which could fail used APIs otherwise
'---------------------------------------------------------------------
Log "Ensure Windows Installer metadata integrity " & " (" & Time & ")"
EnsureValidWIMetadata HKCU,"Software\Classes\Installer\Products", COMPRESSED
EnsureValidWIMetadata HKCR,"Installer\Products", COMPRESSED
EnsureValidWIMetadata HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products", COMPRESSED
EnsureValidWIMetadata HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components", COMPRESSED
EnsureValidWIMetadata HKCR,"Installer\Components", COMPRESSED
' build a list with installed/registered Office products
'-------------------------------------------------------
FindInstalledOProducts
If dicC2RSuite.Count > 0 Then
Log "Registered ARP product(s) found:"
For Each Key In dicC2RSuite.Keys
Log " - " & Key & " - " & dicC2RSuite.Item(Key)
Next 'Key
' For Each Item in dicC2RSuite.Items
' Log " - " & Item
' Next 'Item
Else
Log "No registered product(s) found"
End If
' locate the C2R %PackageFolder% and the PackageGuid
'---------------------------------------------------
sPackageFolder = ""
If RegReadValue(HKLM, "SOFTWARE\Microsoft\Office\15.0\ClickToRun", "PackageFolder", sValue, "REG_SZ") Then
sPackageFolder = sValue
ElseIf RegReadValue(HKLM, "SOFTWARE\Microsoft\Office\16.0\ClickToRun", "PackageFolder", sPackageFolder, "REG_SZ") Then
sPackageFolder = sValue
ElseIf RegReadValue(HKLM, "SOFTWARE\Microsoft\Office\ClickToRun", "PackageFolder", sPackageFolder, "REG_SZ") Then
sPackageFolder = sValue
End If
' if sPackageFolder is invalid set it to the c2r registry reference string
If NOT Len(sPackageFolder) > 0 OR IsNull(sPackageFolder) Then
If oFso.FolderExists(oWShell.ExpandEnvironmentStrings("%programfiles%") & "\Microsoft Office 15") Then
sPackageFolder = oWShell.ExpandEnvironmentStrings("%programfiles%") & "\Microsoft Office 15"
ElseIf oFso.FolderExists(oWShell.ExpandEnvironmentStrings("%programfiles%") & "\Microsoft Office 16") Then
sPackageFolder = oWShell.ExpandEnvironmentStrings("%programfiles%") & "\Microsoft Office 16"
ElseIf oFso.FolderExists(oWShell.ExpandEnvironmentStrings("%programfiles%") & "\Microsoft Office\PackageManifests") Then
sPackageFolder = oWShell.ExpandEnvironmentStrings("%programfiles%") & "\Microsoft Office"
ElseIf oFso.FolderExists(oWShell.ExpandEnvironmentStrings("%programfiles(x86)%") & "\Microsoft Office\PackageManifests") Then
sPackageFolder = oWShell.ExpandEnvironmentStrings("%programfiles(x86)%") & "\Microsoft Office"
End If
End If
sPackageGuid = ""
If RegReadValue(HKLM, "SOFTWARE\Microsoft\Office\15.0\ClickToRun", "PackageGUID", sValue, "REG_SZ") Then
sPackageGuid = sValue
ElseIf RegReadValue(HKLM, "SOFTWARE\Microsoft\Office\16.0\ClickToRun", "PackageGUID", sValue, "REG_SZ") Then
sPackageGuid = sValue
ElseIf RegReadValue(HKLM, "SOFTWARE\Microsoft\Office\ClickToRun", "PackageGUID", sValue, "REG_SZ") Then
sPackageGuid = sValue
End If
' Init complete. Reset the return value
'--------------------------------------
ClearError ERROR_SCRIPTINIT
'-----------------------
' Stage # 1 - Uninstall |
'-----------------------
LogH "Stage # 1 " & chr(34) & "Uninstall" & chr(34)
' clean O15 SPP
'--------------
LogH1 "Clean OSPP"
CleanOSPP
' end all running Office applications
'------------------------------------
LogH1 "End running processes"
CloseOfficeApps
' remove scheduled tasks which might interfere with uninstall
'------------------------------------------------------------
DelSchtasks
' unpin shortcuts
'----------------
' need to unpin as long as the shortcuts are still valid!
LogH1 "Unpinning shortcuts"
CleanShortcuts sAllusersProfile, True, True
CleanShortcuts sProfilesDirectory, True, True
' uninstall
'----------
LogH1 "Removing " & ONAME
Uninstall
'---------------------
' Stage # 2 - CleanUp |
'---------------------
LogH "Stage # 2 " & chr(34) & "CleanUp" & chr(34)
' Cleanup registry data
'----------------------
RegWipe
' Cleanup files
'--------------
FileWipe
' for test purposes only!
If fTestRerun Then
LogH2 "Enforcing 'Rerun' mode for test purposes"
fRebootRequired = True
SetError ERROR_REBOOT_REQUIRED
Rerun
End If
ExitScript
Sub ExitScript
'------------------
' Stage # 3 - Exit |
'------------------
' Update cached error and quit
'-----------------------------
If NOT CBool(iError AND (ERROR_FAIL + ERROR_INCOMPLETE)) Then RegDeleteValue HKCU, "SOFTWARE\Microsoft\Office\15.0\CleanC2R", "Rerun", False
SetRetVal iError
' log result
If CBool(iError AND ERROR_INCOMPLETE) Then
LogH2 "Removal result: " & iError & " - INCOMPLETE. Uninstall requires a system reboot to complete."
Else
sTmp = " - SUCCESS"
If CBool(iError AND ERROR_USERCANCEL) Then sTmp = " - USER CANCELED"
If CBool(iError AND ERROR_FAIL) Then sTmp = " - FAIL"
LogH2 "Removal result: " & iError & sTmp
End If
If CBool(iError AND ERROR_FAIL) Then
If CBool(iError AND ERROR_REBOOT_REQUIRED) Then Log " - Reboot required"
If CBool(iError AND ERROR_USERCANCEL) Then Log " - User cancel"
If CBool(iError AND ERROR_STAGE1) Then Log " - Msiexec failed"
If CBool(iError AND ERROR_STAGE2) Then Log " - Cleanup failed"
If CBool(iError AND ERROR_INCOMPLETE) Then Log " - Removal incomplete. Rerun after reboot needed"
If CBool(iError AND ERROR_DCAF_FAILURE) Then Log " - Second attempt cleanup still incomplete"
If CBool(iError AND ERROR_ELEVATION_USERDECLINED) Then Log " - User declined elevation"
If CBool(iError AND ERROR_ELEVATION) Then Log " - Elevation failed"
If CBool(iError AND ERROR_SCRIPTINIT) Then Log " - Initialization error"
If CBool(iError AND ERROR_RELAUNCH) Then Log " - Unhandled error during relaunch attempt"
If CBool(iError AND ERROR_UNKNOWN) Then Log " - Unknown error"
' ERROR_USER_ABORT is only valid for the temporary cached error file
'If CBool(iError AND ERROR_USER_ABORT) Then Log " - Process terminated by user"
End If
LogH2 "Removal end."
' Reboot handling
If fRebootRequired Then
sPrompt = "In order to complete uninstall, a system reboot is necessary. Would you like to reboot now?"
If NOT fQuiet Then
If MsgBox(sPrompt, vbYesNo, SCRIPTNAME & " - Reboot Required") = VB_YES Then
Dim colOS, oOS
Dim oWmiReboot
Set oWmiReboot = GetObject("winmgmts:{impersonationLevel=impersonate,(Shutdown)}!\\.\root\cimv2")
Set colOS = oWmiReboot.ExecQuery ("Select * from Win32_OperatingSystem")
For Each oOS in colOS
oOS.Reboot()
Next
End If
End If
End If
wscript.quit iError
End Sub 'ExitScript
'-------------------------------------------------------------------------------
' End Main
'
' End of Main section
'-------------------------------------------------------------------------------
'-------------------------------------------------------------------------------
' Initialize
'
' Configure defaults and initialize all required objects
'-------------------------------------------------------------------------------
Sub Initialize ()
Dim iCnt
' set defaults
'-------------
iError = ERROR_SUCCESS
iProcCloseCnt = 0
sLogDir = ""
sPackageFolder = ""
f64 = False
fCScript = False
fLogInitialized = False
fNoCancel = False
fRemoveOse = False
fDetectOnly = False
fQuiet = True
fForce = False
fC2R = True
fRebootRequired = False
fRerun = False
fTestRerun = False
fIsElevated = False
fNoElevate = False
fSetRunOnce = False
fUserConsent = False
' create required objects
'------------------------
Set oWmiLocal = GetObject("winmgmts:\\.\root\cimv2")
Set oWShell = CreateObject("Wscript.Shell")
Set oShellApp = CreateObject("Shell.Application")
Set oFso = CreateObject("Scripting.FileSystemObject")
Set oMsi = CreateObject("WindowsInstaller.Installer")
Set oReg = GetObject("winmgmts:\\.\root\default:StdRegProv")
' get environment path values
'----------------------------
sAppData = oWShell.ExpandEnvironmentStrings("%appdata%")
sLocalAppData = oWShell.ExpandEnvironmentStrings("%localappdata%")
sTemp = oWShell.ExpandEnvironmentStrings("%temp%")
sAllUsersProfile = oWShell.ExpandEnvironmentStrings("%allusersprofile%")
RegReadValue HKLM, "SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList", "ProfilesDirectory", sProfilesDirectory, "REG_EXPAND_SZ"
If NOT oFso.FolderExists(sProfilesDirectory) Then
sProfilesDirectory = oFso.GetParentFolderName(oWShell.ExpandEnvironmentStrings("%userprofile%"))
End If
sProgramFiles = oWShell.ExpandEnvironmentStrings("%programfiles%")
'sProgramFilesX86 = deferred. Depends on operating system architecture check
sCommonProgramFiles = oWShell.ExpandEnvironmentStrings("%commonprogramfiles%")
'sCommonProgramFilesX86 = deferred. Depends on operating system architecture check
sProgramData = oWSHell.ExpandEnvironmentStrings("%programdata%")
sWinDir = oWShell.ExpandEnvironmentStrings("%windir%")
'sPackageFolder = deferred
sWICacheDir = sWinDir & "\" & "Installer"
sScrubDir = sTemp & "\" & SCRIPTNAME
sScriptDir = wscript.ScriptFullName
sScriptDir = Left(sScriptDir, InStrRev(sScriptDir, "\"))
' ensure 64 bit host if needed
If InStr(LCase(wscript.path), "syswow64") > 0 Then RelaunchAs64Host
' create the temp folder
'-----------------------
If Not oFso.FolderExists(sScrubDir) Then oFso.CreateFolder sScrubDir
' set the default logging directory
'----------------------------------
sLogDir = sScrubDir
' detect bitness of the operating system
'----------------------------------------
Set ComputerItem = oWmiLocal.ExecQuery("Select * from Win32_ComputerSystem")
For Each Item In ComputerItem
f64 = Instr(Left(Item.SystemType, 3), "64") > 0
Next
If f64 Then sProgramFilesX86 = oWShell.ExpandEnvironmentStrings("%programfiles(x86)%")
If f64 Then sCommonProgramFilesX86 = oWShell.ExpandEnvironmentStrings("%CommonProgramFiles(x86)%")
' update error flag
'------------------
SetError ERROR_SCRIPTINIT
' get Win32_OperatingSystem details
'----------------------------------
Set ComputerItem = oWmiLocal.ExecQuery("Select * from Win32_OperatingSystem")
For Each Item in ComputerItem
sOSinfo = sOSinfo & Item.Caption
sOSinfo = sOSinfo & Item.OtherTypeDescription
sOSinfo = sOSinfo & ", " & "SP " & Item.ServicePackMajorVersion
sOSinfo = sOSinfo & ", " & "Version: " & Item.Version
sOsVersion = Item.Version
sOSinfo = sOSinfo & ", " & "Codepage: " & Item.CodeSet
sOSinfo = sOSinfo & ", " & "Country Code: " & Item.CountryCode
sOSinfo = sOSinfo & ", " & "Language: " & Item.OSLanguage
Next
' get VersionNT number
'---------------------
arrVersion = Split(sOsVersion, Delimiter(sOsVersion))
iVersionNt = CInt(arrVersion(0)) * 100 + CInt(arrVersion(1))
' ensure sufficient registry permisions
'--------------------------------------
fIsElevated = CheckRegPermissions
If NOT fIsElevated AND NOT fNoElevate Then
' try to relaunch elevated
RelaunchElevated
' can't relaunch. Exit out
SetError ERROR_ELEVATION
If UCase(Mid(Wscript.FullName, Len(Wscript.Path) + 2, 1)) = "C" Then
If Not fLogInitialized Then CreateLog
Log "Error: Insufficient registry access permissions - exiting"
End If
SetRetVal iError
wscript.quit iError
End If
' clear error flags
'------------------
ClearError ERROR_ELEVATION
ClearError ERROR_SCRIPTINIT
' ensure CScript as engine
'------------------------
fCScript = UCase(Mid(Wscript.FullName, Len(Wscript.Path) + 2, 1)) = "C"
If NOT fCScript AND NOT fQuiet Then RelaunchAsCScript
' set retval for file based logic
'--------------------------------
' value needs to be kept on 'user abort'
SetRetVal ERROR_USER_ABORT
' create dictionary objects
'--------------------------
Set dicInstalledSku = CreateObject("Scripting.Dictionary")
Set dicRemoveSku = CreateObject("Scripting.Dictionary")
Set dictionaryKeepSku = CreateObject("Scripting.Dictionary")
Set dictionaryKeepLis = CreateObject("Scripting.Dictionary")
Set dictionaryKeepFolder = CreateObject("Scripting.Dictionary")
Set dicApps = CreateObject("Scripting.Dictionary")
Set dicDelRegKey = CreateObject("Scripting.Dictionary")
Set dictionaryKeepReg = CreateObject("Scripting.Dictionary")
Set dicC2RSuite = CreateObject("Scripting.Dictionary")
Set dicDelInUse = CreateObject("Scripting.Dictionary")
Set dicDelFolder = CreateObject("Scripting.Dictionary")
' add initial known .exe files that need to be closed
'----------------------------------------------------
dicApps.Add "appvshnotify.exe", "appvshnotify.exe"
dicApps.Add "integratedoffice.exe", "integratedoffice.exe"
dicApps.Add "integrator.exe", "integrator.exe"
dicApps.Add "firstrun.exe", "firstrun.exe"
dicApps.Add "setup.exe", "setup.exe"
dicApps.Add "communicator.exe", "communicator.exe"
dicApps.Add "msosync.exe", "msosync.exe"
dicApps.Add "OneNoteM.exe", "OneNoteM.exe"
dicApps.Add "iexplore.exe", "iexplore.exe"
dicApps.Add "mavinject32.exe", "mavinject32.exe"
dicApps.Add "werfault.exe", "werfault.exe"
dicApps.Add "perfboost.exe", "perfboost.exe"
dicApps.Add "roamingoffice.exe", "roamingoffice.exe"
' SP1 additions / changes
dicApps.Add "officeclicktorun.exe", "officeclicktorun.exe"
dicApps.Add "officeondemand.exe", "officeondemand.exe"
dicApps.Add "OfficeC2RClient.exe", "OfficeC2RClient.exe"
End Sub 'Initialize
'-------------------------------------------------------------------------------
' ParseCmdLine
'
' Command line parser
'-------------------------------------------------------------------------------
Sub ParseCmdLine
Dim iCnt, iArgCnt
Dim arrArguments
Dim sArg0
iArgCnt = Wscript.Arguments.Count
If iArgCnt > 0 Then
If wscript.Arguments(0) = "UAC" Then
If wscript.arguments.count = 1 Then iArgCnt = 0
End If
End If
If iArgCnt = 0 Then
Select Case UCase(wscript.ScriptName)
Case Else
'Create the log
CreateLog
FindInstalledOProducts
sDefault = "ALL"
arrArguments = Split(Trim(sDefault), " ")
If UBound(arrArguments) = -1 Then ReDim arrArguments(0)
End Select
Else
ReDim arrArguments(iArgCnt-1)
For iCnt = 0 To (iArgCnt-1)
arrArguments(iCnt) = UCase(Wscript.Arguments(iCnt))
Next 'iCnt
End If 'iArgCnt = 0
' hardcode to full removal
sArg0 = "ALL"
Select Case UCase(sArg0)
Case "?"
ShowSyntax
Case "ALL"
fRemoveAll = True
fRemoveOse = False
Case "C2R"
fC2R = True
fRemoveAll = False
fRemoveOse = False
Case Else
fRemoveAll = False
fRemoveOse = False
sSkuRemoveList = sArg0
End Select
For iCnt = 0 To UBound(arrArguments)
Select Case arrArguments(iCnt)
Case "?", "/?", "-?"
ShowSyntax
Case "/L", "/LOG"
fLogInitialized = False
If UBound(arrArguments) > iCnt Then
If oFso.FolderExists(arrArguments(iCnt + 1)) Then
sLogDir = arrArguments(iCnt + 1)
Else
On Error Resume Next
oFso.CreateFolder(arrArguments(iCnt + 1))
If Err <> 0 Then sLogDir = sScrubDir Else sLogDir = arrArguments(iCnt + 1)
End If
End If
Case "/N", "/NOCANCEL"
fNoCancel = True
Case "/NE", "/NOELEVATE"
fNoElevate = True
Case "/O", "/OSE"
fRemoveOse = True
Case "/Q", "/QUIET"
fQuiet = True
' for test purposes only!
Case "/TR", "/TESTRERUN"
fTestRerun = True
Case Else
End Select
Next 'iCnt
If Not fLogInitialized Then CreateLog
End Sub 'ParseCmdLine
'-------------------------------------------------------------------------------
' ShowSyntax
'
' Show the expected syntax for the script usage
'-------------------------------------------------------------------------------
Sub ShowSyntax
Wscript.Echo sErr & vbCrLf & _
SCRIPTFILE & " V " & SCRIPTVERSION & vbCrLf & _
"Copyright (c) Microsoft Corporation. All Rights Reserved" & vbCrLf & vbCrLf & _
SCRIPTFILE & " - Remove " & ONAME & vbCrLf & _
"when a regular uninstall is no longer possible" & vbCrLf & vbCrLf & _
"Usage:" & vbTab & SCRIPTFILE & vbCrLf & vbCrLf & _
vbTab & "/? ' Displays this help"& vbCrLf
Wscript.Quit
End Sub 'ShowSyntax
'-------------------------------------------------------------------------------
' FindInstalledOProducts
'
' Office configuration products are listed with their configuration product
' name in the "Uninstall" key.
'-------------------------------------------------------------------------------
Sub FindInstalledOProducts
Dim ArpItem, prod, cult
Dim sCurKey, sValue, sConfigName, sCulture, sDisplayVersion, sProd
Dim sUninstallString
Dim iLeft, iRight
Dim arrKeys, arrProducts, arrCultures
Dim fSystemComponent0, fDisplayVersion, fUninstallString
Const REG_ARP = "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\"
Const REG_O15RPROPERTYBAG = "SOFTWARE\Microsoft\Office\15.0\ClickToRun\propertyBag\"
Const REG_O15C2RCONFIGURATION = "SOFTWARE\Microsoft\Office\15.0\ClickToRun\Configuration\"
Const REG_O15C2RPRODUCTIDS = "SOFTWARE\Microsoft\Office\15.0\ClickToRun\ProductReleaseIDs\Active\"
Const REG_O16C2RCONFIGURATION = "SOFTWARE\Microsoft\Office\16.0\ClickToRun\Configuration\"
Const REG_O16C2RPRODUCTIDS = "SOFTWARE\Microsoft\Office\16.0\ClickToRun\ProductReleaseIDs\Active\"
Const REG_C2RCONFIGURATION = "SOFTWARE\Microsoft\Office\ClickToRun\Configuration\"
Const REG_C2RPRODUCTIDS = "SOFTWARE\Microsoft\Office\ClickToRun\ProductReleaseIDs\"
If dicInstalledSku.Count > 0 Then Exit Sub 'Already done from command line parser
fDisplayVersion = False
' identify C2R products
LogOnly vbCrLf & "Detecting installed products "
LogOnly "Check for O15 C2R products"
' Check O15 Configuration key
If RegReadValue(HKLM, REG_O15C2RCONFIGURATION, "ProductReleaseIds", sValue, "REG_SZ") Then
arrProducts = Split(sValue, ",")
fDisplayVersion = RegReadValue(HKLM, REG_O15C2RPRODUCTIDS & "culture", "x-none", sDisplayVersion, "REG_SZ")
If NOT Err = 0 Then
Err.Clear
Else
' get version from active with fallback on configuration
For Each prod in arrProducts
LogOnly "Found O15 C2R product in Configuration: " & prod
' update product dictionary
If NOT dicInstalledSku.Exists(LCase(prod)) Then
LogOnly "adding new product to dictionary: " & LCase(prod)
dicInstalledSku.Add LCase(prod), sDisplayVersion
End If
Next 'prod
End If
End If
' Check O15 PropertyBag key
If RegReadValue(HKLM, REG_O15RPROPERTYBAG, "productreleaseid", sValue, "REG_SZ") Then
arrProducts = Split(sValue, ",")
fDisplayVersion = RegReadValue(HKLM, REG_O15C2RPRODUCTIDS & "culture", "x-none", sDisplayVersion, "REG_SZ")
If NOT Err = 0 Then
Err.Clear
Else
For Each prod in arrProducts
LogOnly "Found O15 C2R product in PropertyBag: " & prod
' update product dictionary
If NOT dicInstalledSku.Exists(LCase(prod)) Then
LogOnly "adding new product to dictionary: " & LCase(prod)
dicInstalledSku.Add LCase(prod), sDisplayVersion
End If
Next 'prod
End If
End If
'O16 section
LogOnly "Check for Office C2R products (>=QR8)"
' Check Office Configuration key
If RegReadValue(HKLM, REG_C2RPRODUCTIDS, "ActiveConfiguration", sActiveConfiguration, "REG_SZ") Then
' Get DisplayVersion
'Try QR8 logic first
fDisplayVersion = RegReadValue(HKLM, REG_C2RPRODUCTIDS & sActiveConfiguration & "\culture", "x-none", sDisplayVersion, "REG_SZ")
If RegEnumKey(HKLM, REG_C2RPRODUCTIDS & sActiveConfiguration & "\culture", arrCultures) Then
For Each cult In arrCultures
If InStr(LCase(cult), "x-none") > 0 Then
fDisplayVersion = RegReadValue(HKLM, REG_C2RPRODUCTIDS & sActiveConfiguration & "\culture\" & cult, "Version", sDisplayVersion, "REG_SZ")
End If
Next 'cult
End If
' Update product dic
If RegEnumKey(HKLM, REG_C2RPRODUCTIDS & sActiveConfiguration, arrProducts) Then
For Each prod In arrProducts
sProd = LCase(prod)
If InStr(sProd, ".") > 0 Then sProd = Left(sProd, InStr(sProd, ".") - 1)
Select Case LCase(sProd)
Case "culture", "stream"
Case Else
LogOnly "Found Office C2R product in Configuration: " & prod
' update product dictionary
If NOT dicInstalledSku.Exists(sProd) Then
LogOnly "adding new product to dictionary: " & sProd
dicInstalledSku.Add sProd, sDisplayVersion
End If
End Select
Next 'prod
End If 'arrProducts
End If 'ActiveConfiguration
LogOnly "Check for Office C2R products (QR7)"
' Check Office Configuration key
If RegReadValue(HKLM, REG_C2RCONFIGURATION, "ProductReleaseIds", sValue, "REG_SZ") Then
arrProducts = Split(sValue, ",")
If Not fDisplayVersion Then fDisplayVersion = RegReadValue(HKLM, REG_C2RPRODUCTIDS & "Active\culture", "x-none", sDisplayVersion, "REG_SZ")
If NOT Err = 0 Then
Err.Clear
Else
For Each prod in arrProducts
LogOnly "Found Office C2R product in Configuration: " & prod
' update version tracking
If NOT dicInstalledSku.Exists(LCase(prod)) Then
LogOnly "adding new product to dictionary: " & LCase(prod)
dicInstalledSku.Add LCase(prod), sDisplayVersion
End If
Next 'prod
End If
End If
LogOnly "Check for O16 C2R products (QR6)"
' Check O16 Configuration key
If RegReadValue(HKLM, REG_O16C2RCONFIGURATION, "ProductReleaseIds", sValue, "REG_SZ") Then
arrProducts = Split(sValue, ",")
If Not fDisplayVersion Then fDisplayVersion = RegReadValue(HKLM, REG_O16C2RPRODUCTIDS & "culture", "x-none", sDisplayVersion, "REG_SZ")
If NOT Err = 0 Then
Err.Clear
Else
For Each prod in arrProducts
LogOnly "Found O16 (QR6) C2R product in Configuration: " & prod
' update product dictionary
If NOT dicInstalledSku.Exists(LCase(prod)) Then
LogOnly "adding new product to dictionary: " & prod
dicInstalledSku.Add LCase(prod), sDisplayVersion
End If
Next 'prod
End If
End If
LogOnly "Check ARP for Office C2R products"
' ARP
RegEnumKey HKLM, REG_ARP, arrKeys
If IsArray(arrKeys) Then
For Each ArpItem in arrKeys
' filter on Office C2R products
sCurKey = REG_ARP & ArpItem & "\"
fUninstallString = RegReadValue(HKLM, sCurKey, "UninstallString", sValue, "REG_SZ")
If (fUninstallString And( (InStr(UCase(sValue), UCase("Microsoft Office 1")) > 0) Or (InStr(UCase(sValue), UCase("OfficeClickToRun.exe")) > 0) )) Then
'get Version
fDisplayVersion = RegReadValue(HKLM, sCurKey, "DisplayVersion", sDisplayVersion, "REG_SZ")
'extract the productreleaseid
sValue = Trim(sValue)
prod = Trim(Mid(sValue, InStrRev(sValue, " ")))
prod = Replace(prod, "productstoremove=", "")
If InStr(prod, "_") > 0 Then
prod = Left(prod, InStr(prod, "_") - 1)
End If
If InStr(prod, ".1") > 0 Then
prod = Left(prod, InStr(prod, ".1") - 1)
End If
LogOnly "Found C2R product in ARP: " & prod
If NOT dicInstalledSku.Exists(LCase(prod)) Then
LogOnly "adding new product to dictionary: " & prod
dicInstalledSku.Add LCase(prod), sDisplayVersion
End If
' categorize the SKU as C2R
If NOT dicC2RSuite.Exists(ArpItem) Then dicC2RSuite.Add ArpItem, prod & " - " & sDisplayVersion
Else
'Legacy logic keep for compat reasons
sValue = ""
sDisplayVersion = ""
fSystemComponent0 = NOT (RegReadValue(HKLM, sCurKey, "SystemComponent", sValue, "REG_DWORD") AND (sValue = "1"))
fDisplayVersion = RegReadValue(HKLM, sCurKey, "DisplayVersion", sValue, "REG_SZ")
If fDisplayVersion Then
sDisplayVersion = sValue
If Len(sValue) > 1 Then
On Error Resume Next
fDisplayVersion = (CInt(Left(sValue, 2)) > 14)
If Not Err <> 0 Then Err.Clear
Else
fDisplayVersion = False
End If
End If
fUninstallString = RegReadValue(HKLM, sCurKey, "UninstallString", sUninstallString, "REG_SZ")
' filter on C2R configuration SKU
If (fUninstallString And( (InStr(UCase(sUninstallString), UCase("Microsoft Office 1")) > 0) Or (InStr(UCase(sUninstallString), UCase("OfficeClickToRun.exe")) > 0) )) Then
' Extract the ProductReleaseID
If InStr(sUninstallString, "productstoremove=") > 0 Then
sConfigName = Trim(Mid(sValue, InStrRev(sUninstallString, " ")))
sConfigName = Replace(sConfigName, "productstoremove=", "")
If InStr(prod, "_") > 0 Then
sConfigName = Left(sConfigName, InStr(sConfigName, "_") - 1)
End If
Else
iLeft = InStr(ArpItem, " - ") + 2
iRight = InStr(iLeft, ArpItem, " - ") - 1
If iRight > 0 Then
sConfigName = Trim(Mid(ArpItem, iLeft, (iRight - iLeft)))
sCulture = Mid(ArpItem, iRight + 3)
Else
sConfigName = Trim(Left(ArpItem, iLeft - 3))
sCulture = Mid(ArpItem, iLeft)
End If
sConfigName = Replace(sConfigName, "Microsoft", "")
sConfigName = Replace(sConfigName, "Office", "")
sConfigName = Replace(sConfigName, "Professional", "Pro")
sConfigName = Replace(sConfigName, "Standard", "Std")
sConfigName = Replace(sConfigName, "(Technical Preview)", "")
sConfigName = Replace(sConfigName, "15", "")
sConfigName = Replace(sConfigName, "16", "")
sConfigName = Replace(sConfigName, "2013", "")
sConfigName = Replace(sConfigName, "2016", "")
sConfigName = Replace(sConfigName, " ", "")
sConfigName = Replace(sConfigName, "Project", "Prj")
sConfigName = Replace(sConfigName, "Visio", "Vis")
End If
If NOT dicInstalledSku.Exists(LCase(sConfigName)) Then
LogOnly "adding new product to dictionary (ARP Legacy): " & sConfigName
dicInstalledSku.Add LCase(sConfigName), sDisplayVersion
End If
' categorize the SKU as C2R
If NOT dicC2RSuite.Exists(ArpItem) Then dicC2RSuite.Add ArpItem, sConfigName & " - " & sDisplayVersion
ElseIf (fSystemComponent0 AND fDisplayVersion AND (InStr(UCase(ArpItem), UCase("OFFICE15.")) > 0 Or InStr(UCase(ArpItem), UCase("OFFICE16.")) > 0)) Then
' classic .msi install SKU
iLeft = InStr(ArpItem, ".") + 1
iRight = InStr(iLeft, ArpItem, "-") - 1
sConfigName = Mid(ArpItem, iLeft)
sCulture = ""
If NOT dictionaryKeepSku.Exists(ArpItem) Then dictionaryKeepSku.Add ArpItem, sConfigName & " - " & sDisplayVersion
End If
' Other products
If InScope(ArpItem) Then
Select Case Mid(ArpItem,11,4)
' 007E = Licensing
' 008C = Extensibility Components
Case "007E", "008F", "008C"
sConfigName = "Habanero"
RegReadValue HKLM, sCurKey, "DisplayName", sConfigName, "REG_SZ"
If NOT dicInstalledSku.Exists(LCase(ArpItem)) Then
LogOnly "adding new product to dictionary (ARP Legacy other): " & ArpItem
dicInstalledSku.Add LCase(ArpItem), sDisplayVersion
End If
If NOT dicC2RSuite.Exists(ArpItem) Then dicC2RSuite.Add ArpItem, sConfigName & " - " & sDisplayVersion
Case "24E1", "237A"
sConfigName = "MSOIDLOGIN"
If NOT dicInstalledSku.Exists(LCase(ArpItem)) Then
LogOnly "adding new product to dictionary (ARP Legacy other): " & ArpItem
dicInstalledSku.Add LCase(ArpItem), sDisplayVersion
End If
If NOT dicC2RSuite.Exists(ArpItem) Then dicC2RSuite.Add ArpItem, sConfigName & " - " & sDisplayVersion
Case Else
' not in scope for c2r removal!
If NOT dictionaryKeepSku.Exists(ArpItem) Then dictionaryKeepSku.Add ArpItem, ArpItem
End Select
End If 'InScope
' End legacy logic
End If
Next 'ArpItem
End If
End Sub 'FindInstalledOProducts
'-------------------------------------------------------------------------------
' EnsureValidWIMetadata
'
' Ensures that only valid metadata entries exist to avoid API failures.
' Invalid entries will be removed
'-------------------------------------------------------------------------------
Sub EnsureValidWIMetadata(hDefKey, sKey, iValidLength)
Dim arrKeys
Dim SubKey
If Len(sKey) > 1 Then
If Right(sKey, 1) = "\" Then sKey = Left(sKey, Len(sKey) - 1)
End If
If RegEnumKey(hDefKey, sKey, arrKeys) Then
For Each SubKey in arrKeys
If NOT Len(SubKey) = iValidLength Then
RegDeleteKey hDefKey, sKey & "\" & SubKey & "\"
End If
Next 'SubKey
End If
End Sub 'EnsureValidWIMetadata
'-------------------------------------------------------------------------------
' CleanOSPP
'
' Clean out licenses from the Office Software Protection Platform
'-------------------------------------------------------------------------------
Sub CleanOSPP
Dim oProductInstances, pi
Dim sCleanOSPP, sCmd, sRetVal
CONST OfficeAppId = "0ff1ce15-a989-479d-af46-f275c6370663" 'Office 2013
sCleanOSPP = "x64\CleanOSPP.exe"
If Not f64 Then sCleanOSPP = "x86\CleanOSPP.exe"
If oFso.FileExists(sScriptDir & sCleanOSPP) Then
sCmd = sScriptDir & sCleanOSPP
Log " Running: " & sCmd
On Error Resume Next
sRetVal = oWShell.Run(sCmd, 0, True)
Log " Return value: " & sRetVal
On Error Goto 0
Exit Sub
End If
On Error Resume Next
If NOT (dicC2RSuite.Count > 0 OR dictionaryKeepSku.Count > 0) Then
Log "Skipping CleanOSPP"
Exit Sub
End If
' Initialize the software protection platform object with a filter on Office 2013 products
If iVersionNT > 601 Then
Set oProductInstances = oWmiLocal.ExecQuery("SELECT ID, ApplicationId, PartialProductKey, Name, ProductKeyID FROM SoftwareLicensingProduct WHERE ApplicationId = '" & OfficeAppId & "' " & "AND PartialProductKey <> NULL")
Else
Set oProductInstances = oWmiLocal.ExecQuery("SELECT ID, ApplicationId, PartialProductKey, Name, ProductKeyID FROM OfficeSoftwareProtectionProduct WHERE ApplicationId = '" & OfficeAppId & "' " & "AND PartialProductKey <> NULL")
End If
' Remove all licenses
For Each pi in oProductInstances
If NOT IsNull(pi) Then
pi.UninstallProductKey( pi.ProductKeyID)
End If
Next 'pi
End Sub 'CleanOSPP
'-------------------------------------------------------------------------------
' DelSchtasks
'
' Delete know scheduled tasks.
'-------------------------------------------------------------------------------
Sub DelSchtasks ()
Dim sCmd
If CBool(iError AND ERROR_USERCANCEL) Then Exit Sub
LogH2 "Removing scheduled tasks"
LogOnly "FF_INTEGRATEDstreamSchedule"
oWShell.Run "SCHTASKS /Delete /TN FF_INTEGRATEDstreamSchedule /F", 0, False
wscript.sleep 500
LogOnly "FF_INTEGRATEDUPDATEDETECTION"
oWShell.Run "SCHTASKS /Delete /TN FF_INTEGRATEDUPDATEDETECTION /F", 0, False
wscript.sleep 500
LogOnly "C2RAppVLoggingStart"
oWShell.Run "SCHTASKS /Delete /TN C2RAppVLoggingStart /F", 0, False
wscript.sleep 500
LogOnly "Office 15 Subscription Heartbeat"
sCmd = "SCHTASKS /Delete /TN " & Chr(34) & "Office 15 Subscription Heartbeat" & Chr(34) & " /F"
oWShell.Run sCmd, 0, False
wscript.sleep 500
LogOnly "Microsoft Office 15 Sync Maintenance"
sCmd = "SCHTASKS /Delete /TN " & Chr(34) & "Microsoft Office 15 Sync Maintenance for {d068b555-9700-40b8-992c-f866287b06c1}" & Chr(34) & " /F"
oWShell.Run sCmd, 0, False
wscript.sleep 500
LogOnly "OfficeInventoryAgentFallBack"
sCmd = "SCHTASKS /Delete /TN " & Chr(34) & "\Microsoft\Office\OfficeInventoryAgentFallBack" & Chr(34) & " /F"
oWShell.Run sCmd, 0, False
wscript.sleep 500
LogOnly "OfficeTelemetryAgentFallBack"
sCmd = "SCHTASKS /Delete /TN " & Chr(34) & "\Microsoft\Office\OfficeTelemetryAgentFallBack" & Chr(34) & " /F"
oWShell.Run sCmd, 0, False
wscript.sleep 500
LogOnly "OfficeInventoryAgentLogOn"
sCmd = "SCHTASKS /Delete /TN " & Chr(34) & "\Microsoft\Office\OfficeInventoryAgentLogOn" & Chr(34) & " /F"
oWShell.Run sCmd, 0, False
LogOnly "OfficeTelemetryAgentLogOn"
sCmd = "SCHTASKS /Delete /TN " & Chr(34) & "\Microsoft\Office\OfficeTelemetryAgentLogOn" & Chr(34) & " /F"
oWShell.Run sCmd, 0, False
LogOnly "Office Background Streaming"
sCmd = "SCHTASKS /Delete /TN " & Chr(34) & "Office Background Streaming" & Chr(34) & " /F"
oWShell.Run sCmd, 0, False
wscript.sleep 500
LogOnly "Office Automatic Updates"
sCmd = "SCHTASKS /Delete /TN " & Chr(34) & "\Microsoft\Office\Office Automatic Updates" & Chr(34) & " /F"
oWShell.Run sCmd, 0, False
wscript.sleep 500
LogOnly "Office ClickToRun Service Monitor"
sCmd = "SCHTASKS /Delete /TN " & Chr(34) & "\Microsoft\Office\Office ClickToRun Service Monitor" & Chr(34) & " /F"
oWShell.Run sCmd, 0, False
wscript.sleep 500
LogOnly "Office Subscription Maintenance"
sCmd = "SCHTASKS /Delete /TN " & Chr(34) & "Office Subscription Maintenance" & Chr(34) & " /F"
oWShell.Run sCmd, 0, False
wscript.sleep 500
End Sub
'-------------------------------------------------------------------------------
' CloseOfficeApps
'
' End all running instances of applications that will be removed.
'-------------------------------------------------------------------------------
Sub CloseOfficeApps
Dim Processes, Process, app, prop
Dim sAppName, sOut, sUserWarn
Dim fWait
Dim iRet
On Error Resume Next
fWait = False
iProcCloseCnt = iProcCloseCnt + 1
If fRerun Then Exit Sub
If NOT fUserConsent Then
' detect processes to allow a user warning
sUserWarn = "Please save all open documents and close all Office, IE and Windows Explorer applications before proceeding." & vbCrLf & _
"When you click OK this removal process will terminate all running Office, IE and Windows Explorer processes and applications." & vbCrLf & vbCrLf & _
"Click ‘Cancel’ to to end this removal now."
For Each app in dicApps.Keys
sAppName = Replace(app, ".", "%.")
Set Processes = oWmiLocal.ExecQuery("Select * From Win32_Process Where Name like '" & sAppName & "'")
For Each Process in Processes
If NOT InStr(sUserWarn, Process.Name) > 0 Then sUserWarn = sUserWarn & vbCrLf & " - " & Process.Name
Next 'Process
Next 'app
Set Processes = oWmiLocal.ExecQuery("Select * From Win32_Process")
For Each Process in Processes
For Each prop in Process.Properties_
If prop.Name = "ExecutablePath" Then
If IsC2R(prop.Value) Then sUserWarn = sUserWarn & vbCrLf & " - " & Process.Name
End If 'ExcecutablePath
Next 'prop
Next 'Process
If (InStr(sUserWarn, " - ") > 0 AND NOT fQuiet) Then
iRet = MsgBox(sUserWarn, 49, "Save your unsaved work now!")
If iRet = 2 Then
SetError ERROR_USERCANCEL
ExitScript
Else
fUserConsent = True
End If
End If
End If 'fUserConsent
' end known processes first
For Each app in dicApps.Keys
sAppName = Replace(app, ".", "%.")
Set Processes = oWmiLocal.ExecQuery("Select * From Win32_Process Where Name like '" & sAppName & "'")
For Each Process in Processes
sOut = "End process '" & Process.Name
iRet = Process.Terminate()
CheckError "CloseOfficeApps: " & "Process.Name"
Log sOut & "' returned: " & iRet
fWait = True
Next 'Process
Next 'app
' end running applications
Set Processes = oWmiLocal.ExecQuery("Select * From Win32_Process")
For Each Process in Processes
For Each prop in Process.Properties_
If prop.Name = "ExecutablePath" Then
If IsC2R(prop.Value) Then
sOut = "End process '" & Process.Name
iRet = Process.Terminate()
CheckError "CloseOfficeApps: " & "Process.Name"
Log sOut & "' returned: " & iRet
fWait = True
End If
End If 'ExcecutablePath
Next 'prop
Next 'Process
If fWait Then wscript.sleep 5000
End Sub 'CloseOfficeApps
'-------------------------------------------------------------------------------
' Uninstall
'
' Identify and invoke default uninstall command for a regular uninstall.
'-------------------------------------------------------------------------------
Sub Uninstall
Dim OseService, srvc
Dim hDefKey, sSubKeyName, sValue, Name, arrNames, arrTypes
Dim sku, prod, sUninstallCmd, sReturn, sMsiProp, sCmd
Dim sPkgFld, sPkgGuid
Dim i
If CBool(iError AND ERROR_USERCANCEL) Then Exit Sub
' check if OSE service is *installed, *not disabled, *running under System context.
LogH2 "Checking state of OSE service"
Set OseService = oWmiLocal.Execquery("Select * From Win32_Service Where Name like 'ose%'")
For Each srvc in OseService
If (srvc.StartMode = "Disabled") AND (Not srvc.ChangeStartMode("Manual") = 0) Then _
Log "Conflict detected: OSE service is disabled"
If (Not srvc.StartName = "LocalSystem") AND (srvc.Change( , , , , , , "LocalSystem", "")) Then _
Log "Conflict detected: OSE service not running as LocalSystem"
Next 'srvc
If NOT dicC2RSuite.Count > 0 Then
Log "No uninstallable C2R items registered in Uninstall"
End If
' remove the published component registration for C2R packages
LogH2 "Removing published component registration for C2R packages"
' delete the manifest files
For i = 1 To 4
Select Case i
Case 1
RegReadValue HKLM, "SOFTWARE\Microsoft\Office\15.0\ClickToRun", "PackageFolder", sPkgFld, "REG_SZ"
RegReadValue HKLM, "SOFTWARE\Microsoft\Office\15.0\ClickToRun", "PackageGUID", sPkgGuid, "REG_SZ"
Case 2
RegReadValue HKLM, "SOFTWARE\Microsoft\Office\16.0\ClickToRun", "PackageFolder", sPkgFld, "REG_SZ"
RegReadValue HKLM, "SOFTWARE\Microsoft\Office\16.0\ClickToRun", "PackageGUID", sPkgGuid, "REG_SZ"
Case 3
RegReadValue HKLM, "SOFTWARE\Microsoft\Office\ClickToRun", "PackageFolder", sPkgFld, "REG_SZ"
RegReadValue HKLM, "SOFTWARE\Microsoft\Office\ClickToRun", "PackageGUID", sPkgGuid, "REG_SZ"
Case 4
sPkgFld = sPackageFolder
sPkgGuid = sPackageGuid
End Select
If oFso.FolderExists(sValue & "\root\Integration") Then
sCmd = "cmd.exe /c del " & chr(34) & sPkgFld & "\root\Integration\C2RManifest*.xml" & chr(34)
Log " Running: " & sCmd
sReturn = oWShell.Run (sCmd, 0, True)
Log " Return value: " & sReturn
If oFso.FileExists(sPkgFld & "\root\Integration\integrator.exe") Then
sCmd = chr(34) & sPkgFld & "\root\Integration\integrator.exe" & chr(34) & " /U /Extension PackageRoot=" & chr(34) & sPkgFld & "\root" & chr(34) & " PackageGUID=" & sPkgGuid
Log " Running: " & sCmd
sReturn = oWShell.Run (sCmd, 0, True)
Log " Return value: " & sReturn
sCmd = chr(34) & sPkgFld & "\root\Integration\integrator.exe" & chr(34) & " /U"
Log " Running: " & sCmd
sReturn = oWShell.Run (sCmd, 0, True)
Log " Return value: " & sReturn
End If
If oFso.FileExists(sProgramData & "\Microsoft\ClickToRun\{" & sPkgGuid & "}\integrator.exe") Then
sCmd = chr(34) & sProgramData & "\Microsoft\ClickToRun\{" & sPkgGuid & "}\integrator.exe" & chr(34) & " /U /Extension PackageRoot=" & chr(34) & sPkgFld & "\root" & chr(34) & " PackageGUID=" & sPkgGuid
Log " Running: " & sCmd
sReturn = oWShell.Run (sCmd, 0, True)
Log " Return value: " & sReturn
End If
End If
Next 'i
' delete potential blocking registry keys for msiexec based tasks
LogH2 "Removing C2R and App-V registry data"
For Each sku in dicC2RSuite.Keys
' remove the ARP entry
RegDeleteKey HKLM, REG_ARP & sku
Next 'sku
RegDeleteKey HKCU, "SOFTWARE\Microsoft\Office\15.0\ClickToRun"
RegDeleteKey HKLM, "SOFTWARE\Microsoft\Office\15.0\ClickToRun"
RegDeleteKey HKCU, "SOFTWARE\Microsoft\Office\16.0\ClickToRun"
RegDeleteKey HKLM, "SOFTWARE\Microsoft\Office\16.0\ClickToRun"
RegDeleteKey HKCU, "SOFTWARE\Microsoft\Office\ClickToRun"
RegDeleteKey HKLM, "SOFTWARE\Microsoft\Office\ClickToRun"
' AppV keys
hDefKey = HKCU
sSubKeyName = "SOFTWARE\Microsoft\AppV\ISV"
Do
If RegEnumValues(hDefKey, sSubKeyName, arrNames, arrTypes) Then
For Each Name in arrNames
If IsC2R(Name) Then RegDeleteValue hDefKey, sSubKeyName, Name, False
Next 'Name
End If 'RegEnumValues
If hDefKey = HKLM Then Exit Do
hDefKey = HKLM
Loop
' msiexec based uninstall
sMsiProp = " REBOOT=ReallySuppress NOREMOVESPAWN=True"
LogH2 "Detect Msi based products"
For Each prod in oMsi.Products
If CheckDelete(prod) Then
Log "Calling msiexec.exe to remove " & prod
sUninstallCmd = "msiexec.exe /x" & prod & sMsiProp
If fQuiet Then
sUninstallCmd = sUninstallCmd & " /q"
Else
sUninstallCmd = sUninstallCmd & " /qb-!"
End If
sUninstallCmd = sUninstallCmd & " /l*v " & chr(34) & sLogDir & "\Uninstall_" & prod & ".log" & chr(34)
CloseOfficeApps
LogOnly "Calling msiexec with '" & sUninstallCmd & "'"
sReturn = oWShell.Run(sUninstallCmd, 0, True)
Log "msiexec returned: " & SetupRetVal(sReturn) & " (" & sReturn & ")" & vbCrLf
fRebootRequired = fRebootRequired OR (sReturn = "3010")
If fRebootRequired Then SetError ERROR_REBOOT_REQUIRED
Select Case CInt(sReturn)
Case ERROR_SUCCESS,ERROR_SUCCESS_CONFIG_COMPLETE,ERROR_SUCCESS_REBOOT_REQUIRED
'success no action required
Case Else
SetError ERROR_STAGE1
End Select
Else
LogOnly "Skipping out of scope product: " & prod
End If 'CheckDelete
Next 'Product
oWShell.Run "cmd.exe /c net stop msiserver", 0, False
End Sub 'Uninstall
'-------------------------------------------------------------------------------
' RegWipe
'
' Removal of left behind registry data
'-------------------------------------------------------------------------------
Sub Regwipe
Dim hDefKey, item, name, value, RetVal
Dim sGuid, sSubKeyName, sValue, sCmd
Dim i, iLoopCnt
Dim arrKeys, arrNames, arrTypes, arrTestNames, arrTestTypes
Dim arrMultiSzValues, arrMultiSzNewValues
Dim fDelReg
If CBool(iError AND ERROR_USERCANCEL) Then Exit Sub
LogH1 "Registry CleanUp"
CloseOfficeApps
' Note: ARP entries have already been cleared in uninstall stage
' HKCU Registration
RegDeleteKey HKCU, "Software\Microsoft\Office\15.0\Registration"
RegDeleteKey HKCU, "Software\Microsoft\Office\16.0\Registration"
RegDeleteKey HKCU, "Software\Microsoft\Office\Registration"
' C2R specifics
' AppV key "SOFTWARE\Microsoft\AppV" has already been cleared in uninstall stage
' Virtual InstallRoot
RegDeleteKey HKLM, "SOFTWARE\Microsoft\Office\15.0\Common\InstallRoot\Virtual"
RegDeleteKey HKLM, "SOFTWARE\Microsoft\Office\16.0\Common\InstallRoot\Virtual"
RegDeleteKey HKLM, "SOFTWARE\Microsoft\Office\Common\InstallRoot\Virtual"
' Mapi reg
If NOT dictionaryKeepSku.Count > 0 Then RegDeleteKey HKLM, "SOFTWARE\Classes\CLSID\{2027FC3B-CF9D-4ec7-A823-38BA308625CC}"
' C2R keys
RegDeleteKey HKCU, "SOFTWARE\Microsoft\Office\15.0\ClickToRun"
RegDeleteKey HKLM, "SOFTWARE\Microsoft\Office\15.0\ClickToRun"
RegDeleteKey HKLM, "SOFTWARE\Microsoft\Office\15.0\ClickToRunStore"
RegDeleteKey HKCU, "SOFTWARE\Microsoft\Office\16.0\ClickToRun"
RegDeleteKey HKLM, "SOFTWARE\Microsoft\Office\16.0\ClickToRun"
RegDeleteKey HKLM, "SOFTWARE\Microsoft\Office\16.0\ClickToRunStore"
RegDeleteKey HKCU, "SOFTWARE\Microsoft\Office\ClickToRun"
RegDeleteKey HKLM, "SOFTWARE\Microsoft\Office\ClickToRun"
RegDeleteKey HKLM, "SOFTWARE\Microsoft\Office\ClickToRunStore"
' Office key in HKLM
If Not dictionaryKeepSku.Count > 0 Then
'double calls to ensure Wow6432 gets cleared out as well
RegDeleteKey HKLM, "Software\Microsoft\Office\15.0"
RegDeleteKey HKLM, "Software\Microsoft\Office\15.0"
RegDeleteKey HKLM, "Software\Microsoft\Office\16.0"
RegDeleteKey HKLM, "Software\Microsoft\Office\16.0"
End If
ClearOfficeHKLM "SOFTWARE\Microsoft\Office"
' Run key
sSubKeyName = "SOFTWARE\Microsoft\Windows\CurrentVersion\Run"
If RegEnumValues (HKLM, sSubKeyName, arrNames, arrTypes) Then
For Each name in arrNames
If RegReadValue(HKLM, sSubKeyName, name, sValue, "REG_SZ") Then
If IsC2R(sValue) Then RegDeleteValue HKLM, sSubKeyName, name, False
End If
Next 'item
End If
RegDeleteValue HKLM, "SOFTWARE\Microsoft\Windows\CurrentVersion\Run", "Lync15", False
RegDeleteValue HKLM, "SOFTWARE\Microsoft\Windows\CurrentVersion\Run", "Lync16", False
If NOT dictionaryKeepSku.Count > 0 Then
' Protocol Handlers
RegDeleteKey HKLM, "SOFTWARE\Classes\Protocols\Handler\osf"
' Groove ShellIconOverlayIdentifiers
RegDeleteKey HKLM, "SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers\Microsoft SPFS Icon Overlay 1 (ErrorConflict)"
RegDeleteKey HKLM, "SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers\Microsoft SPFS Icon Overlay 2 (SyncInProgress)"
RegDeleteKey HKLM, "SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers\Microsoft SPFS Icon Overlay 3 (InSync)"
RegDeleteKey HKLM, "SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers\Microsoft SPFS Icon Overlay 1 (ErrorConflict)"
RegDeleteKey HKLM, "SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers\Microsoft SPFS Icon Overlay 2 (SyncInProgress)"
RegDeleteKey HKLM, "SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers\Microsoft SPFS Icon Overlay 3 (InSync)"
' Shell extensions
RegDeleteValue HKLM, "SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\", "{B28AA736-876B-46DA-B3A8-84C5E30BA492}", False
RegDeleteValue HKLM, "SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\", "{8B02D659-EBBB-43D7-9BBA-52CF22C5B025}", False
RegDeleteValue HKLM, "SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\", "{0875DCB6-C686-4243-9432-ADCCF0B9F2D7}", False
RegDeleteValue HKLM, "SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\", "{42042206-2D85-11D3-8CFF-005004838597}", False
RegDeleteValue HKLM, "SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\", "{993BE281-6695-4BA5-8A2A-7AACBFAAB69E}", False
RegDeleteValue HKLM, "SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\", "{C41662BB-1FA0-4CE0-8DC5-9B7F8279FF97}", False
RegDeleteValue HKLM, "SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\", "{506F4668-F13E-4AA1-BB04-B43203AB3CC0}", False
RegDeleteValue HKLM, "SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\", "{D66DC78C-4F61-447F-942B-3FB6980118CF}", False
RegDeleteValue HKLM, "SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\", "{46137B78-0EC3-426D-8B89-FF7C3A458B5E}", False
' BHO
RegDeleteKey HKLM, "SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Browser Helper Objects\{31D09BA0-12F5-4CCE-BE8A-2923E76605DA}"
RegDeleteKey HKLM, "SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Browser Helper Objects\{B4F3A835-0E21-4959-BA22-42B3008E02FF}"
RegDeleteKey HKLM, "SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Browser Helper Objects\{D0498E0A-45B7-42AE-A9AA-ABA463DBD3BF}"
RegDeleteKey HKLM, "SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Explorer\Browser Helper Objects\{31D09BA0-12F5-4CCE-BE8A-2923E76605DA}"
RegDeleteKey HKLM, "SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Explorer\Browser Helper Objects\{B4F3A835-0E21-4959-BA22-42B3008E02FF}"
RegDeleteKey HKLM, "SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Explorer\Browser Helper Objects\{D0498E0A-45B7-42AE-A9AA-ABA463DBD3BF}"
' OneNote Namespace Extension for Desktop
RegDeleteKey HKLM, "SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Desktop\NameSpace\{0875DCB6-C686-4243-9432-ADCCF0B9F2D7}"
' Web Sites
RegDeleteKey HKLM, "SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Desktop\Namespace\{B28AA736-876B-46DA-B3A8-84C5E30BA492}"
RegDeleteKey HKLM, "SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\NetworkNeighborhood\Namespace\{46137B78-0EC3-426D-8B89-FF7C3A458B5E}"
' VolumeCaches
RegDeleteKey HKLM, "SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Microsoft Office Temp Files"
End If 'NOT dictionaryKeepSku.Count > 0
' ARP
' Note: configuration entries have already been removed
' as part of the 'Uninstall' stage
If RegEnumKey(HKLM, REG_ARP, arrKeys) Then
For Each item in arrKeys
If Len(item) > 37 Then
sGuid = UCase(Left(item, 38))
If CheckDelete(sGuid) Then RegDeleteKey HKLM, REG_ARP & item & "\"
End If 'Len(Item)>37
Next 'Item
End If
' UpgradeCodes, WI config, WI global config
LogH2 "Scanning Windows Installer metadata for removeable UpgradeCodes"
For iLoopCnt = 1 to 5
Select Case iLoopCnt
Case 1
sSubKeyName = "SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UpgradeCodes\"
hDefKey = HKLM
Case 2
sSubKeyName = "Installer\UpgradeCodes\"
hDefKey = HKCR
Case 3
sSubKeyName = "SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\"
hDefKey = HKLM
Case 4
sSubKeyName = "Installer\Features\"
hDefKey = HKCR
Case 5
sSubKeyName = "Installer\Products\"
hDefKey = HKCR
End Select
If RegEnumKey(hDefKey, sSubKeyName, arrKeys) Then
For Each item in arrKeys
' ensure the expected length for a compressed GUID
If Len(item) = 32 Then
' expand the GUID
sGuid = GetExpandedGuid(item)
' check if it's an Office key
If CheckDelete(sGuid) Then
If iLoopCnt < 3 Then
' enum all entries
RegEnumValues hDefKey, sSubKeyName & item, arrNames, arrTypes
If IsArray(arrNames) Then
' delete entries within removal scope
For Each name in arrNames
If Len(name) = 32 Then
sGuid = GetExpandedGuid(name)
If CheckDelete(sGuid) Then RegDeleteValue hDefKey, sSubKeyName & item & "\", name, True
Else
' invalid data -> delete the value
RegDeleteValue hDefKey, sSubKeyName & item & "\", name, True
End If
Next 'Name
End If 'IsArray(arrNames)
' if all entries were removed - delete the key
If NOT RegEnumValues(hDefKey, sSubKeyName & item, arrNames, arrTypes) Then RegDeleteKey hDefKey, sSubKeyName & item & "\"
Else 'iLoopCnt >= 3
RegDeleteKey hDefKey, sSubKeyName & item & "\"
End If 'iLoopCnt < 3
End If 'InScope
End If 'Len(Item)=32
Next 'Item
End If 'RegEnumKey
Next 'iLoopCnt
' Components in Global
LogH2 "Scanning Windows Installer Global Components metadata"
sSubKeyName = "SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components\"
hDefKey = HKLM
If RegEnumKey(hDefKey, sSubKeyName, arrKeys) Then
For Each item in arrKeys
' ensure the expected length for a compressed GUID
If Len(Item) = 32 Then
If RegEnumValues(hDefKey, sSubKeyName & item, arrNames, arrTypes) Then
For Each name in arrNames
If Len(Name) = 32 Then
sGuid = GetExpandedGuid(Name)
If CheckDelete(sGuid) Then
RegDeleteValue hDefKey, sSubKeyName & item & "\", name, False
' if all entries were removed - delete the key
If NOT RegEnumValues(hDefKey, sSubKeyName & item, arrTestNames, arrTestTypes) Then RegDeleteKey hDefKey, sSubKeyName & item & "\"
End If
End If '32
Next 'Name
End If 'RegEnumValues
End If '32
Next 'Item
End If 'RegEnumKey
' Published Components
LogH2 "Scanning Windows Installer Published Components metadata"
sSubKeyName = "Installer\Components\"
hDefKey = HKCR
If RegEnumKey(hDefKey, sSubKeyName, arrKeys) Then
For Each item in arrKeys
' ensure the expected length for a compressed GUID
If Len(Item) = 32 Then
If RegEnumValues(hDefKey, sSubKeyName & item, arrNames, arrTypes) Then
For Each name in arrNames
If RegReadValue (hDefKey, sSubKeyName & item, name, sValue, "REG_MULTI_SZ") Then
arrMultiSzValues = Split(sValue, chr(13))
If IsArray(arrMultiSzValues) Then
i = -1
ReDim arrMultiSzNewValues(-1)
fDelReg = False
For Each value in arrMultiSzValues
If Len(value) > 19 Then
sGuid = ""
If GetDecodedGuid(Left(value, SQUISHED), sGuid) Then
If CheckDelete(sGuid) Then
fDelReg = True
Else
i = i + 1
ReDim Preserve arrMultiSzNewValues(i)
arrMultiSzNewValues(i) = value
End If 'CheckDelete
End If 'decode
End If '19
Next 'Value
If NOT (i = -1) Then
If NOT UBound(arrMultiSzValues) = i Then oReg.SetMultiStringValue hDefKey, sSubKeyName & item, name,arrMultiSzNewValues
Else
If fDelReg Then
RegDeleteValue hDefKey, sSubKeyName & item & "\", name, True
' if all entries were removed - delete the key
If NOT RegEnumValues(hDefKey, sSubKeyName & item, arrTestNames, arrTestTypes) Then RegDeleteKey hDefKey, sSubKeyName & item & "\"
End If 'DelReg
End If
End If 'IsArray
End If
Next 'Name
End If 'RegEnumValues
End If '32
Next 'Item
End If 'RegEnumKey
End Sub 'Regwipe
'-------------------------------------------------------------------------------
' FileWipe
'
' Removal of left behind services, files and shortcuts
'-------------------------------------------------------------------------------
Sub FileWipe
Dim scRoot
Dim fDelFolders
If CBool(iError AND ERROR_USERCANCEL) Then Exit Sub
LogH1 "File Cleanup"
fDelFolders = False
CloseOfficeApps
DelSchtasks
' remove the OfficeSvc service
LogH2 "Deleting OfficeSvc service"
DeleteService "OfficeSvc"
' SP1 addition / change
' remove the ClickToRunSvc service
LogH2 "Deleting ClickToRunSvc service"
DeleteService "ClickToRunSvc"
' adding additional processes for termination
dicApps.Add "explorer.exe", "explorer.exe"
dicApps.Add "msiexec.exe", "msiexec.exe"
dicApps.Add "ose.exe", "ose.exe"
If fC2R Then
' delete C2R package files
LogH2 "Deleting C2R package files"
If oFso.FolderExists(sProgramFiles & "\Microsoft Office 15") _
Or oFso.FolderExists(sProgramFiles & "\Microsoft Office 16") _
Or oFso.FolderExists(oWShell.ExpandEnvironmentStrings("%programfiles%") & "\Microsoft Office\PackageManifests") _
Or oFso.FolderExists(oWShell.ExpandEnvironmentStrings("%programfiles(x86)%") & "\Microsoft Office\PackageManifests") Then
fDelFolders = True
Log " Attention: Now closing Explorer.exe for file delete operations"
Log " Explorer will automatically restart."
wscript.sleep 2000
CloseOfficeApps
End If
' delete Office folders
LogH2 "Deleting Office folders"
DeleteFolder sProgramFiles & "\Microsoft Office 15"
DeleteFolder sProgramFiles & "\Microsoft Office 16"
If f64 Then
DeleteFolder sCommonProgramFilesX86 & "\Microsoft Office 15"
DeleteFolder sCommonProgramFilesX86 & "\Microsoft Office 16"
End If
If fDelFolders Then
DeleteFolder sProgramFiles & "\Microsoft Office\PackageManifests"
DeleteFolder sProgramFiles & "\Microsoft Office\PackageSunrisePolicies"
DeleteFolder sProgramFiles & "\Microsoft Office\root"
DeleteFile sProgramFiles & "\Microsoft Office\AppXManifest.xml"
DeleteFile sProgramFiles & "\Microsoft Office\FileSystemMetadata.xml"
If Not dictionaryKeepSku.Count > 0 Then
DeleteFolder sProgramFiles & "\Microsoft Office\Office16"
DeleteFolder sProgramFiles & "\Microsoft Office\Office15"
End If
If f64 Then
DeleteFolder sProgramFilesX86 & "\Microsoft Office\PackageManifests"
DeleteFolder sProgramFilesX86 & "\Microsoft Office\PackageSunrisePolicies"
DeleteFolder sProgramFilesX86 & "\Microsoft Office\root"
DeleteFile sProgramFilesX86 & "\Microsoft Office\AppXManifest.xml"
DeleteFile sProgramFilesX86 & "\Microsoft Office\FileSystemMetadata.xml"
If Not dictionaryKeepSku.Count > 0 Then
DeleteFolder sProgramFilesX86 & "\Microsoft Office\Office16"
DeleteFolder sProgramFilesX86 & "\Microsoft Office\Office15"
End If
End If
End If
DeleteFolder sProgramData & "\Microsoft\ClickToRun"
DeleteFolder sCommonProgramFiles & "\microsoft shared\ClickToRun"
DeleteFolder sProgramData & "\Microsoft\office\FFPackageLocker"
DeleteFolder sProgramData & "\Microsoft\office\ClickToRunPackageLocker"
If oFso.FileExists(sProgramData & "\Microsoft\office\FFPackageLocker") Then DeleteFile sProgramData & "\Microsoft\office\FFPackageLocker"
If oFso.FileExists(sProgramData & "\Microsoft\office\FFStatePBLocker") Then DeleteFile sProgramData & "\Microsoft\office\FFStatePBLocker"
If NOT dictionaryKeepSku.Count > 0 Then DeleteFolder sProgramData & "\Microsoft\office\Heartbeat"
DeleteFolder oWShell.ExpandEnvironmentStrings("%userprofile%") & "\Microsoft Office"
DeleteFolder oWShell.ExpandEnvironmentStrings("%userprofile%") & "\Microsoft Office 15"
DeleteFolder oWShell.ExpandEnvironmentStrings("%userprofile%") & "\Microsoft Office 16"
End If
' restore explorer.exe if needed
RestoreExplorer
' delete shortcuts
LogH2 "Search and delete shortcuts"
CleanShortcuts sAllUsersProfile, True, False
CleanShortcuts sProfilesDirectory, True, False
' delete empty folder structures
If dicDelFolder.Count > 0 Then
LogH2 "Removing empty folders"
DeleteEmptyFolders
End If
' add the collected files in use for delete on reboot
If dicDelInUse.Count > 0 Then ScheduleDeleteEx
LogH2 "File Cleanup complete"
End Sub ' FileWipe
'-------------------------------------------------------------------------------
' CleanShortcuts
'
' Recursively search all profile folders for Office shortcuts in scope
'-------------------------------------------------------------------------------
Sub CleanShortcuts (sFolder, fDelete, fUnPin)
Dim oFolder, fld, file, sc, item
Dim fDeleteSC
Set oFolder = oFso.GetFolder(sFolder)
' exclude system protected link folders
If CBool(oFolder.Attributes AND 1024) Then Exit Sub
On Error Resume Next
For Each fld In oFolder.SubFolders
If Err <> 0 Then
CheckError "CleanShortcuts: " & vbTab & sFolder
Else
CleanShortcuts fld.Path, fDelete, fUnPin
End If
Next
For Each file In oFolder.Files
If LCase(Right(file.Path, 4)) = ".lnk" Then
fDeleteSC = False
LogOnly " check file: " & file.Path
set sc = oWShell.CreateShortcut(file.Path)
If Err <> 0 Then
CheckError "CleanShortcutsSC: " & vbTab & sFolder
Else
'Compare if the shortcut target is in the list of executables that will be removed
'LogOnly " - SC.TargetPath: " & sc.TargetPath
If Len(sc.TargetPath) > 0 Then
If InStr(sc.TargetPath,"{") > 0 Then
'Handle Windows Installer shortcuts
If Len(sc.TargetPath) >= InStr(sc.TargetPath,"{") + 37 Then
If CheckDelete(Mid(sc.TargetPath, InStr(sc.TargetPath,"{"), 38)) Then fDeleteSC = True
End If
Else
'Handle regular shortcuts
If IsC2R(sc.TargetPath) Then fDeleteSC = True
If NOT oFso.FileExists(sc.TargetPath) Then
' Shortcut target does not exist
If IsC2R(sc.TargetPath) Then
LogOnly "removing Office shortcut with non-existent target: " & file.Path & " - " & sc.TargetPath
fDeleteSC = True
Else
'LogOnly " - keep orphaned SC as target is not in scope: " & sc.TargetPath
End If
Else
'LogOnly " - keep SC as shortcut target does still exist: " & sc.TargetPath
End If
End If
End If
End If
If fDeleteSC Then
If NOT dicDelFolder.Exists(sFolder) Then dicDelFolder.Add sFolder, sFolder
If fUnPin Then UnPin file
If fDelete Then
UnPin file
DeleteFile file.Path
End If
fDeleteSC = False
End If 'fDeleteSC
End If
Next
On Error Goto 0
End Sub 'CleanShortcuts
'-------------------------------------------------------------------------------
' UnPin
'
' Unpins a shortcut from the taskbar or start menu
'-------------------------------------------------------------------------------
Sub UnPin(file)
Dim fldItem, verb
On Error Resume Next
Set fldItem = oShellApp.NameSpace(file.ParentFolder.Path).ParseName(file.Name)
For Each verb in fldItem.Verbs
Select Case Replace(verb, "&", "")
Case "Unpin from Taskbar", "Von Taskleiste lösen", "Détacher du barre des tâches", "Détacher de la barre des tâches", "Desanclar de la barra de tareas", "Ta bort från Aktivitetsfältet", "タスク バーに表示しない(K)", "작업 표시줄에서 제거(K)", "Открепить от панели задач"
verb.DoIt
Case "Unpin from Start Menu", "Vom Startmenü lösen", "Détacher du menu Démarrer", "Détacher de la menu Démarrer"
If iVersionNT > 600 Then verb.DoIt
End Select
Next
On Error Goto 0
End Sub
'-------------------------------------------------------------------------------
' ClearOfficeHKLM
'
' Recursively search and clear the HKLM Office key from references in scope
'-------------------------------------------------------------------------------
Sub ClearOfficeHKLM (sSubKeyName)
Dim key, name
Dim sValue
Dim arrKeys, arrNames, arrTypes
Dim arrTestNames, arrTestTypes, arrTestKeys
' recursion
If RegEnumKey(HKLM, sSubKeyName, arrKeys) Then
For Each key in arrKeys
ClearOfficeHKLM sSubKeyName & "\" & key
Next 'key
End If
' identify & clear removable entries
If RegEnumValues(HKLM, sSubKeyName, arrNames, arrTypes) Then
For Each name in arrNames
If RegReadValue(HKLM, sSubKeyName, name, sValue, "REG_SZ") Then
If IsC2R(sValue) Then RegDeleteValue HKLM, sSubKeyName, name, False
End If
Next 'item
End If
' clear out empty keys
If (NOT RegEnumValues(HKLM, sSubKeyName, arrNames, arrTypes)) AND _
(NOT RegEnumKey(HKLM, sSubKeyName, arrKeys)) AND _
(NOT dictionaryKeepSku.Count > 0) Then _
RegDeleteKey HKLM, sSubKeyName
End Sub
'-------------------------------------------------------------------------------
'
' Helper Functions
'
'-------------------------------------------------------------------------------
'-------------------------------------------------------------------------------
' IsC2R
'
' Check if the passed in string is related to C2R
' Returns TRUE if in C2R scope
'-------------------------------------------------------------------------------
Function IsC2R (sValue)
Const OREF = "\ROOT\OFFICE1"
Const OREFROOT = "Microsoft Office\Root\"
Const OREGREFC2R15 = "Microsoft Office 15"
Const OREGREFC2R16 = "Microsoft Office 16"
Const OCOMMON = "\microsoft shared\ClickToRun"
Const OMANIFEST = "\Microsoft Office\PackageManifests"
Const OSUNRISE = "\Microsoft Office\PackageSunrisePolicies"
Dim fReturn
fReturn = False
If InStr(LCase(sValue), LCase(OREF)) > 0 _
Or InStr(LCase(sValue), LCase(OREFROOT)) > 0 _
Or InStr(LCase(sValue), LCase(OCOMMON)) > 0 _
Or InStr(LCase(sValue), LCase(OMANIFEST)) > 0 _
Or InStr(LCase(sValue), LCase(OSUNRISE)) > 0 _
Or InStr(LCase(sValue), LCase(OREGREFC2R15)) > 0 _
Or InStr(LCase(sValue), LCase(OREGREFC2R16)) > 0 Then fReturn = True
IsC2R = fReturn
End Function
'-------------------------------------------------------------------------------
' CheckRegPermissions
'
' Test the permissions on some key registry locations to determine if
' sufficient permissions are given.
'-------------------------------------------------------------------------------
Function CheckRegPermissions
Const KEY_QUERY_VALUE = &H0001
Const KEY_SET_VALUE = &H0002
Const KEY_CREATE_SUB_KEY = &H0004
Const DELETE = &H00010000
Dim sSubKeyName
Dim fReturn
CheckRegPermissions = True
sSubKeyName = "Software\Microsoft\Windows\"
oReg.CheckAccess HKLM, sSubKeyName, KEY_QUERY_VALUE, fReturn
If Not fReturn Then CheckRegPermissions = False
oReg.CheckAccess HKLM, sSubKeyName, KEY_SET_VALUE, fReturn
If Not fReturn Then CheckRegPermissions = False
oReg.CheckAccess HKLM, sSubKeyName, KEY_CREATE_SUB_KEY, fReturn
If Not fReturn Then CheckRegPermissions = False
oReg.CheckAccess HKLM, sSubKeyName, DELETE, fReturn
If Not fReturn Then CheckRegPermissions = False
End Function 'CheckRegPermissions
'-------------------------------------------------------------------------------
' GetMyProcessId
'
' Returns the process id of the own process
'-------------------------------------------------------------------------------
Function GetMyProcessId()
Dim iParentProcessId
iParentProcessId = 0
' try to obtain from creating a new cscript instance
On Error Resume Next
iParentProcessId = GetObject("winmgmts:root\cimv2").Get("Win32_Process.Handle='" & oWShell.Exec("cscript.exe").ProcessId & "'").ParentProcessId
On Error Goto 0
If iParentProcessId > 0 Then
' succeeded to obtain the process id
GetMyProcessId = iParentProcessId
Exit Function
End If
' failed to obtain the id from the creation of a new instance
' get it from enum of Win32_Process
Dim Process, Processes
Err.Clear
Set Processes = oWmiLocal.ExecQuery("Select * From Win32_Process WHERE Name='cscript.exe' AND CommandLine like '%" & SCRIPTNAME & "%'")
For Each Process in Processes
iParentProcessId = Process.ProcessId
Exit For
Next
GetMyProcessId = iParentProcessId
End Function 'GetMyProcessId
'-------------------------------------------------------------------------------
' Delimiter
'
' Returns the delimiter for a passed in string
'-------------------------------------------------------------------------------
Function Delimiter (sVersion)
Dim iCnt, iAsc
Delimiter = " "
For iCnt = 1 To Len(sVersion)
iAsc = Asc(Mid(sVersion, iCnt, 1))
If Not (iASC >= 48 And iASC <= 57) Then
Delimiter = Mid(sVersion, iCnt, 1)
Exit Function
End If
Next 'iCnt
End Function
'-------------------------------------------------------------------------------
' GetExpandedGuid
'
' Returns the expanded string from a compressed GUID
'-------------------------------------------------------------------------------
Function GetExpandedGuid (sGuid)
Dim i
'Ensure valid length
If NOT Len(sGuid) = 32 Then Exit Function
GetExpandedGuid = "{" & StrReverse(Mid(sGuid,1,8)) & "-" & _
StrReverse(Mid(sGuid,9,4)) & "-" & _
StrReverse(Mid(sGuid,13,4))& "-"
For i = 17 To 20
If i Mod 2 Then
GetExpandedGuid = GetExpandedGuid & mid(sGuid,(i + 1),1)
Else
GetExpandedGuid = GetExpandedGuid & mid(sGuid,(i - 1),1)
End If
Next
GetExpandedGuid = GetExpandedGuid & "-"
For i = 21 To 32
If i Mod 2 Then
GetExpandedGuid = GetExpandedGuid & mid(sGuid,(i + 1),1)
Else
GetExpandedGuid = GetExpandedGuid & mid(sGuid,(i - 1),1)
End If
Next
GetExpandedGuid = GetExpandedGuid & "}"
End Function 'GetExpandedGuid
'-------------------------------------------------------------------------------
' GetCompressedGuid
'
' Returns the compressed string for a GUID
'-------------------------------------------------------------------------------
Function GetCompressedGuid (sGuid)
Dim sCompGUID
Dim i
'Ensure Valid Length
If NOT Len(sGuid) = 38 Then Exit Function
sCompGUID = StrReverse(Mid(sGuid,2,8)) & _
StrReverse(Mid(sGuid,11,4)) & _
StrReverse(Mid(sGuid,16,4))
For i = 21 To 24
If i Mod 2 Then
sCompGUID = sCompGUID & Mid(sGuid, (i + 1), 1)
Else
sCompGUID = sCompGUID & Mid(sGuid, (i - 1), 1)
End If
Next
For i = 26 To 37
If i Mod 2 Then
sCompGUID = sCompGUID & Mid(sGuid, (i - 1), 1)
Else
sCompGUID = sCompGUID & Mid(sGuid, (i + 1), 1)
End If
Next
GetCompressedGuid = sCompGUID
End Function
'-------------------------------------------------------------------------------
' GetDecodedGuid
'
' Returns the GUID from a squished format
'-------------------------------------------------------------------------------
Function GetDecodedGuid(sEncGuid, sGuid)
Dim sDecode, sTable, sHex, iChr
Dim arrTable
Dim i, iAsc, pow85, decChar
Dim lTotal
Dim fFailed
fFailed = False
sTable = "0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff," & _
"0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff," & _
"0xff,0x00,0xff,0xff,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0xff," & _
"0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0xff,0xff,0xff,0x16,0xff,0x17," & _
"0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27," & _
"0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x30,0x31,0x32,0x33,0xff,0x34,0x35,0x36," & _
"0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x40,0x41,0x42,0x43,0x44,0x45,0x46," & _
"0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,0x50,0x51,0x52,0xff,0x53,0x54,0xff"
arrTable = Split(sTable,",")
lTotal = 0 : pow85 = 1
For i = 0 To 19
fFailed = True
If i Mod 5 = 0 Then
lTotal = 0 : pow85 = 1
End If ' i Mod 5 = 0
iAsc = Asc(Mid(sEncGuid,i+1,1))
sHex = arrTable(iAsc)
If iAsc >=128 Then Exit For
If sHex = "0xff" Then Exit For
iChr = CInt("&h"&Right(sHex,2))
lTotal = lTotal + (iChr * pow85)
If i Mod 5 = 4 Then sDecode = sDecode & DecToHex(lTotal)
pow85 = pow85 * 85
fFailed = False
Next 'i
If NOT fFailed Then sGuid = "{"&Mid(sDecode,1,8)&"-"& _
Mid(sDecode,13,4)&"-"& _
Mid(sDecode,9,4)&"-"& _
Mid(sDecode,23,2) & Mid(sDecode,21,2)&"-"& _
Mid(sDecode,19,2) & Mid(sDecode,17,2) & Mid(sDecode,31,2) & Mid(sDecode,29,2) & Mid(sDecode,27,2) & Mid(sDecode,25,2) &"}"
GetDecodedGuid = NOT fFailed
End Function 'GetDecodedGuid
'-------------------------------------------------------------------------------
' DecToHex
'
' Convert a long decimal to hex
'-------------------------------------------------------------------------------
Function DecToHex(lDec)
Dim sHex
Dim iLen
Dim lVal, lExp
Dim arrChr
arrChr = Array("0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F")
sHex = ""
lVal = lDec
lExp = 16^10
While lExp >= 1
If lVal >= lExp Then
sHex = sHex & arrChr(Int(lVal / lExp))
lVal = lVal - lExp * Int(lVal / lExp)
Else
sHex = sHex & "0"
If sHex = "0" Then sHex = ""
End If
lExp = lExp / 16
Wend
iLen = 8 - Len(sHex)
If iLen > 0 Then sHex = String(iLen, "0") & sHex
DecToHex = sHex
End Function
'-------------------------------------------------------------------------------
' RelaunchAs64Host
'
' Relaunch self with 64 bit CScript host
'-------------------------------------------------------------------------------
Sub RelaunchAs64Host
Dim Argument, sCmd
sCmd = Replace(LCase(wscript.Path), "syswow64", "sysnative") & "\cscript.exe " & Chr(34) & WScript.scriptFullName & Chr(34)
If Wscript.Arguments.Count > 0 Then
For Each Argument in Wscript.Arguments
sCmd = sCmd & " " & chr(34) & Argument & chr(34)
Next 'Argument
End If
Wscript.Quit CLng(oWShell.Run(sCmd, 1, True))
End Sub 'RelaunchAs64Host
'-------------------------------------------------------------------------------
' RelaunchElevated
'
' Relaunch the script with elevated permissions
'-------------------------------------------------------------------------------
Sub RelaunchElevated
Dim Argument, Process, Processes
Dim iParentProcessId, iSpawnedProcessId
Dim sCmdLine, sRetValFile, sValue
Dim oShell
SetError ERROR_RELAUNCH
' Shell object for relaunch
Set oShell = CreateObject("Shell.Application")
' build command line for relaunch
sCmdLine = Chr(34) & WScript.ScriptFullName & Chr(34)
If Wscript.Arguments.Count > 0 Then
For Each Argument in Wscript.Arguments
Select Case UCase(Argument)
Case "/Q","/QUIET"
'Don't try to relaunch in quiet mode
Exit Sub
SetError ERROR_ELEVATION_FAILED
Case "UAC"
'Already tried elevated relaunch
SetError ERROR_ELEVATION_FAILED
Exit Sub
Case Else
sCmdLine = sCmdLine & " " & chr(34) & Argument & chr(34)
End Select
Next 'Argument
End If
' prep work to get the return value from the elevated process
iParentProcessId = GetMyProcessId
' ' make user aware of elevation attempt after reboot
' If RegReadValue(HKCU, "SOFTWARE\Microsoft\Office\15.0\CleanC2R", "Rerun", sValue, "REG_DWORD") Then
' oWShell.Popup "System reboot complete. OffScrub will now prompt for elevation!", 10, SCRIPTNAME & " - NOTE!"
' End If
' launch the elevated instance
oShell.ShellExecute "cscript.exe", sCmdLine & " /NoElevate UAC", "", "runas", 1
' get the process id of the spawned instance
WScript.Sleep 500
Set Processes = oWmiLocal.ExecQuery("Select * From Win32_Process WHERE ParentProcessId='" & iParentProcessId & "'")
If Processes.Count > 0 Then
For Each Process in Processes
iSpawnedProcessId = Process.ProcessId
Exit For
Next 'Process
' monitor the tasklist to detect the end of the spawned process
While oWmiLocal.ExecQuery("Select * From Win32_Process WHERE ProcessId='" & iSpawnedProcessId & "'").Count > 0
WScript.Sleep 3000
Wend
' get the return value from the file
Wscript.Quit GetRetValFromFile
End If
' elevation failed (user declined)
SetError ERROR_ELEVATION_USERDECLINED
End Sub 'RelaunchElevated
'-------------------------------------------------------------------------------
' RelaunchAsCScript
'
' Relaunch self with Cscript as host
'-------------------------------------------------------------------------------
Sub RelaunchAsCScript
Dim Argument
Dim sCmdLine
SetError ERROR_RELAUNCH
sCmdLine = "cmd.exe /c " & WScript.Path & "\cscript.exe //NOLOGO " & Chr(34) & WScript.scriptFullName & Chr(34)
If Wscript.Arguments.Count > 0 Then
For Each Argument in Wscript.Arguments
sCmdLine = sCmdLine & " " & chr(34) & Argument & chr(34)
Next 'Argument
End If
Wscript.Quit CLng(oWShell.Run(sCmdLine, 1, True))
End Sub 'RelaunchAsCScript
'-------------------------------------------------------------------------------
' SetError
'
' Set error bit(s)
'-------------------------------------------------------------------------------
Sub SetError(ErrorBit)
iError = iError OR ErrorBit
Select Case ErrorBit
Case ERROR_DCAF_FAILURE, ERROR_STAGE2, ERROR_ELEVATION_USERDECLINED, ERROR_ELEVATION, ERROR_SCRIPTINIT
iError = iError OR ERROR_FAIL
End Select
End Sub
'-------------------------------------------------------------------------------
' ClearError
'
' Unset error bit(s)
'-------------------------------------------------------------------------------
Sub ClearError(ErrorBit)
iError = iError AND (ERROR_ALL - ErrorBit)
Select Case ErrorBit
Case ERROR_ELEVATION_USERDECLINED, ERROR_ELEVATION, ERROR_SCRIPTINIT
iError = iError AND (ERROR_ALL - ERROR_FAIL)
End Select
End Sub
'-------------------------------------------------------------------------------
' SetRetVal
'
' Write return value to file
'-------------------------------------------------------------------------------
Sub SetRetVal(iError)
Dim RetValFileStream
'don't fail script execution if writing the return value to file fails
On Error Resume Next
Set RetValFileStream = oFso.createTextFile(sScrubDir & "\" & RETVALFILE, True, True)
RetValFileStream.Write iError
RetValFileStream.Close
On Error Goto 0
End Sub 'SetRetVal
'-------------------------------------------------------------------------------
' GetRetValFromFile
'
' Read return value from file.
' Used to ensure return value can get obtained from an elevated process
'-------------------------------------------------------------------------------
Function GetRetValFromFile ()
Dim RetValFileStream
Dim iRetValFromFile
On Error Resume Next 'don't fail script execution when getting the return value from file fails
If oFso.FileExists(sScrubDir & "\" & RETVALFILE) Then
Set RetValFileStream = oFso.OpenTextFile(sScrubDir & "\" & RETVALFILE, 1, False, -2)
GetRetValFromFile = RetValFileStream.ReadAll
RetValFileStream.Close
Exit Function
End If
Err.Clear
On Error Goto 0
GetRetValFromFile = ERROR_UNKNOWN
End Function 'GetRetValFromFile
'-------------------------------------------------------------------------------
' CreateLog
'
' Create the removal log file
'-------------------------------------------------------------------------------
Sub CreateLog
Dim DateTime
Dim sLogName
On Error Resume Next
' create the log file
Set DateTime = CreateObject("WbemScripting.SWbemDateTime")
DateTime.SetVarDate Now, True
sLogName = sLogDir & "\" & oWShell.ExpandEnvironmentStrings("%COMPUTERNAME%")
sLogName = sLogName & "_" & Left(DateTime.Value, 14)
sLogName = sLogName & "_ScrubLog.txt"
Err.Clear
Set LogStream = oFso.CreateTextFile(sLogName, True, True)
If Err <> 0 Then
Err.Clear
sLogDir = sScrubDir
sLogName = sLogDir & "\" & oWShell.ExpandEnvironmentStrings("%COMPUTERNAME%")
sLogName = sLogName & "_" & Left(DateTime.Value, 14)
sLogName = sLogName & "_ScrubLog.txt"
Set LogStream = oFso.CreateTextFile(sLogName, True, True)
End If
On Error Goto 0
Log "Microsoft Customer Support Services - " & ONAME & " Removal Utility" & vbCrLf &_
"Removes "& ONAME & " when a regular uninstall is no longer possible." & vbCrLf & vbCrLf & _
"Version: " & vbTab & SCRIPTVERSION & vbCrLf & _
"64 bit OS: " & vbTab & f64 & vbCrLf & _
"Removal start: " & vbTab & Time & vbCrLf
fLogInitialized = True
End Sub 'CreateLog
'-------------------------------------------------------------------------------
' HiveString
'
' Translates the numeric constant into the human readable registry hive string
'-------------------------------------------------------------------------------
Function HiveString(hDefKey)
Select Case hDefKey
Case HKCR : HiveString = "HKEY_CLASSES_ROOT"
Case HKCU : HiveString = "HKEY_CURRENT_USER"
Case HKLM : HiveString = "HKEY_LOCAL_MACHINE"
Case HKU : HiveString = "HKEY_USERS"
Case Else : HiveString = hDefKey
End Select
End Function
'-------------------------------------------------------------------------------
' RegKeyExists
'
' Returns a boolean for the test on existence of a given registry key
'-------------------------------------------------------------------------------
Function RegKeyExists(hDefKey, sSubKeyName)
Dim arrKeys
RegKeyExists = False
If oReg.EnumKey(hDefKey, sSubKeyName, arrKeys) = 0 Then RegKeyExists = True
End Function
'-------------------------------------------------------------------------------
' RegValExists
'
' Returns a boolean for the test on existence of a given registry value
'-------------------------------------------------------------------------------
Function RegValExists(hDefKey,sSubKeyName,sName)
Dim arrValueTypes, arrValueNames
Dim i
RegValExists = False
If Not RegKeyExists(hDefKey,sSubKeyName) Then Exit Function
If oReg.EnumValues(hDefKey,sSubKeyName,arrValueNames,arrValueTypes) = 0 AND IsArray(arrValueNames) Then
For i = 0 To UBound(arrValueNames)
If LCase(arrValueNames(i)) = Trim(LCase(sName)) Then RegValExists = True
Next
End If 'oReg.EnumValues
End Function
'-------------------------------------------------------------------------------
' RegReadValue
'
' Read the value of a given registry entry
' The correct type has to be passed in as argument
'-------------------------------------------------------------------------------
Function RegReadValue(hDefKey, sSubKeyName, sName, sValue, sType)
Dim RetVal
Dim Item
Dim arrValues
Select Case UCase(sType)
Case "1", "REG_SZ"
RetVal = oReg.GetStringValue(hDefKey, sSubKeyName, sName, sValue)
If Not RetVal = 0 AND f64 Then RetVal = oReg.GetStringValue(hDefKey, Wow64Key(hDefKey, sSubKeyName), sName, sValue)
Case "2", "REG_EXPAND_SZ"
RetVal = oReg.GetExpandedStringValue(hDefKey, sSubKeyName, sName, sValue)
If NOT RetVal = 0 AND f64 Then RetVal = oReg.GetExpandedStringValue(hDefKey, Wow64Key(hDefKey, sSubKeyName), sName, sValue)
Case "3", "REG_BINARY"
RetVal = oReg.GetBinaryValue(hDefKey, sSubKeyName, sName, sValue)
If NOT RetVal = 0 AND f64 Then RetVal = oReg.GetBinaryValue(hDefKey, Wow64Key(hDefKey, sSubKeyName), sName, sValue)
Case "4", "REG_DWORD"
RetVal = oReg.GetDWORDValue(hDefKey, sSubKeyName, sName, sValue)
If NOT RetVal = 0 AND f64 Then RetVal = oReg.GetDWORDValue(hDefKey, Wow64Key(hDefKey, sSubKeyName), sName, sValue)
Case "7", "REG_MULTI_SZ"
RetVal = oReg.GetMultiStringValue(hDefKey, sSubKeyName, sName, arrValues)
If NOT RetVal = 0 AND f64 Then RetVal = oReg.GetMultiStringValue(hDefKey, Wow64Key(hDefKey, sSubKeyName), sName, arrValues)
If RetVal = 0 Then sValue = Join(arrValues, chr(13))
Case Else
RetVal = -1
End Select 'sValue
RegReadValue = (RetVal = 0)
End Function 'RegReadValue
'-------------------------------------------------------------------------------
' RegEnumValues
'
' Enumerate a registry key to return all values
'-------------------------------------------------------------------------------
Function RegEnumValues(hDefKey, sSubKeyName, arrNames, arrTypes)
Dim RetVal, RetVal64
Dim arrNames32, arrNames64, arrTypes32, arrTypes64
If f64 Then
RetVal = oReg.EnumValues(hDefKey, sSubKeyName, arrNames32, arrTypes32)
RetVal64 = oReg.EnumValues(hDefKey, Wow64Key(hDefKey, sSubKeyName), arrNames64, arrTypes64)
If (RetVal = 0) AND (NOT RetVal64 = 0) AND IsArray(arrNames32) AND IsArray(arrTypes32) Then
arrNames = arrNames32
arrTypes = arrTypes32
End If
If (NOT RetVal = 0) AND (RetVal64 = 0) AND IsArray(arrNames64) AND IsArray(arrTypes64) Then
arrNames = arrNames64
arrTypes = arrTypes64
End If
If (RetVal = 0) AND (RetVal64 = 0) AND IsArray(arrNames32) AND IsArray(arrNames64) AND IsArray(arrTypes32) AND IsArray(arrTypes64) Then
arrNames = RemoveDuplicates(Split((Join(arrNames32, "\") & "\" & Join(arrNames64, "\")), "\"))
arrTypes = RemoveDuplicates(Split((Join(arrTypes32, "\") & "\" & Join(arrTypes64, "\")), "\"))
End If
Else
RetVal = oReg.EnumValues(hDefKey, sSubKeyName, arrNames, arrTypes)
End If 'f64
RegEnumValues = ((RetVal = 0) OR (RetVal64 = 0)) AND IsArray(arrNames) AND IsArray(arrTypes)
End Function 'RegEnumValues
'-------------------------------------------------------------------------------
' RegEnumKey
'
' Enumerate a registry key to return all subkeys
'-------------------------------------------------------------------------------
Function RegEnumKey(hDefKey, sSubKeyName, arrKeys)
Dim RetVal, RetVal64
Dim arrKeys32, arrKeys64
If f64 Then
RetVal = oReg.EnumKey(hDefKey, sSubKeyName, arrKeys32)
RetVal64 = oReg.EnumKey(hDefKey, Wow64Key(hDefKey, sSubKeyName), arrKeys64)
If (RetVal = 0) AND (NOT RetVal64 = 0) AND IsArray(arrKeys32) Then arrKeys = arrKeys32
If (Not RetVal = 0) AND (RetVal64 = 0) AND IsArray(arrKeys64) Then arrKeys = arrKeys64
If (RetVal = 0) AND (RetVal64 = 0) Then
If IsArray(arrKeys32) AND IsArray (arrKeys64) Then
arrKeys = RemoveDuplicates(Split((Join(arrKeys32, "\") & "\" & Join(arrKeys64, "\")), "\"))
ElseIf IsArray(arrKeys64) Then
arrKeys = arrKeys64
Else
arrKeys = arrKeys32
End If
End If
Else
RetVal = oReg.EnumKey(hDefKey, sSubKeyName, arrKeys)
End If 'f64
RegEnumKey = ((RetVal = 0) OR (RetVal64 = 0)) AND IsArray(arrKeys)
End Function 'RegEnumKey
'-------------------------------------------------------------------------------
' RegDeleteValue
'
' Wrapper around oReg.DeleteValue to handle 64 bit
'-------------------------------------------------------------------------------
Sub RegDeleteValue(hDefKey, sSubKeyName, sName, fRegMultiSZ)
Dim sDelKeyName, sValue
Dim iRetVal
Dim fKeep
' ensure trailing "\"
sSubKeyName = sSubKeyName & "\"
While InStr(sSubKeyName, "\\") > 0
sSubKeyName = Replace(sSubKeyName, "\\", "\")
Wend
fKeep = dictionaryKeepReg.Exists(LCase(sSubKeyName & sName))
If (NOT fKeep AND f64) Then fKeep = dictionaryKeepReg.Exists(LCase(Wow64Key(hDefKey, sSubKeyName) & sName))
If fKeep Then
LogOnly "Disallowing the delete of still required keypath element: " & HiveString(hDefKey) & "\" & sSubKeyName & sName
If NOT fForce Then Exit Sub
End If
' check on forced delete
If fKeep Then
LogOnly "Enforced delete of still required keypath element: " & HiveString(hDefKey) & "\" & sSubKeyName & sName
LogOnly " Remaining applications will need a repair!"
End If
' ensure value exists
If RegValExists(hDefKey, sSubKeyName, sName) Then
sDelKeyName = sSubKeyName
ElseIf RegValExists(hDefKey, Wow64Key(hDefKey, sSubKeyName), sName) Then
sDelKeyName = Wow64Key(hDefKey, sSubKeyName)
Else
LogOnly "Value not found. Cannot delete value: " & HiveString(hDefKey) & "\" & sSubKeyName & sName
Exit Sub
End If
' prevent unintentional, unsafe REG_MULTI_SZ delete
If RegReadValue(hDefKey, sDelKeyName, sName, sValue, "REG_MULTI_SZ") AND NOT fRegMultiSZ Then
LogOnly "Disallowing unsafe delete of REG_MULTI_SZ: " & HiveString(hDefKey) & "\" & sDelKeyName & sName
Exit Sub
End If
' execute delete operation
If Not fDetectOnly Then
LogOnly "Delete registry value: " & HiveString(hDefKey) & "\" & sDelKeyName & " -> " & sName
iRetVal = 0
iRetVal = oReg.DeleteValue(hDefKey, sDelKeyName, sName)
CheckError "RegDeleteValue"
If NOT (iRetVal = 0) Then
LogOnly " Delete failed. Return value: " & iRetVal
SetError ERROR_STAGE2
End If
Else
LogOnly "Preview mode. Disallowing delete registry value: " & HiveString(hDefKey) & "\" & sDelKeyName & " -> " & sName
End If
On Error Goto 0
End Sub 'RegDeleteValue
'-------------------------------------------------------------------------------
' RegDeleteKey
'
' Wrappper around RegDeleteKeyEx to handle 64bit
'-------------------------------------------------------------------------------
Sub RegDeleteKey(hDefKey, sSubKeyName)
Dim sDelKeyName
Dim fKeep
' ensure trailing "\"
sSubKeyName = sSubKeyName & "\"
While InStr(sSubKeyName, "\\") > 0
sSubKeyName = Replace(sSubKeyName, "\\", "\")
Wend
fKeep = dictionaryKeepReg.Exists(LCase(sSubKeyName))
If (NOT fKeep AND f64) Then fKeep = dictionaryKeepReg.Exists(LCase(Wow64Key(hDefKey, sSubKeyName)))
If fKeep Then
LogOnly "Disallowing the delete of still required keypath element: " & HiveString(hDefKey) & "\" & sSubKeyName
If NOT fForce Then Exit Sub
End If
' check on forced delete
If fKeep Then
LogOnly "Enforced delete of still required keypath element: " & HiveString(hDefKey) & "\" & sSubKeyName
LogOnly " Remaining applications will need a repair!"
End If
If Len(sSubKeyName) > 1 Then
'Strip of trailing "\"
sSubKeyName = Left(sSubKeyName, Len(sSubKeyName) - 1)
End If
' ensure key exists
If RegKeyExists(hDefKey, sSubKeyName) Then
sDelKeyName = sSubKeyName
ElseIf f64 AND RegKeyExists(hDefKey, Wow64Key(hDefKey, sSubKeyName)) Then
sDelKeyName = Wow64Key(hDefKey, sSubKeyName)
Else
LogOnly "Key not found. Cannot delete key: " & HiveString(hDefKey) & "\" & sSubKeyName
Exit Sub
End If
' execute delete
If Not fDetectOnly Then
LogOnly "Delete registry key: " & HiveString(hDefKey) & "\" & sDelKeyName
On Error Resume Next
RegDeleteKeyEx hDefKey, sDelKeyName
On Error Goto 0
Else
LogOnly "Preview mode. Disallowing delete of registry key: " & HiveString(hDefKey) & "\" & sSubKeyName
End If
End Sub 'RegDeleteKey
'-------------------------------------------------------------------------------
' RegDeleteKeyEx
'
' Recursively delete a registry structure
'-------------------------------------------------------------------------------
Sub RegDeleteKeyEx(hDefKey, sSubKeyName)
Dim arrSubkeys
Dim sSubkey
Dim iRetVal
'Strip of trailing "\"
If Len(sSubKeyName) > 1 Then
If Right(sSubKeyName, 1) = "\" Then sSubKeyName = Left(sSubKeyName, Len(sSubKeyName) - 1)
End If
On Error Resume Next
' exception handler
If (hDefKey = HKLM) AND (sSubKeyName = "SOFTWARE\Microsoft\Office\15.0\ClickToRun") Then
iRetVal = oWShell.Run("reg delete HKLM\SOFTWARE\Microsoft\Office\15.0\ClickToRun /f", 0, True)
Exit Sub
End If
' regular recursion
oReg.EnumKey hDefKey, sSubKeyName, arrSubkeys
If IsArray(arrSubkeys) Then
For Each sSubkey In arrSubkeys
RegDeleteKeyEx hDefKey, sSubKeyName & "\" & sSubkey
Next
End If
If Not fDetectOnly Then
iRetVal = 0
iRetVal = oReg.DeleteKey(hDefKey, sSubKeyName)
If NOT (iRetVal = 0) Then LogOnly " Delete failed. Return value: "&iRetVal
End If
On Error Goto 0
End Sub 'RegDeleteKeyEx
'-------------------------------------------------------------------------------
' Wow64Key
'
' Return the 32bit regkey location on a 64bit environment
'-------------------------------------------------------------------------------
Function Wow64Key(hDefKey, sSubKeyName)
Dim iPos
Select Case hDefKey
Case HKCU
If Left(sSubKeyName, 17) = "Software\Classes\" Then
Wow64Key = Left(sSubKeyName, 17) & "Wow6432Node\" & Right(sSubKeyName, Len(sSubKeyName) - 17)
Else
iPos = InStr(sSubKeyName, "\")
Wow64Key = Left(sSubKeyName, iPos) & "Wow6432Node\" & Right(sSubKeyName, Len(sSubKeyName) - iPos)
End If
Case HKLM
If Left(sSubKeyName, 17) = "Software\Classes\" Then
Wow64Key = Left(sSubKeyName, 17) & "Wow6432Node\" & Right(sSubKeyName, Len(sSubKeyName) - 17)
Else
iPos = InStr(sSubKeyName, "\")
Wow64Key = Left(sSubKeyName, iPos) & "Wow6432Node\" & Right(sSubKeyName, Len(sSubKeyName) - iPos)
End If
Case Else
Wow64Key = "Wow6432Node\" & sSubKeyName
End Select 'hDefKey
End Function 'Wow64Key
'-------------------------------------------------------------------------------
' RemoveDuplicates
'
' Remove duplicate entries from a one dimensional array
'-------------------------------------------------------------------------------
Function RemoveDuplicates(Array)
Dim Item
Dim dicNoDupes
Set dicNoDupes = CreateObject("Scripting.Dictionary")
For Each Item in Array
If Not dicNoDupes.Exists(Item) Then dicNoDupes.Add Item,Item
Next 'Item
RemoveDuplicates = dicNoDupes.Keys
End Function 'RemoveDuplicates
'-------------------------------------------------------------------------------
' CheckError
'
' Checks the status of 'Err' and logs the error details if <> 0
'-------------------------------------------------------------------------------
Sub CheckError(sModule)
If Err <> 0 Then
LogOnly " Error: " & sModule & " - Source: " & Err.Source & "; Err# (Hex): " & Hex( Err ) & _
"; Err# (Dec): " & Err & "; Description : " & Err.Description
End If 'Err = 0
Err.Clear
End Sub
'-------------------------------------------------------------------------------
' LogH
'
' Write a header log string to the log file
'-------------------------------------------------------------------------------
Sub LogH (sLog)
LogStream.WriteLine ""
sLog = sLog & vbCrLf & String(Len(sLog), "=")
If NOT fQuiet AND fCScript Then wscript.echo ""
If NOT fQuiet AND fCScript Then wscript.echo sLog
LogStream.WriteLine sLog
End Sub 'Logh
'-------------------------------------------------------------------------------
' LogH1
'
' Write a header log string to the log file
'-------------------------------------------------------------------------------
Sub LogH1 (sLog)
LogStream.WriteLine ""
sLog = sLog & vbCrLf & String(Len(sLog), "-")
If NOT fQuiet AND fCScript Then wscript.echo ""
If NOT fQuiet AND fCScript Then wscript.echo sLog
LogStream.WriteLine sLog
End Sub 'LogH1
'-------------------------------------------------------------------------------
' LogH2
'
' Write w/o indent Cmd window and the log file
'-------------------------------------------------------------------------------
Sub LogH2 (sLog)
If NOT fQuiet AND fCScript Then wscript.echo sLog
LogStream.WriteLine ""
LogStream.WriteLine sLog
End Sub 'LogH2
'-------------------------------------------------------------------------------
' Log
'
' Echos the log string to the Cmd window and the log file
'-------------------------------------------------------------------------------
Sub Log (sLog)
If NOT fQuiet AND fCScript Then wscript.echo sLog
LogStream.WriteLine " " & Time & ": " & sLog
End Sub 'Log
'-------------------------------------------------------------------------------
' LogOnly
'
' Commits the log string to the log file
'-------------------------------------------------------------------------------
Sub LogOnly (sLog)
LogStream.WriteLine " " & Time & ": " & sLog
End Sub 'Log
'-------------------------------------------------------------------------------
' InScope
'
' Check if ProductCode is in scope for removal
'-------------------------------------------------------------------------------
'Check if ProductCode is in scope
Function InScope(sProductCode)
Dim fInScope
Dim sProd
Const OFFICEID = "0000000FF1CE}"
On Error Resume Next
fInScope = False
If Len(sProductCode) = 38 Then
sProd = UCase(sProductCode)
If Right(sProd, PRODLEN) = OFFICEID Then
If CInt(Mid(sProd, 4, 2)) > 14 Then
If Err <> 0 Then
Err.Clear
Exit Function
End If
Select Case Mid(sProd, 11, 4)
Case "007E", "008F", "008C", "24E1", "237A"
fInScope = True
End Select
End If
End If
' Microsoft Online Services Sign-in Assistant (x64 ship and x86 ship)
If sProd = "{6C1ADE97-24E1-4AE4-AEDD-86D3A209CE60}" Then fInScope = True
If sProd = "{9520DDEB-237A-41DB-AA20-F2EF2360DCEB}" Then fInScope = True
If sProd = UCase(sPackageGuid) Then fInScope = True
If sProd = UCase("{9AC08E99-230B-47e8-9721-4577B7F124EA}") Then fInScope = True
End If '38
InScope = fInScope
End Function 'InScope
'-------------------------------------------------------------------------------
' CheckDelete
'
' Check a ProductCode is known to stay installed
'-------------------------------------------------------------------------------
Function CheckDelete(sProductCode)
CheckDelete = False
' ensure valid GUID length
If NOT Len(sProductCode) = 38 Then Exit Function
' only care if it's in the expected ProductCode pattern
If NOT InScope(sProductCode) Then Exit Function
' check if it's a known product that should be kept
If dictionaryKeepSku.Exists(UCase(sProductCode)) Then Exit Function
CheckDelete = True
End Function 'CheckDelete
'-------------------------------------------------------------------------------
' DeleteService
'
' Delete a service
'-------------------------------------------------------------------------------
'Delete a service
Sub DeleteService(sName)
Dim Services, srvc, Processes, process
Dim sQuery, sStates, sProcessName, sCmd
Dim iRet
On Error Resume Next
sStates = "STARTED;RUNNING"
sQuery = "Select * From Win32_Service Where Name='" & sName & "'"
Set Services = oWmiLocal.Execquery(sQuery)
' stop and delete the service
For Each srvc in Services
Log " Found service " & sName & " (" & srvc.DisplayName & ") in state " & srvc.State
' get the process name
sProcessName = Trim(Replace(Mid(srvc.PathName, InStrRev(srvc.PathName,"\") + 1), chr(34), ""))
' stop the service
If InStr(sStates, UCase(srvc.State)) > 0 Then
iRet = srvc.StopService()
LogOnly " attempt to stop service " & sName & " returned: " & iRet
End If
' ensure no more instances of the service are running
Set Processes = oWmiLocal.ExecQuery("Select * From Win32_Process Where Name='" & sProcessName & "'")
For Each process in Processes
iRet = process.Terminate()
Next 'Process
If fDetectOnly Then
Log " Not deleting service " & sName & " in preview mode"
Exit Sub
End If
iRet = srvc.Delete()
Log " Delete service " & sName & " returned: " & iRet
Next 'srvc
' check if service got deleted
Set Services = oWmiLocal.Execquery(sQuery)
For Each srvc in Services
' failed to delete service. retry with 'sc' command
sLog " Deleting service " & sName & " failed. Retry delete using 'SC' command"
sCmd = "sc delete " & sName
iRet = oWShell.Run(sCmd, 0, True)
Next 'srvc
Set Services = Nothing
Err.Clear
On Error Goto 0
End Sub 'DeleteService
'-------------------------------------------------------------------------------
' SetupRetVal
'
' Translation for known uninstall return values
'-------------------------------------------------------------------------------
Function SetupRetVal(RetVal)
Select Case RetVal
Case 0 : SetupRetVal = "Success"
'msiexec return values
Case 1259 : SetupRetVal = "APPHELP_BLOCK"
Case 1601 : SetupRetVal = "INSTALL_SERVICE_FAILURE"
Case 1602 : SetupRetVal = "INSTALL_USEREXIT"
Case 1603 : SetupRetVal = "INSTALL_FAILURE"
Case 1604 : SetupRetVal = "INSTALL_SUSPEND"
Case 1605 : SetupRetVal = "UNKNOWN_PRODUCT"
Case 1606 : SetupRetVal = "UNKNOWN_FEATURE"
Case 1607 : SetupRetVal = "UNKNOWN_COMPONENT"
Case 1608 : SetupRetVal = "UNKNOWN_PROPERTY"
Case 1609 : SetupRetVal = "INVALID_HANDLE_STATE"
Case 1610 : SetupRetVal = "BAD_CONFIGURATION"
Case 1611 : SetupRetVal = "INDEX_ABSENT"
Case 1612 : SetupRetVal = "INSTALL_SOURCE_ABSENT"
Case 1613 : SetupRetVal = "INSTALL_PACKAGE_VERSION"
Case 1614 : SetupRetVal = "PRODUCT_UNINSTALLED"
Case 1615 : SetupRetVal = "BAD_QUERY_SYNTAX"
Case 1616 : SetupRetVal = "INVALID_FIELD"
Case 1618 : SetupRetVal = "INSTALL_ALREADY_RUNNING"
Case 1619 : SetupRetVal = "INSTALL_PACKAGE_OPEN_FAILED"
Case 1620 : SetupRetVal = "INSTALL_PACKAGE_INVALID"
Case 1621 : SetupRetVal = "INSTALL_UI_FAILURE"
Case 1622 : SetupRetVal = "INSTALL_LOG_FAILURE"
Case 1623 : SetupRetVal = "INSTALL_LANGUAGE_UNSUPPORTED"
Case 1624 : SetupRetVal = "INSTALL_TRANSFORM_FAILURE"
Case 1625 : SetupRetVal = "INSTALL_PACKAGE_REJECTED"
Case 1626 : SetupRetVal = "FUNCTION_NOT_CALLED"
Case 1627 : SetupRetVal = "FUNCTION_FAILED"
Case 1628 : SetupRetVal = "INVALID_TABLE"
Case 1629 : SetupRetVal = "DATATYPE_MISMATCH"
Case 1630 : SetupRetVal = "UNSUPPORTED_TYPE"
Case 1631 : SetupRetVal = "CREATE_FAILED"
Case 1632 : SetupRetVal = "INSTALL_TEMP_UNWRITABLE"
Case 1633 : SetupRetVal = "INSTALL_PLATFORM_UNSUPPORTED"
Case 1634 : SetupRetVal = "INSTALL_NOTUSED"
Case 1635 : SetupRetVal = "PATCH_PACKAGE_OPEN_FAILED"
Case 1636 : SetupRetVal = "PATCH_PACKAGE_INVALID"
Case 1637 : SetupRetVal = "PATCH_PACKAGE_UNSUPPORTED"
Case 1638 : SetupRetVal = "PRODUCT_VERSION"
Case 1639 : SetupRetVal = "INVALID_COMMAND_LINE"
Case 1640 : SetupRetVal = "INSTALL_REMOTE_DISALLOWED"
Case 1641 : SetupRetVal = "SUCCESS_REBOOT_INITIATED"
Case 1642 : SetupRetVal = "PATCH_TARGET_NOT_FOUND"
Case 1643 : SetupRetVal = "PATCH_PACKAGE_REJECTED"
Case 1644 : SetupRetVal = "INSTALL_TRANSFORM_REJECTED"
Case 1645 : SetupRetVal = "INSTALL_REMOTE_PROHIBITED"
Case 1646 : SetupRetVal = "PATCH_REMOVAL_UNSUPPORTED"
Case 1647 : SetupRetVal = "UNKNOWN_PATCH"
Case 1648 : SetupRetVal = "PATCH_NO_SEQUENCE"
Case 1649 : SetupRetVal = "PATCH_REMOVAL_DISALLOWED"
Case 1650 : SetupRetVal = "INVALID_PATCH_XML"
Case 3010 : SetupRetVal = "SUCCESS_REBOOT_REQUIRED"
Case Else : SetupRetVal = "Unknown Return Value"
End Select
End Function 'SetupRetVal
'-------------------------------------------------------------------------------
' DeleteFile
'
' Wrapper to delete a file
'-------------------------------------------------------------------------------
Sub DeleteFile(sFile)
Dim File, attr
Dim sDelFile, sFileName, sNewPath
Dim fKeep
On Error Resume Next
fKeep = dictionaryKeepFolder.Exists(LCase(sFile))
If (NOT fKeep AND f64) Then fKeep = dictionaryKeepFolder.Exists(LCase(Wow64Folder(sFile)))
If fKeep Then
LogOnly "Disallowing the delete of still required keypath element: " & sFile
If NOT fForce Then Exit Sub
End If
' check on forced delete
If fKeep Then
LogOnly "Enforced delete of still required keypath element: " & sFile
LogOnly " Remaining applications will need a repair!"
End If
If oFso.FileExists(sFile) Then
sDelFile = sFile
ElseIf f64 AND oFso.FileExists(Wow64Folder(sFile)) Then
sDelFile = Wow64Folder(sFile)
Else
LogOnly "Path not found. Cannot not delete folder: " & sFile
Exit Sub
End If
If Not fDetectOnly Then
LogOnly "Delete file: " & sDelFile
Set File = oFso.GetFile(sDelFile)
' ensure read-only flag is not set
attr = File.Attributes
If CBool(attr AND 1) Then File.Attributes = attr AND (attr - 1)
' add folder to empty folder cleanup list
If NOT dicDelFolder.Exists(File.ParentFolder.Path) Then dicDelFolder.Add File.ParentFolder.Path, File.ParentFolder.Path
' delete the file
sFile = File.Path
File.Delete True
Set File = Nothing
If Err <> 0 Then
CheckError "DeleteFile"
' schedule file for delete on next reboot
ScheduleDeleteFile sFile
End If 'Err <> 0
Else
LogOnly "Preview mode. Disallowing delete for folder: " & sDelFile
End If
On Error Goto 0
End Sub 'DeleteFile
'-------------------------------------------------------------------------------
' DeleteFolder
'
' Wrapper to delete a folder
'-------------------------------------------------------------------------------
Sub DeleteFolder(sFolder)
Dim Folder, fld, attr
Dim sDelFolder, sFolderName, sNewPath, sCmd
Dim fKeep
' ensure trailing "\"
' trailing \ is required for dictionaryKeepFolder comparisons
sFolder = sFolder & "\"
While InStr(sFolder,"\\")>0
sFolder = Replace(sFolder,"\\","\")
Wend
' prevent delete of folders that are known to be still required
fKeep = dictionaryKeepFolder.Exists(LCase(sFolder))
If (NOT fKeep AND f64) Then fKeep = dictionaryKeepFolder.Exists(LCase(Wow64Folder(sFolder)))
If fKeep Then
LogOnly "Disallowing the delete of still required keypath element: " & sFolder
If NOT fForce Then Exit Sub
End If
' check on forced delete
If fKeep Then
LogOnly "Enforced delete of still required keypath element: " & sFolder
LogOnly " Remaining applications will need a repair!"
End If
' strip trailing "\"
If Len(sFolder) > 1 Then
sFolder = Left(sFolder, Len(sFolder) - 1)
End If
On Error Resume Next
If oFso.FolderExists(sFolder) Then
sDelFolder = sFolder
ElseIf f64 AND oFso.FolderExists(Wow64Folder(sFolder)) Then
sDelFolder = Wow64Folder(sFolder)
Else
LogOnly "Path not found. Cannot not delete folder: " & sFolder
Exit Sub
End If
If Not fDetectOnly Then
LogOnly "Delete folder: " & sDelFolder
Set Folder = oFso.GetFolder(sDelFolder)
' ensure to remove read only flag
attr = Folder.Attributes
If CBool(attr AND 1) Then Folder.Attributes = attr AND (attr - 1)
' add to empty folder cleanup list
If NOT dicDelFolder.Exists(Folder.Path) Then dicDelFolder.Add Folder.Path, Folder.Path
' delete the folder
' for performance reasons try 'rd' first
Set Folder = Nothing
sCmd = "cmd.exe /c rd /s " & chr(34) & sDelFolder & chr(34) & " /q"
oWShell.Run sCmd, 0, True
If NOT oFso.FolderExists(sDelFolder) Then Exit Sub
' rd didn't work check with FileSystemObject
Set Folder = oFso.GetFolder(sDelFolder)
Folder.Delete True
Set Folder = Nothing
' error handling
If Err <> 0 Then
Select Case Err
Case 70
' Access Denied
' Retry after closing running processes
CheckError "DeleteFolder"
If NOT fRerun Then
CloseOfficeApps
' attempt 'rd' command
LogOnly " Attempt to remove with 'rd' command"
sCmd = "cmd.exe /c rd /s " & chr(34) & sDelFolder & chr(34) & " /q"
oWShell.Run sCmd, 0, True
If NOT oFso.FolderExists(sDelFolder) Then Exit Sub
End If
Case 76
' check on invalid path lengt issues Err 76 (0x4C) "Path not found"
' attempt 'rd' command
CheckError "DeleteFolder"
LogOnly " Attempt to remove with 'rd' command"
sCmd = "cmd.exe /c rd /s " & chr(34) & sDelFolder & chr(34) & " /q"
oWShell.Run sCmd, 0, True
If NOT oFso.FolderExists(sDelFolder) Then Exit Sub
End Select
' stil failed!
Log " Failed to delete folder: " & sDelFolder
CheckError "DeleteFolder"
' try to delete as many folder contents as possible
' before the recursive error handling is called
Set Folder = oFso.GetFolder(sDelFolder)
For Each fld in Folder.Subfolders
sCmd = "cmd.exe /c rd /s " & chr(34) & fld.Path & chr(34) & " /q"
oWShell.Run sCmd, 0, True
Next 'fld
sCmd = "cmd.exe /c del " & chr(34) & fld.Path & "\*.*" & chr(34)
oWShell.Run sCmd, 0, True
Set Folder = Nothing
' schedule an additional run of the tool after reboot
If NOT fRerun Then Rerun
' schedule folder for delete on next reboot
ScheduleDeleteFolder sDelFolder
End If 'Err <> 0
Else
LogOnly "Preview mode. Disallowing delete of folder: " & sDelFolder
End If
On Error Goto 0
End Sub 'DeleteFolder
Sub DeleteFolder_WMI (sFolder)
Dim Folder, Folders
Dim sWqlFolder
Dim iRet
sWqlFolder = Replace(sFolder, "\", "\\")
Set Folders = oWmiLocal.ExecQuery ("Select * from Win32_Directory where name = '" & sWqlFolder & "'")
For Each Folder in Folders
iRet = Folder.Delete
Next 'Folder
LogOnly " Delete (wmi) for folder " & sFolder & " returned: " & iRet
End Sub
'-------------------------------------------------------------------------------
' Wow64Folder
'
' Returns the WOW folder structure to handle folder-path operations on
' 64 bit environments
'-------------------------------------------------------------------------------
Function Wow64Folder(sFolder)
If LCase(Left(sFolder, Len(sWinDir & "\System32"))) = LCase(sWinDir & "\System32") Then
Wow64Folder = sWinDir & "\syswow64" & Right(sFolder, Len(sFolder) - Len(sWinDir & "\System32"))
ElseIf LCase(Left(sFolder, Len(sProgramFiles))) = LCase(sProgramFiles) Then
Wow64Folder = sProgramFilesX86 & Right(sFolder, Len(sFolder) - Len(sProgramFiles))
Else
Wow64Folder = "?" 'Return invalid string to ensure the folder cannot exist
End If
End Function 'Wow64Folder
'-------------------------------------------------------------------------------
' ScheduleDeleteFile
'
' Adds a file to the list of items to delete on reboot
'-------------------------------------------------------------------------------
Sub ScheduleDeleteFile (sFile)
If NOT dicDelInUse.Exists(sFile) Then dicDelInUse.Add sFile, sFile Else Exit Sub
LogOnly "Adding file in use for delete on reboot: " & sFile
fRebootRequired = True
SetError ERROR_REBOOT_REQUIRED
End Sub 'ScheduleDeleteFile
'-------------------------------------------------------------------------------
' ScheduleDeleteFolder
'
' Recursively adds a folder and its contents to the list of
' items to delete on reboot
'-------------------------------------------------------------------------------
Sub ScheduleDeleteFolder (sFolder)
Dim oFolder, fld, file, attr
Set oFolder = oFso.GetFolder(sFolder)
' exclude hidden system folders
attr = oFolder.Attributes
If CBool(attr AND 6) Then Exit Sub
For Each fld In oFolder.SubFolders
DeleteFolder fld.Path
Next
For Each file In oFolder.Files
DeleteFile file.Path
Next
If NOT dicDelInUse.Exists(oFolder.Path) Then dicDelInUse.Add oFolder.Path, "" Else Exit Sub
LogOnly "Adding folder for delete on reboot: " & oFolder.Path
fRebootRequired = True
SetError ERROR_REBOOT_REQUIRED
End Sub 'ScheduleDeleteFile
'-------------------------------------------------------------------------------
' ScheduleDeleteEx
'
' Schedules the delete of files/folders in use on next reboot by adding
' affected files/folders to the PendingFileRenameOperations registry entry
'-------------------------------------------------------------------------------
Sub ScheduleDeleteEx ()
Dim key, hDefKey, sKeyName, sValueName
Dim i
Dim arrData
hDefKey = HKLM
sKeyName = "SYSTEM\CurrentControlSet\Control\Session Manager"
sValueName = "PendingFileRenameOperations"
LogH2 "Adding " & dicDelInUse.Count & " PendingFileRenameOperations"
If NOT RegValExists(hDefKey, sKeyName, sValueName) Then
ReDim arrData(-1)
Else
oReg.GetMultiStringValue hDefKey, sKeyName, sValueName, arrData
End If
i = UBound(arrData) + 1
ReDim Preserve arrData(UBound(arrData) + (dicDelInUse.Count * 2))
For Each key in dicDelInUse.Keys
LogOnly " " & key
arrData(i) = "\??\" & key
arrData(i + 1) = ""
i = i + 2
Next 'key
oReg.SetMultiStringValue hDefKey, sKeyName, sValueName, arrData
End Sub 'ScheduleDeleteEx
'-------------------------------------------------------------------------------
' DeleteEmptyFolders
'
' Deletes an individual folder structure if empty
'-------------------------------------------------------------------------------
Sub DeleteEmptyFolder (sFolder)
Dim Folder
' cosmetic' task don't fail on error
On Error Resume Next
If oFso.FolderExists(sFolder) Then
Set Folder = oFso.GetFolder(sFolder)
If (Folder.Subfolders.Count = 0) AND (Folder.Files.Count = 0) Then
Set Folder = Nothing
SmartDeleteFolder sFolder
End If
End If
CheckError "DeleteEmptyFolder"
On Error Goto 0
End Sub 'DeleteEmptyFolders
'-------------------------------------------------------------------------------
' DeleteEmptyFolders
'
' Delete an empty folder structure
'-------------------------------------------------------------------------------
Sub DeleteEmptyFolders
Dim Folder
Dim sFolder
' cosmetic' task don't fail on error
On Error Resume Next
DeleteEmptyFolder sCommonProgramFiles & "\Microsoft Shared\Office15"
DeleteEmptyFolder sCommonProgramFiles & "\Microsoft Shared\Office16"
DeleteEmptyFolder sCommonProgramFiles & "\Microsoft Shared\"
DeleteEmptyFolder sProgramFiles & "\Microsoft Office\Office15"
DeleteEmptyFolder sProgramFiles & "\Microsoft Office\Office16"
For Each sFolder in dicDelFolder.Keys
If oFso.FolderExists(sFolder) Then
Set Folder = oFso.GetFolder(sFolder)
If (Folder.Subfolders.Count = 0) AND (Folder.Files.Count = 0) Then
Set Folder = Nothing
SmartDeleteFolder sFolder
End If
End If
Next 'sFolder
CheckError "DeleteEmptyFolders"
On Error Goto 0
End Sub 'DeleteEmptyFolders
'-------------------------------------------------------------------------------
' SmartDeleteFolder
'
' Wrapper to delete a folder and the empty parent folder structure
'-------------------------------------------------------------------------------
Sub SmartDeleteFolder(sFolder)
Dim sDelFolder
If oFso.FolderExists(sFolder) Then
sDelFolder = sFolder
ElseIf f64 AND oFso.FolderExists(Wow64Folder(sFolder)) Then
sDelFolder = Wow64Folder(sFolder)
Else
Exit Sub
End If
If Not fDetectOnly Then
LogOnly "Request SmartDelete for folder: " & sDelFolder
SmartDeleteFolderEx sDelFolder
Else
LogOnly "Preview mode. Disallowing SmartDelete request for folder: " & sDelFolder
End If
End Sub 'SmartDeleteFolder
'-------------------------------------------------------------------------------
' SmartDeleteFolderEx
'
' Executes the folder delete operation(s)
'-------------------------------------------------------------------------------
Sub SmartDeleteFolderEx(sFolder)
Dim Folder
On Error Resume Next
DeleteFolder sFolder : CheckError "SmartDeleteFolderEx"
On Error Goto 0
Set Folder = oFso.GetFolder(oFso.GetParentFolderName(sFolder))
If (Folder.Subfolders.Count = 0) AND (Folder.Files.Count = 0) Then SmartDeleteFolderEx(Folder.Path)
End Sub 'SmartDeleteFolderEx
'-------------------------------------------------------------------------------
' RestoreExplorer
'
' Ensure Windows Explorer is restarted if needed
'-------------------------------------------------------------------------------
Sub RestoreExplorer
Dim Processes, Result, oAT, DateTime, JobID
Dim sCmd
'Non critical routine. Don't fail on error
On Error Resume Next
wscript.sleep 1000
Set Processes = oWmiLocal.ExecQuery("Select * From Win32_Process Where Name='explorer.exe'")
If Processes.Count < 1 Then
oWShell.Run "explorer.exe"
'To handle this in case of System context, schedule and run as interactive task
oWShell.Run "SCHTASKS /Create /TN OffScrEx /TR explorer /SC ONCE /ST 12:00 /IT", 0, True
oWShell.Run "SCHTASKS /Run /TN OffScrEx", 0, True
oWShell.Run "SCHTASKS /Delete /TN OffScrEx /F", 0, False
End If
On Error Goto 0
End Sub 'RestoreExploer
'-------------------------------------------------------------------------------
' MyJoin
'
' Replacement function to the internal Join function to prevent failures
' that were seen in some instances
'-------------------------------------------------------------------------------
Function MyJoin(arrToJoin, sSeparator)
Dim sJoined
Dim i
sJoined = ""
If IsArray(arrToJoin) Then
For i = 0 To UBound(arrToJoin)
sJoined = sJoined & arrToJoin(i) & sSeparator
Next 'i
End If
If Len(sJoined) > 1 Then sJoined = Left(sJoined, Len(sJoined) - 1)
MyJoin = sJoined
End Function
'-------------------------------------------------------------------------------
' Rerun
'
' Flag need for reboot and schedule autorun to run the tool again on reboot.
'-------------------------------------------------------------------------------
Sub Rerun ()
Dim sValue
' check if Rerun has already been called
If fRerun Then Exit Sub
' set Rerun flag
fRerun = True
' check if the previous run already initiated the Rerun
If RegReadValue(HKCU, "SOFTWARE\Microsoft\Office\15.0\CleanC2R", "Rerun", sValue, "REG_DWORD") Then
' Rerun has already been tried
LogH2 "Error: Removal failed"
SetError ERROR_DCAF_FAILURE
Exit Sub
End If
fRebootRequired = True
SetError ERROR_REBOOT_REQUIRED
SetError ERROR_INCOMPLETE
' cache the script to the local scrub folder
oFso.CopyFile WScript.scriptFullName, sScrubDir & "\" & SCRIPTFILE
oReg.CreateKey HKLM, "SOFTWARE"
oReg.CreateKey HKLM, "SOFTWARE\Microsoft"
oReg.CreateKey HKLM, "SOFTWARE\Microsoft\Office"
oReg.CreateKey HKLM, "SOFTWARE\Microsoft\Office\15.0"
oReg.CreateKey HKLM, "SOFTWARE\Microsoft\Office\15.0\CleanC2R"
oReg.SetDWordValue HKLM, "SOFTWARE\Microsoft\Office\15.0\CleanC2R", "Rerun", 1
fSetRunOnce = True
' oReg.CreateKey HKCU, "SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce"
' oReg.SetStringValue HKCU, "SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce", "CleanC2R", "cscript.exe " & chr(34) & sScrubDir & "\" & SCRIPTFILE & chr(34)
End Sub
'-------------------------------------------------------------------------------
' SetRunOnce
'
' Create a RunOnce entry to resume setup after a reboot
'-------------------------------------------------------------------------------
Sub SetRunOnce
Dim sValue
oReg.CreateKey HKLM, "SOFTWARE\Microsoft\Windows\CurrentVersion"
oReg.CreateKey HKLM, "SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce"
sValue = "cscript.exe " & chr(34) & sScrubDir & "\" & SCRIPTFILE & chr(34) & " /NoElevate /Relaunched"
oReg.SetStringValue HKLM, "SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce", "O15CleanUp", sValue
End Sub 'SetRunOnce
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment