Guest User

OffScrub13.vbs

a guest
Jul 16th, 2014
1,578
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
VB.NET 170.97 KB | None | 0 0
  1. '=======================================================================================================
  2. ' Name: OffScrub_O15msi.vbs
  3. ' Author: Microsoft Customer Support Services
  4. ' Copyright (c) 2011, 2012 Microsoft Corporation
  5. ' Script to remove (scrub) Office 2013 MSI products
  6. ' when a regular uninstall is no longer possible
  7. '=======================================================================================================
  8. Option Explicit
  9.  
  10. Const SCRIPTVERSION = "1.72"
  11. Const SCRIPTFILE    = "OffScrub_O15msi.vbs"
  12. Const SCRIPTNAME    = "OffScrub_O15msi"
  13. Const OVERSION      = "15.0"
  14. Const OVERSIONMAJOR = "15"
  15. Const OREF          = "Office15"
  16. Const OREGREF       = "OFFICE15."
  17. Const ONAME         = "Office 2013 MSI"
  18. Const OPACKAGE      = "PackageRefs"
  19. Const OFFICEID      = "000000FF1CE}"
  20. Const HKCR          = &H80000000
  21. Const HKCU          = &H80000001
  22. Const HKLM          = &H80000002
  23. Const HKU           = &H80000003
  24. Const FOR_WRITING   = 2
  25. Const PRODLEN       = 12
  26. Const COMPPERMANENT = "00000000000000000000000000000000"
  27. Const UNCOMPRESSED  = 38
  28. Const SQUISHED      = 20
  29. Const COMPRESSED    = 32
  30. Const REG_ARP       = "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\"
  31. Const VB_YES        = 6
  32. Const MSIOPENDATABASEREADONLY = 0
  33.  
  34. '=======================================================================================================
  35. Dim oFso, oMsi, oReg, oWShell, oWmiLocal, oShellApp
  36. Dim ComputerItem, Item, LogStream, TmpKey
  37. Dim arrTmpSKUs, arrDeleteFiles, arrDeleteFolders, arrMseFolders, arrVersion
  38. Dim dicKeepProd, dicKeepLis, dicApps, dicKeepFolder, dicDelRegKey, dicKeepReg
  39. Dim dicInstalledSku, dicRemoveSku, dicKeepSku, dicSrv, dicCSuite, dicCSingle
  40. Dim f64, fLegacyProductFound
  41. Dim sErr, sTmp, sSkuRemoveList, sDefault, sWinDir, sWICacheDir, sMode
  42. Dim sAppData, sTemp, sScrubDir, sProgramFiles, sProgramFilesX86, sCommonProgramFiles
  43. Dim sAllusersProfile, sOSinfo, sOSVersion, sCommonProgramFilesX86, sProfilesDirectory
  44. Dim sProgramData, sLocalAppData, sOInstallRoot
  45. Dim iVersionNT
  46.  
  47. '=======================================================================================================
  48. 'Main
  49. '=======================================================================================================
  50. 'Configure defaults
  51. Dim sLogDir : sLogDir = ""
  52. Dim sMoveMessage: sMoveMessage = ""
  53. Dim fRemoveOse      : fRemoveOse = False
  54. Dim fRemoveOspp     : fRemoveOspp = False
  55. Dim fRemoveAll      : fRemoveAll = False
  56. Dim fRemoveCSuites  : fRemoveCSuites = False
  57. Dim fRemoveCSingle  : fRemoveCSingle = False
  58. Dim fRemoveSrv      : fRemoveSrv = False
  59. Dim fKeepUser       : fKeepUser = True  'Default to keep per user settings
  60. Dim fSkipSD         : fSkipSD = False 'Default to not Skip the Shortcut Detection
  61. Dim fDetectOnly     : fDetectOnly = False
  62. Dim fQuiet          : fQuiet = False
  63. Dim fBasic          : fBasic = False
  64. Dim fNoCancel       : fNoCancel = False
  65. Dim fNoElevate      : fNoElevate = False
  66. Dim fIsElevated     : fIsElevated = False
  67. Dim fTryReconcile   : fTryReconcile = False
  68. 'CAUTION! -> "fForce" will kill running applications which can result in data loss! <- CAUTION
  69. Dim fForce          : fForce = False
  70. 'CAUTION! -> "fForce" will kill running applications which can result in data loss! <- CAUTION
  71. Dim fLogInitialized : fLogInitialized = False
  72. Dim fBypass_Stage1  : fBypass_Stage1 = True 'Component Detection
  73. Dim fBypass_Stage2  : fBypass_Stage2 = False 'Setup
  74. Dim fBypass_Stage3  : fBypass_Stage3 = False 'Msiexec
  75. Dim fBypass_Stage4  : fBypass_Stage4 = False 'CleanUp
  76. Dim fRebootRequired : fRebootRequired = False
  77.  
  78. 'Create required objects
  79. Set oWmiLocal   = GetObject("winmgmts:{(Debug)}\\.\root\cimv2")
  80. Set oWShell     = CreateObject("Wscript.Shell")
  81. Set oShellApp   = CreateObject("Shell.Application")
  82. Set oFso        = CreateObject("Scripting.FileSystemObject")
  83. Set oMsi        = CreateObject("WindowsInstaller.Installer")
  84. Set oReg        = GetObject("winmgmts:\\.\root\default:StdRegProv")
  85.  
  86. 'Get environment path info
  87. sAppData            = oWShell.ExpandEnvironmentStrings("%appdata%")
  88. sLocalAppData       = oWShell.ExpandEnvironmentStrings("%localappdata%")
  89. sTemp               = oWShell.ExpandEnvironmentStrings("%temp%")
  90. sAllUsersProfile    = oWShell.ExpandEnvironmentStrings("%allusersprofile%")
  91. RegReadValue HKLM, "SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList", "ProfilesDirectory", sProfilesDirectory, "REG_EXPAND_SZ"
  92. If NOT oFso.FolderExists(sProfilesDirectory) Then
  93.     sProfilesDirectory  = oFso.GetParentFolderName(oWShell.ExpandEnvironmentStrings("%userprofile%"))
  94. End If
  95. sProgramFiles       = oWShell.ExpandEnvironmentStrings("%programfiles%")
  96. 'Deferred until after architecture check
  97. 'sProgramFilesX86 = oWShell.ExpandEnvironmentStrings("%programfiles(x86)%")
  98.  
  99. sCommonProgramFiles = oWShell.ExpandEnvironmentStrings("%commonprogramfiles%")
  100. 'Deferred until after architecture check
  101. 'sCommonProgramFilesX86 = oWShell.ExpandEnvironmentStrings("%CommonProgramFiles(x86)%")
  102.  
  103. sProgramData        = oWSHell.ExpandEnvironmentStrings("%programdata%")
  104. sWinDir             = oWShell.ExpandEnvironmentStrings("%windir%")
  105. sWICacheDir         = sWinDir & "\" & "Installer"
  106. sScrubDir           = sTemp & "\" & SCRIPTNAME
  107.  
  108. 'Detect if we're running on a 64 bit OS
  109. Set ComputerItem = oWmiLocal.ExecQuery("Select * from Win32_ComputerSystem")
  110. For Each Item In ComputerItem
  111.     f64 = Instr(Left(Item.SystemType,3),"64") > 0
  112.     If f64 Then Exit For
  113. Next
  114. If f64 Then sProgramFilesX86 = oWShell.ExpandEnvironmentStrings("%programfiles(x86)%")
  115. If f64 Then sCommonProgramFilesX86 = oWShell.ExpandEnvironmentStrings("%CommonProgramFiles(x86)%")
  116.  
  117. 'Get OS details and VersionNT
  118. Set ComputerItem =oWmiLocal.ExecQuery("Select * from Win32_OperatingSystem")
  119. For Each Item in ComputerItem
  120.     sOSinfo = sOSinfo & Item.Caption
  121.     sOSinfo = sOSinfo & Item.OtherTypeDescription
  122.     sOSinfo = sOSinfo & ", " & "SP " & Item.ServicePackMajorVersion
  123.     sOSinfo = sOSinfo & ", " & "Version: " & Item.Version
  124.     sOsVersion = Item.Version
  125.     sOSinfo = sOSinfo & ", " & "Codepage: " & Item.CodeSet
  126.     sOSinfo = sOSinfo & ", " & "Country Code: " & Item.CountryCode
  127.     sOSinfo = sOSinfo & ", " & "Language: " & Item.OSLanguage
  128. Next
  129.  
  130. 'Build the VersionNT number
  131. arrVersion = Split(sOsVersion,Delimiter(sOsVersion))
  132. iVersionNt = CInt(arrVersion(0))*100 + CInt(arrVersion(1))
  133.  
  134. fIsElevated = CheckRegPermissions
  135. If NOT fIsElevated AND NOT fNoElevate Then
  136.     'Try to relaunch elevated
  137.     RelaunchElevated
  138.  
  139.     'Can't relaunch. Exit out
  140.     If UCase(Mid(Wscript.FullName, Len(Wscript.Path) + 2, 1)) = "C" Then
  141.         If Not fLogInitialized Then CreateLog
  142.         Log "Insufficient registry access permissions - exiting"
  143.     End If
  144.     'Undo temporary entries created in ARP
  145.     TmpKeyCleanUp
  146.     wscript.quit
  147. End If
  148.  
  149. 'Ensure CScript as engine
  150. If Not UCase(Mid(Wscript.FullName, Len(Wscript.Path) + 2, 1)) = "C" Then RelaunchAsCScript
  151.  
  152. 'Create Dictionaries
  153. Set dicKeepProd = CreateObject("Scripting.Dictionary")
  154. Set dicInstalledSku = CreateObject("Scripting.Dictionary")
  155. Set dicRemoveSku = CreateObject("Scripting.Dictionary")
  156. Set dicKeepSku = CreateObject("Scripting.Dictionary")
  157. Set dicKeepLis = CreateObject("Scripting.Dictionary")
  158. Set dicKeepFolder = CreateObject("Scripting.Dictionary")
  159. Set dicApps = CreateObject("Scripting.Dictionary")
  160. Set dicDelRegKey = CreateObject("Scripting.Dictionary")
  161. Set dicKeepReg = CreateObject("Scripting.Dictionary")
  162. Set dicSrv = CreateObject("Scripting.Dictionary")
  163. Set dicCSuite = CreateObject("Scripting.Dictionary")
  164. Set dicCSingle = CreateObject("Scripting.Dictionary")
  165.  
  166. 'Create the temp folder
  167. If Not oFso.FolderExists(sScrubDir) Then oFso.CreateFolder sScrubDir
  168.  
  169. 'Set the default logging directory
  170. sLogDir = sScrubDir
  171.  
  172. 'Call the command line parser
  173. ParseCmdLine
  174.  
  175. 'Get Office Install Folder
  176. If NOT RegReadValue(HKLM,"SOFTWARE\Microsoft\Office\"&OVERSION&"\Common\InstallRoot","Path",sOInstallRoot,"REG_SZ") Then
  177.     sOInstallRoot = sProgramFiles & "\Microsoft Office\"&OREF
  178. End If
  179.  
  180. 'Ensure integrity of WI metadata which could fail used APIs otherwise
  181. EnsureValidWIMetadata HKCU,"Software\Classes\Installer\Products",COMPRESSED
  182. EnsureValidWIMetadata HKCR,"Installer\Products",COMPRESSED
  183. EnsureValidWIMetadata HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products",COMPRESSED
  184. EnsureValidWIMetadata HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components",COMPRESSED
  185. EnsureValidWIMetadata HKCR,"Installer\Components",COMPRESSED
  186.  
  187. 'Add initial known .exe files that might need to be closed
  188. dicApps.Add "communicator.exe", "communicator.exe"
  189. dicApps.Add "setup.exe", "setup.exe"
  190. Select Case OVERSIONMAJOR
  191. Case "12"
  192. Case "14"
  193.     dicApps.Add "bcssync.exe","bcssync.exe"
  194.     dicApps.Add "officesas.exe","officesas.exe"
  195.     dicApps.Add "officesasscheduler.exe","officesasscheduler.exe"
  196.     dicApps.Add "msosync.exe","msosync.exe"
  197.     dicApps.Add "onenotem.exe","onenotem.exe"
  198. Case "15"
  199. Case Else
  200. End Select
  201.  
  202. '-------------------
  203. 'Stage # 0 - Basics |
  204. '-------------------
  205. 'Build a list with installed/registered Office products
  206. sTmp = "Stage # 0 " & chr(34) & "Basics" & chr(34) & " (" & Time & ")"
  207. Log vbCrLf & sTmp & vbCrLf & String(Len(sTmp),"=") & vbCrLf
  208.  
  209. FindInstalledOProducts
  210. If dicInstalledSku.Count > 0 Then Log "Found registered product(s): " & Join(RemoveDuplicates(dicInstalledSku.Items),",") &vbCrLf
  211.  
  212. 'Validate the list of products we got from the command line if applicable
  213. ValidateRemoveSkuList
  214.  
  215. 'Log detection results
  216. If dicRemoveSku.Count > 0 Then Log "Product(s) to be removed: " & Join(RemoveDuplicates(dicRemoveSku.Items),",")
  217. sMode = "Selected " & ONAME & " products"
  218. If Not dicRemoveSku.Count > 0 Then sMode = "Orphaned " & ONAME & " products"
  219. If fRemoveAll Then sMode = "All " & ONAME & " products"
  220. Log "Final removal mode: " & sMode
  221. Log "Remove OSE service: " & fRemoveOse &vbCrLf
  222.  
  223. 'Log preview mode if applicable
  224. If fDetectOnly Then Log "*************************************************************************"
  225. If fDetectOnly Then Log "*                          PREVIEW MODE                                 *"
  226. If fDetectOnly Then Log "* All uninstall and delete operations will only be logged not executed! *"
  227. If fDetectOnly Then Log "*************************************************************************" & vbCrLf
  228.  
  229. 'Check if there are legacy products installed
  230. CheckForLegacyProducts
  231. If fLegacyProductFound Then Log "Found legacy Office products that will not be removed." Else Log "No legacy Office products found."
  232.  
  233. 'Cache .msi files
  234. If dicRemoveSku.Count > 0 Then CacheMsiFiles
  235.  
  236. 'Log Sku/Prod detection results
  237. LogSkuResults
  238.  
  239. 'UnPin Shortcuts
  240. If NOT fSkipSD AND dicRemoveSku.Count > 0 Then
  241.     On Error Resume Next
  242.     Log " Searching for pinned shortcuts"
  243.     CleanShortcuts sAllUsersProfile, False, True
  244.     CleanShortcuts sProfilesDirectory, False, True
  245.     On Error Goto 0
  246. End If 'NOT SkipSD
  247.  
  248.  
  249. '--------------------------------
  250. 'Stage # 1 - Component Detection |
  251. '--------------------------------
  252. sTmp = "Stage # 1 " & chr(34) & "Component Detection" & chr(34) & " (" & Time & ")"
  253. Log vbCrLf & sTmp & vbCrLf & String(Len(sTmp),"=") & vbCrLf
  254. If Not fBypass_Stage1 OR fForce Then
  255.     'Build a list with files which are installed/registered to a product that's going to be removed
  256.     Log "Prepare for CleanUp stages."
  257.     Log "Identifying removable elements. This can take several minutes."
  258.     ScanComponents
  259. Else
  260.     Log "Not running Component Detection in default removal."
  261. End If
  262.  
  263. 'End all running Office applications
  264. If fForce OR fQuiet Then CloseOfficeApps
  265.  
  266. '----------------------
  267. 'Stage # 2 - Setup.exe |
  268. '----------------------
  269. sTmp = "Stage # 2 " & chr(34) & "Setup.exe" & chr(34) & " (" & Time & ")"
  270. Log vbCrLf & sTmp & vbCrLf & String(Len(sTmp),"=") & vbCrLf
  271. If Not fBypass_Stage2 Then
  272.     SetupExeRemoval
  273. Else
  274.     Log "Skipping Setup.exe because bypass was requested."
  275. End If
  276.  
  277. '------------------------
  278. 'Stage # 3 - Msiexec.exe |
  279. '------------------------
  280. sTmp = "Stage # 3 " & chr(34) & "Msiexec.exe" & chr(34) & " (" & Time & ")"
  281. Log vbCrLf & sTmp & vbCrLf & String(Len(sTmp),"=") & vbCrLf
  282. If Not fBypass_Stage3 Then
  283.     MsiexecRemoval
  284. Else
  285.     Log "Skipping Msiexec.exe because bypass was requested."
  286. End If
  287.  
  288. '--------------------
  289. 'Stage # 4 - CleanUp |
  290. '--------------------
  291. 'Removal of files and registry settings
  292. sTmp = "Stage # 4 " & chr(34) & "CleanUp" & chr(34) & " (" & Time & ")"
  293. Log vbCrLf & sTmp & vbCrLf & String(Len(sTmp),"=") & vbCrLf
  294. If Not fBypass_Stage4 Then
  295.    
  296.     'Office Source Engine
  297.     If fRemoveOse Then RemoveOSE
  298.  
  299.     'Local Installation Source (MSOCache)
  300.     WipeLIS
  301.    
  302.     'Obsolete files
  303.     If fRemoveAll Then
  304.         FileWipeAll
  305.     Else
  306.         FileWipeIndividual
  307.     End If
  308.    
  309.     'Empty Folders
  310.     DeleteEmptyFolders
  311.    
  312.     'Restore Explorer if needed
  313.     If fForce OR fQuiet Then RestoreExplorer
  314.    
  315.     'Registry data
  316.     RegWipe
  317.    
  318.     'Wipe orphaned files from Windows Installer cache
  319.     MsiClearOrphanedFiles
  320.    
  321.     'Temporary .msi files in scrubcache
  322.     DeleteMsiScrubCache
  323.    
  324.     'Temporary files
  325.     DelScrubTmp
  326.    
  327. Else
  328.     Log "Skipping CleanUp because bypass was requested."
  329. End If
  330.  
  331. If Not sMoveMessage = "" Then Log vbCrLf & "Please remove this folder after next reboot: " & sMoveMessage
  332.  
  333. 'THE END
  334. Log vbCrLf & "End removal: " & Now & vbCrLf
  335. Log vbCrLf & "For detailed logging please refer to the log in folder " &chr(34)&sScrubDir&chr(34)&vbCrLf
  336.  
  337. 'If fRebootRequired Then
  338. '    Log vbCrLf & "A restart is required to complete the operation!"
  339. '    If NOT fQuiet Then
  340. '        If MsgBox("Do you want to reboot now?",vbYesNo,"Reboot Required") = VB_YES Then
  341. '            Dim colOS, oOS
  342. '            Dim oWmiReboot
  343. '            Set oWmiReboot = GetObject("winmgmts:{impersonationLevel=impersonate,(Shutdown)}!\\.\root\cimv2")
  344. '            Set colOS = oWmiReboot.ExecQuery ("Select * from Win32_OperatingSystem")
  345. '            For Each oOS in colOS
  346. '                oOS.Reboot()
  347. '            Next
  348. '        End If
  349. '    End If
  350. 'End If
  351.  
  352. 'If NOT fQuiet Then
  353. '    For Each Item in Wscript.Arguments
  354. '        If Item = "UAC" Then
  355. '            wscript.stdout.write "Press <Enter> to close this window"
  356. '            sTemp = wscript.stdin.read(1)
  357. '        End If
  358. '    Next 'Argument
  359. 'End If
  360. '=======================================================================================================
  361. '=======================================================================================================
  362.  
  363. 'Stage 0 - 4 Subroutines
  364. '=======================================================================================================
  365.  
  366. 'Office configuration products are listed with their configuration product name in the "Uninstall" key
  367. 'To identify an Office configuration product all of these condiditions have to be met:
  368. ' - "SystemComponent" does not have a value of "1" (DWORD)
  369. ' - "OPACKAGE" (see constant declaration) entry exists and is not empty
  370. ' - "DisplayVersion" exists and the 2 leftmost digits are "OVERSIONMAJOR"
  371. Sub FindInstalledOProducts
  372.     Dim ArpItem, File
  373.     Dim sCurKey, sValue, sConfigName, sProdC, sCVHValue
  374.     Dim sProductCodeList, sProductCode
  375.     Dim arrKeys, arrMultiSzValues
  376.     Dim fSystemComponent0, fPackages, fDisplayVersion, fReturn, fCategorized
  377.  
  378.     If dicInstalledSku.Count > 0 Then Exit Sub 'Already done from InputBox prompt
  379.    
  380.     'Handle orphaned products to get them added to the detection scope
  381.     If fTryReconcile Then
  382.         For Each File in oFso.GetFolder(sWICacheDir).Files
  383.             If Len(File.Name)>3 Then
  384.                 Select Case LCase(Right(File.Name,4))
  385.                 Case ".msi"
  386.                     sProductCode = ""
  387.                     sProductCode = GetMsiProductCode(File.Path)
  388.                     If InScope(sProductCode) Then
  389.                         If NOT RegKeyExists(HKLM,REG_ARP & sProductCode) Then
  390.                             'Ensure the orphaned item is getting removed
  391.                             If Len(sSkuRemoveList) > 0 Then
  392.                                 sSkuRemoveList = sSkuRemoveList & "," & GetProductID(Mid(sProductCode,11,4))
  393.                             Else
  394.                                 sSkuRemoveList = GetProductID(Mid(sProductCode,11,4))
  395.                             End If
  396.                             'Add to ScrubDir
  397.                             oFso.CopyFile File.Path,sScrubDir & "\" & prod & ".msi",True
  398.                             'Register the product with MSI
  399.                             MsiRegisterProduct File.Path
  400.                         End If 'NOT sProductCode
  401.                     End If 'InScope
  402.                 Case Else
  403.                 End Select
  404.             End If '>3
  405.         Next 'File
  406.     End If 'fTryReconcile
  407.  
  408.     'Locate standalone Office products that have no configuration product entry and create a
  409.     'temporary configuration entry
  410.     ReDim arrTmpSKUs(-1)
  411.     If RegEnumKey(HKLM,REG_ARP,arrKeys) Then
  412.         For Each ArpItem in arrKeys
  413.             If InScope(ArpItem) Then
  414.                 sCurKey = REG_ARP & ArpItem & "\"
  415.                 fSystemComponent0 = Not (RegReadValue(HKLM,sCurKey,"SystemComponent",sValue,"REG_DWORD") AND (sValue = "1"))
  416.                 If (fSystemComponent0 AND (NOT RegReadValue(HKLM,sCurKey,"CVH",sCVHValue,"REG_DWORD"))) Then
  417.                     RegReadValue HKLM,sCurKey,"DisplayVersion",sValue,"REG_SZ"
  418.                     Redim arrMultiSzValues(0)
  419.                     'Logic changed to drop the LCID identifier
  420.                     'sConfigName = GetProductID(Mid(ArpItem,11,4)) & "_" & CInt("&h" & Mid(ArpItem,16,4))
  421.                     sConfigName = OREGREF & GetProductID(Mid(ArpItem,11,4))
  422.                     If NOT RegKeyExists(HKLM,REG_ARP&sConfigName) Then
  423.                         'Create a new ARP item
  424.                         ReDim Preserve arrTmpSKUs(UBound(arrTmpSKUs)+1)
  425.                         arrTmpSKUs(UBound(arrTmpSKUs)) = sConfigName
  426.                         oReg.CreateKey HKLM,REG_ARP & sConfigName
  427.                         arrMultiSzValues(0) = sConfigName
  428.                         oReg.SetMultiStringValue HKLM,REG_ARP & sConfigName,OPACKAGE,arrMultiSzValues
  429.                         arrMultiSzValues(0) = ArpItem
  430.                         oReg.SetStringValue HKLM, REG_ARP & sConfigName, "Comment", "Temporary OffScrub generated key. Please delete this key!"
  431.                         oReg.SetMultiStringValue HKLM,REG_ARP & sConfigName,"ProductCodes",arrMultiSzValues
  432.                         oReg.SetStringValue HKLM,REG_ARP & sConfigName,"DisplayVersion",sValue
  433.                         oReg.SetStringValue HKLM,REG_ARP & sConfigName,"DisplayName",SCRIPTNAME & "_" & sConfigName
  434.                         oReg.SetDWordValue HKLM,REG_ARP & sConfigName,"SystemComponent",0
  435.                     Else
  436.                         'Update the existing temporary ARP item
  437.                         fReturn = RegReadValue(HKLM,REG_ARP&sConfigName,"ProductCodes",sProdC,"REG_MULTI_SZ")
  438.                         If NOT InStr(sProdC,ArpItem)>0 Then sProdC = sProdC & chr(34) & ArpItem
  439.                         oReg.SetMultiStringValue HKLM,REG_ARP & sConfigName,"ProductCodes",Split(sProdC,chr(34))
  440.                     End If 'RegKeyExists
  441.                 End If 'fSystemComponent0
  442.             End If 'InScope
  443.         Next 'ArpItem
  444.     End If 'RegEnumKey
  445.    
  446.     'Find the configuration products
  447.     If RegEnumKey(HKLM,REG_ARP,arrKeys) Then
  448.         For Each ArpItem in arrKeys
  449.             sCurKey = REG_ARP & ArpItem & "\"
  450.             sValue = ""
  451.             fSystemComponent0 = NOT (RegReadValue(HKLM,sCurKey,"SystemComponent",sValue,"REG_DWORD") AND (sValue = "1"))
  452.             fPackages = RegReadValue(HKLM,sCurKey,OPACKAGE,sValue,"REG_MULTI_SZ")
  453.             fDisplayVersion = RegReadValue(HKLM,sCurKey,"DisplayVersion",sValue,"REG_SZ")
  454.             If fDisplayVersion Then
  455.                 If Len(sValue) > 1 Then
  456.                     fDisplayVersion = (Left(sValue,2) = OVERSIONMAJOR)
  457.                 Else
  458.                     fDisplayVersion = False
  459.                 End If
  460.             End If
  461.             If (fSystemComponent0 AND fPackages AND fDisplayVersion) Then
  462.                 If InStr(ArpItem,".")>0 Then sConfigName = UCase(Mid(ArpItem,InStr(ArpItem,".")+1)) Else sConfigName = UCase(ArpItem)
  463.                 If NOT dicInstalledSku.Exists(sConfigName) Then dicInstalledSku.Add sConfigName,sConfigName
  464.  
  465.                 'Categorize the SKU
  466.                 'Three categories are available: ClientSuite, ClientSingleProduct, Server
  467.                 If RegReadValue(HKLM, REG_ARP & OREGREF & sConfigName, "ProductCodes", sProductCodeList, "REG_MULTI_SZ") Then
  468.                     fCategorized = False
  469.                     For Each sProductCode in Split(sProductCodeList,chr(34))
  470.                         If Len(sProductCode) = 38 Then
  471.                             If NOT Mid(sProductCode,11,1) = "0" Then
  472.                                 'Server product
  473.                                 If NOT dicSrv.Exists(UCase(sConfigName)) Then dicSrv.Add UCase(sConfigName),sConfigName
  474.                                 fCategorized = True
  475.                                 Exit For
  476.                             Else
  477.                                 Select Case Mid(sProductCode,11,4)
  478.                                 'Client Suites
  479.                                 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"
  480.                                     If NOT dicCSuite.Exists(UCase(sConfigName)) Then dicCSuite.Add UCase(sConfigName),sConfigName
  481.                                     fCategorized = True
  482.                                     Exit For
  483.  
  484.                                 Case Else
  485.                                 End Select
  486.                             End If
  487.  
  488.                         End If 'Len 38
  489.                     Next 'sProductCode
  490.                     If NOT fCategorized Then
  491.                         If NOT dicCSingle.Exists(UCase(sConfigName)) Then dicCSingle.Add UCase(sConfigName),sConfigName
  492.                     End If 'fCategorized
  493.                 End If 'RegReadValue "ProductCodes"
  494.  
  495.             End If
  496.         Next 'ArpItem
  497.     End If 'RegEnumKey
  498. End Sub 'FindInstalledOProducts
  499. '=======================================================================================================
  500.  
  501. 'Check if there are Office products from previous versions on the computer
  502. Sub CheckForLegacyProducts
  503.     Const OLEGACY = "78E1-11D2-B60F-006097C998E7}.6000-11D3-8CFE-0050048383C9}.6000-11D3-8CFE-0150048383C9}.BDCA-11D1-B7AE-00C04FB92F3D}.6D54-11D4-BEE3-00C04F990354}"
  504.     Dim Product
  505.    
  506.     'Set safe default
  507.     fLegacyProductFound = True
  508.    
  509.     For Each Product in oMsi.Products
  510.         If Len(Product) = 38 Then
  511.         'Handle O09 - O11 Products
  512.             If InStr(OLEGACY, UCase(Right(Product, 28)))>0 Then
  513.                 'Found legacy Office product. Keep flag in default and exit
  514.                 Exit Sub
  515.             End If
  516.             If UCase(Right(Product,PRODLEN)) = OFFICEID Then
  517.                 Select Case Mid(Product,4,2)
  518.                 Case "12", "14"
  519.                     'Found legacy Office product. Keep flag in default and exit
  520.                     Exit Sub
  521.                 Case Else
  522.                 End Select
  523.             End If
  524.         End If '38
  525.     Next 'Product
  526.     fLegacyProductFound = False
  527.    
  528. End Sub 'CheckForLegacyProducts
  529. '=======================================================================================================
  530.  
  531. 'Create clean list of Products to remove.
  532. 'Strip off bad & empty contents
  533. Sub ValidateRemoveSkuList
  534.     Dim Sku, Key, sProductCode, sProductCodeList
  535.     Dim arrRemoveSKUs
  536.    
  537.     If fRemoveAll Then
  538.         'Remove all mode
  539.         For Each Key in dicInstalledSku.Keys
  540.             dicRemoveSku.Add Key,dicInstalledSku.Item(Key)
  541.         Next 'Key
  542.     Else
  543.         'Remove individual products or preconfigured configurations mode
  544.        
  545.         'Ensure to have a string with no unexpected contents
  546.         sSkuRemoveList = Replace(sSkuRemoveList,";",",")
  547.         sSkuRemoveList = Replace(sSkuRemoveList," ","")
  548.         sSkuRemoveList = Replace(sSkuRemoveList,Chr(34),"")
  549.         While InStr(sSkuRemoveList,",,")>0
  550.             sSkuRemoveList = Replace(sSkuRemoveList,",,",",")
  551.         Wend
  552.        
  553.         'Prepare 'remove' and 'keep' dictionaries to determine what has to be removed
  554.        
  555.         'Initial pre-fill of 'keep' dic
  556.         For Each Key in dicInstalledSku.Keys
  557.             dicKeepSku.Add Key,dicInstalledSku.Item(Key)
  558.         Next 'Key
  559.        
  560.         'Determine contents of keep and remove dic
  561.         'Individual products
  562.         arrRemoveSKUs = Split(UCase(sSkuRemoveList),",")
  563.         For Each Sku in arrRemoveSKUs
  564.             If Sku = "OSE" Then fRemoveOse = True
  565.             If dicKeepSku.Exists(Sku) Then
  566.                 'A Sku to remove has been passed in
  567.                 'remove the item from the keep dic
  568.                 dicKeepSku.Remove(Sku)
  569.                 'Now add it to the remove dic
  570.                 If NOT dicRemoveSku.Exists(Sku) Then dicRemoveSku.Add Sku,Sku
  571.             End If
  572.         Next 'Sku
  573.  
  574.         'Client Suite Category
  575.         If fRemoveCSuites Then
  576.             For Each Key in dicInstalledSku.Keys
  577.                 If dicCSuite.Exists(Key) Then
  578.                     If dicKeepSku.Exists(Key) Then dicKeepSku.Remove(Key)
  579.                     If NOT dicRemoveSku.Exists(Key) Then dicRemoveSku.Add Key,Key
  580.                 End If
  581.             Next 'Key
  582.         End If 'fRemoveCSuites
  583.        
  584.         'Client Single/Standalone Category
  585.         If fRemoveCSingle Then
  586.             For Each Key in dicInstalledSku.Keys
  587.                 If dicCSingle.Exists(Key) Then
  588.                     If dicKeepSku.Exists(Key) Then dicKeepSku.Remove(Key)
  589.                     If NOT dicRemoveSku.Exists(Key) Then dicRemoveSku.Add Key,Key
  590.                 End If
  591.             Next 'Key
  592.         End If 'fRemoveCSingle
  593.        
  594.         'Server Category
  595.         If fRemoveSrv Then
  596.             For Each Key in dicInstalledSku.Keys
  597.                 If dicSrv.Exists(Key) Then
  598.                     If dicKeepSku.Exists(Key) Then dicKeepSku.Remove(Key)
  599.                     If NOT dicRemoveSku.Exists(Key) Then dicRemoveSku.Add Key,Key
  600.                 End If
  601.             Next 'Key
  602.         End If 'fRemoveSrv
  603.        
  604.         If NOT dicKeepSku.Count > 0 Then fRemoveAll = True
  605.  
  606.     End If 'fRemoveAll
  607.  
  608.     'Fill the KeepProd dic
  609.     For Each Sku in dicKeepSku.Keys
  610.         If RegReadValue(HKLM,REG_ARP & OREGREF & Sku,"ProductCodes",sProductCodeList,"REG_MULTI_SZ") Then
  611.             For Each sProductCode in Split(sProductCodeList,chr(34))
  612.                 If Len(sProductCode) = 38 Then
  613.                     If NOT dicKeepProd.Exists(sProductCode) Then dicKeepProd.Add sProductCode,Sku
  614.                 End If '38
  615.             Next 'sProductCod
  616.         End If
  617.     Next 'Sku
  618.        
  619.     If fRemoveAll OR fRemoveOse Then CheckRemoveOSE
  620.     If fRemoveAll OR fRemoveOspp Then CheckRemoveOspp
  621.  
  622. End Sub 'ValidateRemoveSkuList
  623. '=======================================================================================================
  624.  
  625. 'Check if OSE service can be scrubbed
  626. Sub CheckRemoveOSE
  627.     Const O11 = "6000-11D3-8CFE-0150048383C9}"
  628.     Dim Product
  629.    
  630.     If fRemoveOse Then Exit Sub
  631.     For Each Product in oMsi.Products
  632.         If Len(Product) = 38 Then
  633.             If UCase(Right(Product,28)) = O11 Then
  634.                 'Found Office 2003 Product. Set flag to not remove the OSE service
  635.                 Exit Sub
  636.             End If
  637.             If UCase(Right(Product,PRODLEN))=OFFICEID Then
  638.                 Select Case Mid(Product,4,2)
  639.                 Case "12","14","15","16","17"
  640.                     'Found another Office product. Set flag to keep the OSE service
  641.                     If NOT Mid(Product,4,2) = OVERSIONMAJOR Then
  642.                         fRemoveOse = False
  643.                         Exit Sub
  644.                     End If
  645.                 Case Else
  646.                 End Select
  647.             End If
  648.         End If '38
  649.     Next 'Product
  650.     fRemoveOse = True
  651. End Sub 'CheckRemoveOSE
  652. '=======================================================================================================
  653.  
  654. 'Check if OSPP service can be scrubbed
  655. Sub CheckRemoveOSPP
  656.     Dim Product
  657.    
  658.     If NOT CInt(OVERSIONMAJOR) > 12 Then
  659.         fRemoveOspp = False
  660.         Exit Sub
  661.     End If
  662.  
  663.     If fRemoveOspp Then Exit Sub
  664.     For Each Product in oMsi.Products
  665.         If Len(Product) = 38 Then
  666.             If UCase(Right(Product,PRODLEN))=OFFICEID Then
  667.                 Select Case Mid(Product,4,2)
  668.                 Case "14","15","16","17"
  669.                     'Found another Office product. Set flag to keep the OSPP service
  670.                     If NOT Mid(Product,4,2) = OVERSIONMAJOR Then
  671.                         fRemoveOspp = False
  672.                         Exit Sub
  673.                     End If
  674.                 Case Else
  675.                 End Select
  676.             End If
  677.         End If '38
  678.     Next 'Product
  679.     fRemoveOspp = True
  680. End Sub 'CheckRemoveOSPP
  681. '=======================================================================================================
  682.  
  683. 'Cache .msi files for products that will be removed in case they are needed for later file detection
  684. Sub CacheMsiFiles
  685.     Dim Product
  686.     Dim sMsiFile
  687.    
  688.     'Non critical routine for failures.
  689.     'Errors will be logged but must not fail the execution
  690.     On Error Resume Next
  691.     Log " Cache .msi files to temporary Scrub folder"
  692.     'Cache the files
  693.     For Each Product in oMsi.Products
  694.         'Ensure valid GUID length
  695.         If InScope(Product) Then
  696.             If (fRemoveAll OR CheckDelete(Product))Then
  697.                 CheckError "CacheMsiFiles"
  698.                 sMsiFile = oMsi.ProductInfo(Product,"LocalPackage") : CheckError "CacheMsiFiles"
  699.                 LogOnly " - " & Product & ".msi"
  700.                 If oFso.FileExists(sMsiFile) Then oFso.CopyFile sMsiFile,sScrubDir & "\" & Product & ".msi",True
  701.                 CheckError "CacheMsiFiles"
  702.             End If
  703.         End If 'InScope
  704.     Next 'Product
  705.  
  706.     Err.Clear
  707. End Sub 'CacheMsiFiles
  708. '=======================================================================================================
  709.  
  710. 'Build a list of all files that will be deleted
  711. Sub ScanComponents
  712.     Const MSIINSTALLSTATE_LOCAL = 3
  713.  
  714.     Dim FileList, RegList, ComponentID, CompClient, Record, qView, MsiDb, CompVerbose
  715.     Dim Processes, Process, Prop, prod
  716.     Dim sQuery, sSubKeyName, sPath, sFile, sMsiFile, sCompClient, sComponent, sCompReg
  717.     Dim fRemoveComponent, fAffectedComponent, fIsPermanent, fIsFile, fIsFolder
  718.     Dim i, iProgress, iCompCnt, iRemCnt
  719.     Dim dicFLError, oDic, oFolderDic, dicCompPath
  720.     Dim hDefKey
  721.  
  722.     'Logfile
  723.     Set FileList = oFso.OpenTextFile(sScrubDir & "\FileList.txt",FOR_WRITING,True,True)
  724.     Set RegList = oFso.OpenTextFile(sScrubDir & "\RegList.txt",FOR_WRITING,True,True)
  725.     Set CompVerbose = oFso.OpenTextFile(sScrubDir & "\CompVerbose.txt",FOR_WRITING,True,True)
  726.    
  727.     'FileListError dic
  728.     Set dicFLError = CreateObject("Scripting.Dictionary")
  729.    
  730.     Set oDic = CreateObject("Scripting.Dictionary")
  731.     Set oFolderDic = CreateObject("Scripting.Dictionary")
  732.     Set dicCompPath = CreateObject("Scripting.Dictionary")
  733.  
  734.     'Prevent that API errors fail script execution
  735.     On Error Resume Next
  736.  
  737.     iCompCnt = oMsi.Components.Count
  738.     If NOT Err = 0 Then
  739.         'API failure
  740.         Log "Error during components detection. Cannot complete this task."
  741.         Err.Clear
  742.         Exit Sub
  743.     End If
  744.  
  745.     'Ensure to not divide by zero
  746.     If iCompCnt = 0 Then iCompCnt = 1
  747.     LogOnly " Scanning " & iCompCnt & " components"
  748.     'Enum all Components
  749.     For Each ComponentID In oMsi.Components
  750.         CompVerbose.WriteLine vbCrLf & "Checking Component: " & ComponentID
  751.        
  752.         'Progress bar
  753.         i = i + 1
  754.         If iProgress < (i / iCompCnt) * 100 Then
  755.             wscript.stdout.write "." : LogStream.Write "."
  756.             iProgress = iProgress + 1
  757.             If iProgress = 35 OR iProgress = 70 Then Log ""
  758.         End If
  759.  
  760.         'Check if all ComponentClients will be removed
  761.         sCompClient = ""
  762.         iRemCnt = 0
  763.         fIsPermanent = False
  764.         fRemoveComponent = False 'Flag to track if the component will be completely removed
  765.         fAffectedComponent = False 'Flag to track if some clients remain installed who have a none shared location
  766.         dicCompPath.RemoveAll
  767.         Err.Clear
  768.         For Each CompClient In oMsi.ComponentClients(ComponentID)
  769.             CompVerbose.Write " CompClient " & CompClient & "-> "
  770.             If Err = 0 Then
  771.                 'Ensure valid guid length
  772.                 If Len(CompClient) = 38 Then
  773.                     fRemoveComponent = InScope(CompClient)
  774.                     If fRemoveComponent OR (CompClient = "{00000000-0000-0000-0000-000000000000}") Then
  775.                         sPath = ""
  776.                         sPath = LCase(oMsi.ComponentPath(CompClient,ComponentID))
  777.                         sPath = Replace(sPath,"?",":")
  778.                         'Scan for msidbComponentAttributesPermanent flag
  779.                         If CompClient = "{00000000-0000-0000-0000-000000000000}" Then
  780.                             fIsPermanent = True
  781.                             iRemCnt = iRemCnt + 1
  782.                         End If
  783.                         If fRemoveComponent Then fRemoveComponent = CheckDelete(CompClient)
  784.                         CompVerbose.Write "CheckDelete: " & fRemoveComponent & "; "
  785.                         If fRemoveComponent Then
  786.                             iRemCnt = iRemCnt + 1
  787.                             fAffectedComponent = True
  788.                             'Since the scope remains within one Office family the keypath for the component
  789.                             'is assumed to be identical
  790.                             If sCompClient = "" Then sCompClient = CompClient
  791.                         ' flag the CompClient entry for removal
  792.                             sCompReg = "Installer\Components\"&GetCompressedGuid(ComponentID)&"\"&GetCompressedGuid(CompClient)
  793.                             If NOT dicDelRegKey.Exists(sCompReg) Then
  794.                                 dicDelRegKey.Add sCompReg,HKCR
  795.                                 RegList.WriteLine HiveString(HKCR)&"\"&sCompReg
  796.                             End If
  797.                             sCompReg = "SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components\"&GetCompressedGuid(ComponentID)&"\"&GetCompressedGuid(CompClient)
  798.                             If NOT dicDelRegKey.Exists(sCompReg) Then
  799.                                 dicDelRegKey.Add sCompReg,HKLM
  800.                                 RegList.WriteLine HiveString(HKCR)&"\"&sCompReg
  801.                             End If
  802.                         Else
  803.                             If NOT dicCompPath.Exists(sPath) Then dicCompPath.Add sPath,CompClient
  804.                         End If
  805.                         CompVerbose.WriteLine "AffectedComponent: " & fAffectedComponent
  806.                         CompVerbose.WriteLine " CompClient now set to: " & sCompClient
  807.                     Else
  808.                         CompVerbose.Write "InScope: " & fRemoveComponent & "; "
  809.                     End If
  810.                 Else
  811.                     CompVerbose.WriteLine "Error: Invalid metadata"
  812.                     If NOT dicFLError.Exists("Error: Invalid metadata found. ComponentID: "&ComponentID &", ComponentClient: "&CompClient) Then _
  813.                         dicFLError.Add "Error: Invalid metadata found. ComponentID: "&ComponentID &", ComponentClient: "&CompClient, ComponentID
  814.                 End If '38
  815.             Else
  816.                 CompVerbose.WriteLine "Error: " & Err.number & " " & Err.Description
  817.                 Err.Clear
  818.             End If 'Err = 0
  819.         Next 'CompClient
  820.        
  821.         'Determine if the component resources go away
  822.         sPath = ""
  823.         fRemoveComponent = fAffectedComponent AND (iRemCnt = oMsi.ComponentClients(ComponentID).Count)
  824.         CompVerbose.WriteLine " Component goes away: " & fRemoveComponent
  825. ' This caused unintentional removals
  826. '        If NOT fRemoveComponent AND fAffectedComponent Then
  827. '            'Flag as removable if component has a unique keypath
  828. '            sPath = LCase(oMsi.ComponentPath(sCompClient,ComponentID))
  829. '            sPath = Replace(sPath,"?",":")
  830. '            fRemoveComponent = NOT dicCompPath.Exists(sPath)
  831. '        End If
  832.         If fRemoveComponent Then
  833.             'Check msidbComponentAttributesPermanent flag
  834.             If fIsPermanent AND NOT fForce Then fRemoveComponent = False
  835.             CompVerbose.WriteLine " msidbComponentAttributesPermanent: " & NOT fRemoveComponent
  836.         End If
  837.  
  838.         If fRemoveComponent Then
  839.             CompVerbose.WriteLine " RESULT: Component IN SCOPE for removal"
  840.             fIsFile = False : fIsFolder = False
  841.  
  842.             'Component resources go away for this product
  843.             Err.Clear
  844.             'Add the component registration key to ensure removal
  845.             sCompReg = "Installer\Components\"&GetCompressedGuid(ComponentID)&"\"
  846.             If NOT dicDelRegKey.Exists(sCompReg) Then
  847.                 dicDelRegKey.Add sCompReg,HKCR
  848.                 RegList.WriteLine HiveString(HKCR)&"\"&sCompReg
  849.             End If
  850.             sCompReg = "SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components\"&GetCompressedGuid(ComponentID)&"\"
  851.             If NOT dicDelRegKey.Exists(sCompReg) Then
  852.                 dicDelRegKey.Add sCompReg,HKLM
  853.                 RegList.WriteLine HiveString(HKCR)&"\"&sCompReg
  854.             End If
  855.             'Get the component path
  856.             If sPath = "" Then
  857.                 sPath = LCase(oMsi.ComponentPath(sCompClient,ComponentID))
  858.                 sPath = Replace(sPath,"?",":")
  859.             End If
  860.             CompVerbose.WriteLine " Path: " & sPath
  861.             If Len(sPath) > 4 Then
  862.                 If Left(sPath,1) = "0" Then
  863.                     'Registry keypath
  864.  
  865.                     Select Case Left(sPath,2)
  866.                     Case "00"
  867.                         sPath = Mid(sPath,5)
  868.                         hDefKey = HKCR
  869.                     Case "01"
  870.                         sPath = Mid(sPath,5)
  871.                         hDefKey = HKCU
  872.                     Case "02","22"
  873.                         sPath = Mid(sPath,5)
  874.                         hDefKey = HKLM
  875.                     Case Else
  876.                         '
  877.                     End Select
  878.                     If NOT dicDelRegKey.Exists(sPath) Then
  879.                         dicDelRegKey.Add sPath,hDefKey
  880.                         RegList.WriteLine HiveString(hDefKey)&"\"&sPath
  881.                     End If
  882.                 Else
  883.                
  884.                     'File or Folder
  885.                     If oFso.FileExists(sPath) OR oFso.FolderExists(sPath) Then
  886.                         If Right(sPath,1) = "\" Then
  887.                             fIsFolder = True
  888.                             CompVerbose.WriteLine " Folder check OK"
  889.                         Else
  890.                             fIsFile = True
  891.                             CompVerbose.WriteLine " File check OK"
  892.                         End If
  893.                         If fIsFile Then sPath = oFso.GetFile(sPath).ParentFolder
  894.                         If Not oFolderDic.Exists(sPath) Then
  895.                             oFolderDic.Add sPath,sPath
  896.                             FileList.WriteLine sPath & vbTab & "(FOLDER)"
  897.                         End If
  898.                         'Get the .msi file
  899.                         If oFso.FileExists(sScrubDir & "\" & sCompClient & ".msi") Then
  900.                             sMsiFile = sScrubDir & "\" & sCompClient & ".msi"
  901.                         Else
  902.                             sMsiFile = oMsi.ProductInfo(sCompClient,"LocalPackage")
  903.                         End If
  904.                         CompVerbose.WriteLine " Set msi file to : " & sMsiFile
  905.                         If Not Err = 0 Then
  906.                             CompVerbose.WriteLine " Error: Failed to obtain .msi file for product " & sCompClient
  907.                             If NOT dicFLError.Exists("Failed to obtain .msi file for product "&sCompClient) Then _
  908.                                 dicFLError.Add "Failed to obtain .msi file for product "&sCompClient, ComponentID
  909.                             Err.Clear
  910.                         End If
  911.                         CompVerbose.Write " Open .msi file for reading returned: "
  912.                         Set MsiDb = oMsi.OpenDatabase(sMsiFile,MSIOPENDATABASEREADONLY)
  913.                        
  914.                         If Err = 0 Then
  915.                             CompVerbose.WriteLine " SUCCESS"
  916.                             'Get the component name from the 'Component' table
  917.                             sQuery = "SELECT `Component`,`ComponentId` FROM Component WHERE `ComponentId` = '" & ComponentID &"'"
  918.                             Set qView = MsiDb.OpenView(sQuery) : qView.Execute
  919.                             Set Record = qView.Fetch()
  920.                             If Not Record Is Nothing Then sComponent = Record.Stringdata(1)
  921.                             CompVerbose.WriteLine " Obtained ComponentId as: " & sComponent
  922.  
  923.                             'Get filenames from the 'File' table
  924.                             sQuery = "SELECT `Component_`,`FileName` FROM File WHERE `Component_` = '" & sComponent &"'"
  925.                             Set qView = MsiDb.OpenView(sQuery) : qView.Execute
  926.                             Set Record = qView.Fetch()
  927.                             Do Until Record Is Nothing
  928.                                 'Read the filename
  929.                                 sFile = Record.StringData(2)
  930.                                 If InStr(sFile,"|") > 0 Then sFile = Mid(sFile,InStr(sFile,"|")+1,Len(sFile))
  931.                                 'sFile = sPath & "\" & sFile
  932.                                 CompVerbose.WriteLine "  File: " & sPath& "\" & sFile
  933.                                 If Not oDic.Exists(sPath & "\" & sFile) Then
  934.                                     'Exception handler
  935.                                     fAdd = True
  936.                                     Select Case UCase(sFile)
  937.                                     Case "FPERSON.DLL"
  938.                                         'Catch exception caused by changed .msi keypath authoring logic for smart tags
  939.                                         For Each prod in oMsi.Products
  940.                                             If NOT Checkdelete(prod) Then
  941.                                                 If oMsi.FeatureState(prod, "MSTagPluginNamesFiles") = MSIINSTALLSTATE_LOCAL Then
  942.                                                     fAdd = False
  943.                                                     Exit For
  944.                                                 End If
  945.                                             End If
  946.                                         Next 'prod
  947.                                     Case Else
  948.                                     End Select
  949.                                     If fAdd Then
  950.                                         CompVerbose.WriteLine "  Added as new file to dictionary"
  951.                                         oDic.Add sPath & "\" & sFile,sFile
  952.                                         FileList.WriteLine sFile & vbTab & sPath & "\" & sFile
  953.                                         If Len(sFile)>4 Then
  954.                                             sFile = LCase(sFile)
  955.                                             If Right(sFile,4) = ".exe" Then
  956.                                                 If NOT dicApps.Exists(sFile) Then
  957.                                                     Select Case sFile
  958.                                                     Case "setup.exe","ose.exe","osppsvc.exe","explorer.exe"
  959.                                                     Case Else
  960.                                                         dicApps.Add sFile,LCase(sPath) & "\" & sFile
  961.                                                         CompVerbose.WriteLine "  Added to the list of processes that need to be closed."
  962.                                                     End Select
  963.                                                 End If 'dicApps.Exists
  964.                                             End If '.exe
  965.                                         End If 'Len > 4
  966.                                     End If 'fAdd
  967.                                 End If 'oDic.Exists
  968.                                 Set Record = qView.Fetch()
  969.                             Loop
  970.                             Set Record = Nothing
  971.                             qView.Close
  972.                             Set qView = Nothing
  973.                         Else
  974.                             CompVerbose.WriteLine " Error: Could not read from .msi file"
  975.                             If NOT dicFLError.Exists("Error: Could not read from .msi file: "&sMsiFile) Then _
  976.                                 dicFLError.Add "Error: Could not read from .msi file: "&sMsiFile, ComponentID
  977.                             Err.Clear
  978.                         End If 'Err = 0
  979.                     Else
  980.                         CompVerbose.WriteLine " Error: File check FAILED"
  981.                     End If 'FileExists(sPath)
  982.                 End If
  983.             End If 'Len(sPath) > 4
  984.         Else
  985.             CompVerbose.WriteLine " RESULT: Component NOT in scope for removal"
  986.             If fAffectedComponent Then
  987.                 'Add the path to the 'Keep' dictionary
  988.                 Err.Clear
  989.                 For Each CompClient In oMsi.ComponentClients(ComponentID)
  990.                     'Get the component path
  991.                     sPath = "" : sPath = LCase(oMsi.ComponentPath(CompClient,ComponentID))
  992.                     sPath = Replace(sPath,"?",":")
  993.                
  994.                     If Len(sPath) > 4 Then
  995.                         If Left(sPath,1) = "0" Then
  996.                             'Registry keypath
  997.  
  998.                             Select Case Left(sPath,2)
  999.                             Case "00"
  1000.                                 sPath = Mid(sPath,5)
  1001.                                 hDefKey = HKCR
  1002.                             Case "01"
  1003.                                 sPath = Mid(sPath,5)
  1004.                                 hDefKey = HKCU
  1005.                             Case "02","22"
  1006.                                 sPath = Mid(sPath,5)
  1007.                                 hDefKey = HKLM
  1008.                             Case Else
  1009.                                 '
  1010.                             End Select
  1011.                             If NOT dicKeepReg.Exists(LCase(sPath)) Then
  1012.                                 dicKeepReg.Add LCase(sPath),hDefKey
  1013.                             End If
  1014.                         Else
  1015.                             'File keypath
  1016.                             If oFso.FileExists(sPath) Then
  1017.                                 If NOT dicKeepFolder.Exists(LCase(sPath)) Then dicKeepFolder.Add LCase(sPath)
  1018.                                 sPath = LCase(oFso.GetFile(sPath).ParentFolder) & "\"
  1019.                                 If NOT dicKeepFolder.Exists(sPath) Then AddKeepFolder sPath
  1020.                             End If
  1021.                             'Folder keypath
  1022.                             If oFso.FolderExists(sPath) Then AddKeepFolder sPath
  1023.                         End If 'Is Registry
  1024.                     End If 'sPath > 4
  1025.                 Next 'CompClient
  1026.             End If 'fAffectedComponent
  1027.         End If 'fRemoveComponent
  1028.         Err.Clear
  1029.     Next 'ComponentID
  1030.     On Error Goto 0
  1031.    
  1032.     Log " Done" & vbCrLf
  1033.     If dicFLError.Count > 0 Then LogOnly Join(dicFLError.Keys,vbCrLf)
  1034.     If Not oFolderDic.Count = 0 Then arrDeleteFolders = oFolderDic.Keys Else Set arrDeleteFolders = Nothing
  1035.     If Not oDic.Count = 0 Then arrDeleteFiles = oDic.Keys Else Set arrDeleteFiles = Nothing
  1036. End Sub 'ScanComponents
  1037. '=======================================================================================================
  1038.  
  1039.  
  1040. 'Try to remove the products by calling setup.exe
  1041. Sub SetupExeRemoval
  1042.     Dim OseService, Service, TextStream
  1043.     Dim iSetupCnt, RetVal
  1044.     Dim Sku, sConfigFile, sUninstallCmd, sCatalyst, sDll, sDisplayLevel, sNoCancel
  1045.  
  1046.     iSetupCnt = 0
  1047.     If Not dicRemoveSku.Count > 0 Then
  1048.         Log " Nothing to remove for Setup.exe"
  1049.         Exit Sub
  1050.     End If
  1051.    
  1052.     'Ensure that the OSE service is *installed, *not disabled, *running under System context.
  1053.     'If validation fails exit out of this sub.
  1054.     Set OseService = oWmiLocal.Execquery("Select * From Win32_Service Where Name like 'ose%'")
  1055.     If OseService.Count = 0 Then Exit Sub
  1056.     For Each Service in OseService
  1057.         If (Service.StartMode = "Disabled") AND (Not Service.ChangeStartMode("Manual")=0) Then Exit Sub
  1058.         If (Not Service.StartName = "LocalSystem") AND (Service.Change( , , , , , , "LocalSystem", "")) Then Exit Sub
  1059.     Next 'Service
  1060.    
  1061.     For Each Sku in dicRemoveSku.Keys
  1062.         If Sku="CLICK2RUN" Then
  1063.             'Already done
  1064.         Else
  1065.             'Create an "unattended" config.xml file for uninstall
  1066.             If fQuiet AND NOT fBasic Then sDisplayLevel = "None" Else sDisplayLevel="Basic"
  1067.             If fNoCancel Then sNoCancel="Yes" Else sNoCancel="No"
  1068.             Set TextStream = oFso.OpenTextFile(sScrubDir & "\config.xml",FOR_WRITING,True,True)
  1069.             TextStream.Writeline "<Configuration Product=""" & Sku & """>"
  1070.             TextStream.Writeline "<Display Level=""" & sDisplayLevel & """ CompletionNotice=""Yes"" SuppressModal=""Yes"" NoCancel=""" & sNoCancel & """ AcceptEula=""Yes"" />"
  1071.             TextStream.Writeline "<Logging Type=""Verbose"" Path=""" & sLogDir & """ Template=""Microsoft Office " & Sku & " Setup(*).txt"" />"
  1072.             TextStream.Writeline "<Setting Id=""MSIRESTARTMANAGERCONTROL"" Value=""Disable"" />"
  1073.             TextStream.Writeline "<Setting Id=""SETUP_REBOOT"" Value=""Never"" />"
  1074.             TextStream.Writeline "</Configuration>"
  1075.             TextStream.Close
  1076.             Set TextStream = Nothing
  1077.        
  1078.             'Ensure path to setup.exe is valid to prevent errors
  1079.             sDll = ""
  1080.             If RegReadValue(HKLM,REG_ARP & OREGREF & Sku,"UninstallString",sCatalyst,"REG_SZ") Then
  1081.                 If InStr(LCase(sCatalyst),"/dll")>0 Then sDll = Right(sCatalyst,Len(sCatalyst)-InStr(LCase(sCatalyst),"/dll")+2)
  1082.                 If InStr(sCatalyst,"/")>0 Then sCatalyst = Left(sCatalyst,InStr(sCatalyst,"/")-1)
  1083.                 sCatalyst = Trim(Replace(sCatalyst,Chr(34),""))
  1084.                 If NOT oFso.FileExists(sCatalyst) Then
  1085.                     sCatalyst = sCommonProgramFiles & "\" & OREF & "\Office Setup Controller\setup.exe"
  1086.                     If NOT oFso.FileExists(sCatalyst) AND f64 Then
  1087.                         sCatalyst = sCommonProgramFilesX86 & "" & OREF & "\Office Setup Controller\setup.exe"
  1088.                     End If
  1089.                 End If
  1090.                 If oFso.FileExists(sCatalyst) Then
  1091.                     sUninstallCmd = Chr(34) & sCatalyst & Chr(34) & " /uninstall " & Sku & " /config " & Chr(34) & sScrubDir & "\config.xml" & Chr(34) & sDll
  1092.                     iSetupCnt = iSetupCnt + 1
  1093.                     Log " - Calling Setup.exe to remove " & Sku '& vbCrLf & sUninstallCmd
  1094.                     If Not fDetectOnly Then
  1095.                         On Error Resume Next
  1096.                         ' end other instances of setup
  1097.                         EndCurrentInstalls
  1098.                         ' call uninstall
  1099.                         RetVal = oWShell.Run(sUninstallCmd,0,True) : CheckError "SetupExeRemoval"
  1100.                         Log " - Setup.exe returned: " & SetupRetVal(Retval) & " (" & RetVal & ")" & vbCrLf
  1101.                         fRebootRequired = fRebootRequired OR (RetVal = "3010")
  1102.                         On Error Goto 0
  1103.                     Else
  1104.                         Log " -> Removal suppressed in preview mode."
  1105.                     End If
  1106.                 Else
  1107.                     Log " Error: Office setup.exe appears to be missing"
  1108.                 End If 'RetVal = 0) AND oFso.FileExists
  1109.             End If 'RegReadValue
  1110.         End If
  1111.     Next 'Sku
  1112.     If iSetupCnt = 0 Then Log " Nothing to remove for setup."
  1113. End Sub 'SetupExeRemoval
  1114. '=======================================================================================================
  1115.  
  1116. 'Invoke msiexec to remove individual .MSI packages
  1117. Sub MsiexecRemoval
  1118.  
  1119.     Dim Product
  1120.     Dim i
  1121.     Dim sCmd, sReturn, sMsiProp
  1122.     Dim fRegWipe
  1123.  
  1124.     fRegWipe = False
  1125.  
  1126.     Select Case OVERSIONMAJOR
  1127.     Case "11"
  1128.         sMsiProp = " REBOOT=ReallySuppress NOLOCALCACHEROLLBACK=1"
  1129.     Case "12"
  1130.         fRegWipe = True
  1131.         sMsiProp = " REBOOT=ReallySuppress NOREMOVESPAWN=True"
  1132.     Case "14"
  1133.         fRegWipe = True
  1134.         sMsiProp = " REBOOT=ReallySuppress NOREMOVESPAWN=True"
  1135.     Case "15"
  1136.         fRegWipe = True
  1137.         sMsiProp = " REBOOT=ReallySuppress NOREMOVESPAWN=True"
  1138.     Case Else
  1139.     End Select
  1140.  
  1141.     'Clear up ARP first to avoid possible custom action dependencies
  1142.     If fRegWipe Then RegWipeARP
  1143.  
  1144.     'Check MSI registered products
  1145.     'Office System does only support per machine installation so it's sufficient to use Installer.Products
  1146.     i = 0
  1147.     'sMsiProp = " MSIRESTARTMANAGERCONTROL=Disable" & sMsiProp
  1148.     For Each Product in oMsi.Products
  1149.         If InScope(Product) Then
  1150.             If fRemoveAll OR CheckDelete(Product) Then
  1151.                 i = i + 1
  1152.                 Log " Calling msiexec.exe to remove " & Product
  1153.                 sCmd = "msiexec.exe /x" & Product & sMsiProp
  1154.                 If fQuiet AND NOT fBasic Then
  1155.                     sCmd = sCmd & " /q"
  1156.                 Else
  1157.                     sCmd = sCmd & " /qb-"
  1158.                 End If
  1159.                 sCmd = sCmd & " /l*v+ "&chr(34)&sLogDir&"\Uninstall_"&Product&".log"&chr(34)
  1160.                 If NOT fDetectOnly Then
  1161.                     ' end other instances of setup
  1162.                     EndCurrentInstalls
  1163.  
  1164.                     'Execute the uninstall
  1165.                     LogOnly " - Calling msiexec with '"&sCmd&"'"
  1166.                     sReturn = oWShell.Run(sCmd, 0, True)
  1167.                     Log " - msiexec returned: " & SetupRetVal(sReturn) & " (" & sReturn & ")" & vbCrLf
  1168.                     fRebootRequired = fRebootRequired OR (sReturn = "3010") OR (sReturn = "1618")
  1169.                 Else
  1170.                     Log "  -> Removal suppressed in preview mode."
  1171.                     LogOnly "  -> Command: "&sCmd
  1172.                 End If
  1173.             End If 'CheckDelete
  1174.         End If 'InScope
  1175.     Next 'Product
  1176.     If i = 0 Then Log " Nothing to remove for msiexec"
  1177. End Sub 'MsiexecRemoval
  1178. '=======================================================================================================
  1179.  
  1180. 'Remove the OSE (Office Source Engine) service
  1181. Sub RemoveOSE
  1182.     On Error Resume Next
  1183.     Log vbCrLf & "OSE CleanUp"
  1184.     DeleteService "ose"
  1185.     'Delete the folder
  1186.     DeleteFolder sCommonProgramFiles & "\Microsoft Shared\Source Engine"
  1187.     'Delete the registration
  1188.     RegDeleteKey HKLM,"SYSTEM\CurrentControlSet\Services\ose\"
  1189. End Sub 'RemoveOSE
  1190. '=======================================================================================================
  1191.  
  1192.  
  1193. 'File cleanup operations for the Local Installation Source (MSOCache)
  1194. Sub WipeLIS
  1195.     Const LISROOT = "MSOCache\All Users\"
  1196.     Dim LogicalDisks, Disk, Folder, SubFolder, MseFolder, File, Files
  1197.     Dim arrSubFolders
  1198.     Dim sFolder
  1199.     Dim fRemoveFolder
  1200.    
  1201.     Log vbCrLf & "LIS CleanUp"
  1202.     'Search all hard disks
  1203.     Set LogicalDisks = oWmiLocal.ExecQuery("Select * From Win32_LogicalDisk WHERE DriveType=3")
  1204.     For Each Disk in LogicalDisks
  1205.         If oFso.FolderExists(Disk.DeviceID & "\" & LISROOT) Then
  1206.             Set Folder = oFso.GetFolder(Disk.DeviceID & "\" & LISROOT)
  1207.             For Each Subfolder in Folder.Subfolders
  1208.                 If Len(Subfolder) > 37 Then
  1209.                     If fRemoveAll Then
  1210.                         If  (Mid(Subfolder.Name,27,PRODLEN) = OFFICEID AND Mid(SubFolder.Name,4,2)=OVERSIONMAJOR) OR _
  1211.                             LCase(Right(Subfolder.Name,7)) = OVERSIONMAJOR &".data" Then DeleteFolder Subfolder.Path
  1212.                     Else
  1213.                         If  (Mid(Subfolder.Name,27,PRODLEN) = OFFICEID AND Mid(SubFolder.Name,4,2)=OVERSIONMAJOR) AND _
  1214.                             CheckDelete(UCase(Left(Subfolder.Name,38))) AND _
  1215.                             UCase(Right(Subfolder,1))= UCase(Left(Disk.DeviceID,1))Then DeleteFolder Subfolder.Path
  1216.                     End If
  1217.                 End If 'Len > 37
  1218.             Next 'Subfolder
  1219.             If (Folder.Subfolders.Count = 0) AND (Folder.Files.Count = 0) Then
  1220.                 sFolder = Folder.Path
  1221.                 Set Folder = Nothing
  1222.                 SmartDeleteFolder sFolder
  1223.             End If
  1224.         End If 'oFso.FolderExists
  1225.     Next 'Disk
  1226.    
  1227.     'MSECache
  1228.     If EnumFolders(sProgramFiles,arrSubFolders) Then
  1229.         For Each SubFolder in arrSubFolders
  1230.             If UCase(Right(SubFolder,9))="\MSECACHE" Then
  1231.                 ReDim arrMseFolders(-1)
  1232.                 Set Folder = oFso.GetFolder(SubFolder)
  1233.                 GetMseFolderStructure Folder
  1234.                 For Each MseFolder in arrMseFolders
  1235.                     If oFso.FolderExists(MseFolder) Then
  1236.                         fRemoveFolder = False
  1237.                         Set Folder = oFso.GetFolder(MseFolder)
  1238.                         Set Files = Folder.Files
  1239.                         For Each File in Files
  1240.                             If (LCase(Right(File.Name,4))=".msi") Then
  1241.                                 If CheckDelete(ProductCode(File.Path)) Then
  1242.                                     fRemoveFolder = True
  1243.                                     Exit For
  1244.                                 End If 'CheckDelete
  1245.                             End If
  1246.                         Next 'File
  1247.                         Set Files = Nothing
  1248.                         Set Folder = Nothing
  1249.                         If fRemoveFolder Then SmartDeleteFolder MseFolder
  1250.                     End If 'oFso.FolderExists(MseFolder)
  1251.                 Next 'MseFolder
  1252.             End If
  1253.         Next 'SubFolder
  1254.     End If 'oFso.FolderExists
  1255. End Sub 'WipeLis
  1256. '=======================================================================================================
  1257.  
  1258. 'Wipe files and folders as documented in KB 928218
  1259. Sub FileWipeAll
  1260.     Dim sFolder
  1261.     Dim Folder, Subfolder
  1262.    
  1263.     If fForce OR fQuiet Then CloseOfficeApps
  1264.    
  1265.     'Handle other services.
  1266.     Select Case OVERSIONMAJOR
  1267.     Case "11"
  1268.     Case "12"
  1269.     Case "14"
  1270.         DeleteService "odserv"
  1271.         DeleteService "Microsoft Office Groove Audit Service"
  1272.         DeleteService "Microsoft SharePoint Workspace Audit Service"
  1273.     Case "15"
  1274.     Case Else
  1275.     End Select
  1276.  
  1277.     'User specific files
  1278.     If NOT fKeepUser Then
  1279.         'Delete files that should be backed up before deleting them
  1280.         CopyAndDeleteFile sAppdata & "\Microsoft\Templates\Normal.dotm"
  1281.         CopyAndDeleteFile sAppdata & "\Microsoft\Templates\Normalemail.dotm"
  1282.         sFolder = sAppdata & "\microsoft\document building blocks"
  1283.         If oFso.FolderExists(sFolder) Then
  1284.             Set Folder = oFso.GetFolder(sFolder)
  1285.             For Each Subfolder In Folder.Subfolders
  1286.                 If oFso.FileExists(Subfolder & "\blocks.dotx") Then CopyAndDeleteFile Subfolder & "\blocks.dotx"
  1287.             Next 'Subfolder
  1288.             Set Folder = Nothing
  1289.         End If 'oFso.FolderExists(sFolder)
  1290.     End If  
  1291.    
  1292.     'Run the individual filewipe from component detection first
  1293.     FileWipeIndividual
  1294.    
  1295.     'Take care of the rest
  1296.     DeleteFolder sOInstallRoot
  1297.     DeleteFolder sCommonProgramFiles & "\Microsoft Shared\" & OREF
  1298.     DeleteFile sAllUsersProfile & "\Application Data\Microsoft\Office\Data\opa"&OVERSIONMAJOR&".dat"
  1299.     DeleteFile sAllUsersProfile & "\Application Data\Microsoft\Office\Data\opa"&OVERSIONMAJOR&".bak"
  1300.     DeleteFile sAllUsersProfile & "\Microsoft\Office\Data\opa"&OVERSIONMAJOR&".dat"
  1301.     DeleteFile sAllUsersProfile & "\Microsoft\Office\Data\opa"&OVERSIONMAJOR&".bak"
  1302.     If (fRemoveOspp OR fForce) AND CInt(OVERSIONMAJOR)>12 Then
  1303.         DeleteService "osppsvc"
  1304.         DeleteFolder sCommonProgramFiles & "\Microsoft Shared\OfficeSoftwareProtectionPlatform"
  1305.         DeleteFolder sAllUsersProfile & "\Microsoft\OfficeSoftwareProtectionPlatform"
  1306.     End If
  1307.     Select Case OVERSIONMAJOR
  1308.     Case "12"
  1309.     Case "14"
  1310.         DeleteFile oWShell.SpecialFolders("AllUsersStartup")&"\OfficeSAS.lnk"
  1311.         DeleteFile oWShell.SpecialFolders("Startup")&"\OneNote 2010 Screen Clipper and Launcher.lnk"
  1312.     Case "15"
  1313.     Case Else
  1314.     End Select
  1315. End Sub 'FileWipeAll
  1316. '=======================================================================================================
  1317.  
  1318. 'Wipe individual files & folders related to SKU's that are no longer installed
  1319. Sub FileWipeIndividual
  1320.     Dim LogicalDisks, Disk,sc
  1321.     Dim File, Files, XmlFile, scFiles, oFile, Folder, SubFolder, Processes, Process, item
  1322.     Dim sFile, sFolder, sPath, sConfigName, sContents, sProductCode, sLocalDrives,sScQuery
  1323.     Dim sValue, sScRoots
  1324.     Dim arrSubfolders, arrShortCutRoots
  1325.     Dim fKeepFolder, fDeleteSC
  1326.     Dim iRet,iCnt,iPos
  1327.    
  1328.     Log vbCrLf & "File CleanUp"
  1329.     If IsArray(arrDeleteFiles) Then
  1330.         If fForce OR fQuiet Then
  1331.             Log " Doing Action: StopOSE"
  1332.             iRet = StopService("ose")
  1333.             Set Processes = oWmiLocal.ExecQuery("Select * From Win32_Process Where Name like 'ose%.exe'")
  1334.             For Each Process in Processes
  1335.                 LogOnly " - Running process : " & Process.Name
  1336.                 Log " -> Ending process: " & Process.Name
  1337.                 iRet = Process.Terminate()
  1338.             Next 'Process
  1339.             LogOnly " End Action: StopOSE"
  1340.             CloseOfficeApps
  1341.         End If
  1342.         'Wipe individual files detected earlier
  1343.         LogOnly " Removing left behind files"
  1344.         For Each sFile in arrDeleteFiles
  1345.             If oFso.FileExists(sFile) Then DeleteFile sFile
  1346.         Next 'File
  1347.     End If 'IsArray
  1348.    
  1349.     'Wipe Catalyst in commonfiles
  1350.     sFolder = sCommonProgramFiles & "\microsoft shared\"&OREF&"\Office Setup Controller\"
  1351.     If EnumFolderNames(sFolder,arrSubFolders) Then
  1352.         For Each SubFolder in arrSubFolders
  1353.             sPath = sFolder & SubFolder
  1354.             If InStr(SubFolder,".")>0 Then sConfigName = UCase(Left(SubFolder,InStr(SubFolder,".")-1))Else sConfigName = UCase(Subfolder)
  1355.             If GetFolderPath(sPath) Then
  1356.                 Set Folder = oFso.GetFolder(sPath)
  1357.                 Set Files = Folder.Files
  1358.                 fKeepFolder = False
  1359.                 For Each File In Files
  1360.                     If Len(File.Name)>3 Then
  1361.                         If (LCase(Right(File.Name,4))=".xml") Then
  1362.                             If Len(File.Name) >= Len(sConfigName) Then
  1363.                                 If (UCase(Left(File.Name,Len(sConfigName)))=sConfigName) Then
  1364.                                     Set XmlFile = oFso.OpenTextFile(File,1)
  1365.                                     sContents = XmlFile.ReadAll
  1366.                                     Set XmlFile = Nothing
  1367.                                     sProductCode = ""
  1368.                                     On Error Resume Next
  1369.                                     sProductCode = Mid(sContents,InStr(sContents,"ProductCode=")+Len("ProductCode=")+1,38)
  1370.                                     On Error Goto 0
  1371.                                     If Len(sProductCode) = 38 Then
  1372.                                         If CheckDelete(sProductCode) Then DeleteFile File.Path Else fKeepFolder = True
  1373.                                     End If
  1374.                                 End If 'sConfigName
  1375.                             End If 'Len >=
  1376.                         End If '.xml
  1377.                     End If 'Len(File.Name)>3
  1378.                 Next 'File
  1379.                 Set Files = Nothing
  1380.                 Set Folder = Nothing
  1381.                 If Not fKeepFolder Then DeleteFolder sPath
  1382.             End If 'GetFolderPath
  1383.         Next 'SubFolder
  1384.     End If 'EnumFolderNames
  1385.    
  1386.     'Wipe Shortcuts
  1387.     If NOT fSkipSD Then
  1388.         On Error Resume Next
  1389.         Log " Searching for shortcuts"
  1390.         CleanShortcuts sAllUsersProfile, True, False
  1391.         CleanShortcuts sProfilesDirectory, True, False
  1392.         On Error Goto 0
  1393.     End If 'NOT SkipSD
  1394.     Err.Clear
  1395.        
  1396. End Sub 'FileWipeIndividual
  1397. '=======================================================================================================
  1398.  
  1399. '-------------------------------------------------------------------------------
  1400. '   CleanShortcuts
  1401. '
  1402. '   Recursively search all profile folders for Office shortcuts in scope
  1403. '-------------------------------------------------------------------------------
  1404. Sub CleanShortcuts (sFolder, fDelete, fUnPin)
  1405.     Dim oFolder, fld, file, sc, item
  1406.     Dim fDeleteSC
  1407.  
  1408.     Set oFolder = oFso.GetFolder(sFolder)
  1409.     ' exclude system protected link folders
  1410.     If CBool(oFolder.Attributes AND 1024) Then Exit Sub
  1411.  
  1412.     On Error Resume Next
  1413.     For Each fld In oFolder.SubFolders
  1414.         If Err <> 0 Then
  1415.             CheckError "CleanShortcuts: " & vbTab & sFolder
  1416.         Else
  1417.             CleanShortcuts fld.Path, fDelete, fUnPin
  1418.         End If
  1419.     Next
  1420.     For Each file In oFolder.Files
  1421.         If LCase(Right(file.Path, 4)) = ".lnk" Then
  1422.             set sc = oWShell.CreateShortcut(file.Path)
  1423.             'Compare if the shortcut target is in the list of executables that will be removed
  1424.             If Len(sc.TargetPath) > 0 Then
  1425.                 For Each item in dicApps.Items
  1426.                     If LCase(sc.TargetPath) = item Then
  1427.                         fDeleteSC = True
  1428.                         Exit For
  1429.                     End If
  1430.                 Next 'item
  1431.             End If
  1432.             'Handle Windows Installer shortcuts
  1433.             If InStr(sc.TargetPath,"{") > 0 Then
  1434.                 If Len(sc.TargetPath) >= InStr(sc.TargetPath,"{") + 37 Then
  1435.                     If CheckDelete(Mid(sc.TargetPath, InStr(sc.TargetPath,"{"), 38)) Then fDeleteSC = True
  1436.                 End If
  1437.             End If
  1438.             If fDeleteSC Then
  1439.                 If Not IsArray(arrDeleteFolders) Then ReDim arrDeleteFolders(0)
  1440.                 sFolder = file.Drive & file.Path
  1441.                 If Not arrDeleteFolders(UBound(arrDeleteFolders)) = sFolder Then
  1442.                     ReDim Preserve arrDeleteFolders(UBound(arrDeleteFolders) + 1)
  1443.                     arrDeleteFolders(UBound(arrDeleteFolders)) = sFolder
  1444.                 End If
  1445.                 If fUnPin Then UnPin file
  1446.                 If fDelete Then DeleteFile file.Path
  1447.             End If 'fDeleteSC
  1448.         End If
  1449.     Next
  1450.     On Error Goto 0
  1451. End Sub 'CleanShortcuts
  1452.  
  1453. '-------------------------------------------------------------------------------
  1454. '   UnPin
  1455. '
  1456. '   Unpins a shortcut from the taskbar or start menu
  1457. '-------------------------------------------------------------------------------
  1458. Sub UnPin(file)
  1459.     Dim fldItem, verb
  1460.  
  1461.     On Error Resume Next
  1462.     Set fldItem = oShellApp.NameSpace(file.ParentFolder.Path).ParseName(file.Name)
  1463.     For Each verb in fldItem.Verbs
  1464.         Select Case Replace(verb, "&", "")
  1465.         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)", "Открепить от панели задач"
  1466.             verb.DoIt
  1467.         Case "Unpin from Start Menu", "Vom Startmenü lösen", "Détacher du menu Démarrer", "Détacher de la menu Démarrer"
  1468.             If iVersionNT = 601 Then verb.DoIt
  1469.         End Select
  1470.     Next
  1471.     On Error Goto 0
  1472. End Sub
  1473.  
  1474.  
  1475. '=======================================================================================================
  1476.  
  1477. Sub DelScrubTmp
  1478.    
  1479.     On Error Resume Next
  1480.     If oFso.FolderExists(sScrubDir & "\ScrubTmp") Then oFso.DeleteFolder sScrubDir & "\ScrubTmp",True
  1481.  
  1482. End Sub 'DelScrubTmp
  1483. '=======================================================================================================
  1484.  
  1485. 'Ensure there are no unexpected .msi files in the scrub folder
  1486. Sub DeleteMsiScrubCache
  1487.     Dim Folder, File, Files
  1488.    
  1489.     Log vbCrLf & "ScrubCache CleanUp"
  1490.     Set Folder = oFso.GetFolder(sScrubDir) : CheckError "DeleteMsiScrubCache"
  1491.     Set Files = Folder.Files
  1492.     For Each File in Files
  1493.         CheckError "DeleteMsiScrubCache"
  1494.         If LCase(Right(File.Name,4))=".msi" Then
  1495.             CheckError "DeleteMsiScrubCache"
  1496.             DeleteFile File.Path : CheckError "DeleteMsiScrubCache"
  1497.         End If
  1498.     Next 'File
  1499. End Sub 'DeleteMsiScrubCache
  1500. '=======================================================================================================
  1501.  
  1502. Sub MsiClearOrphanedFiles
  1503.     Const USERSIDEVERYONE = "s-1-1-0"
  1504.     Const MSIINSTALLCONTEXT_ALL = 7
  1505.     Const MSIPATCHSTATE_ALL = 15
  1506.  
  1507.     'Error handling inlined
  1508.     On Error Resume Next
  1509.  
  1510.     Dim Patch, AllPatches, Product, AllProducts
  1511.     Dim File, Files, Folder
  1512.     Dim sFName, sLocalMsp, sLocalMsi, sPatchList, sMsiList
  1513.  
  1514.     Set Folder = oFso.GetFolder(sWinDir & "\Installer")
  1515.     Set Files = Folder.Files
  1516.  
  1517.     Log vbCrLf & "Windows Installer cache CleanUp"
  1518.     'Get a complete list of patches
  1519.     Err.Clear
  1520.     Set AllPatches = oMsi.PatchesEx("",USERSIDEVERYONE,MSIINSTALLCONTEXT_ALL,MSIPATCHSTATE_ALL)
  1521.     If Err <> 0 Then
  1522.         CheckError "MsiClearOrphanedFiles (msp)"
  1523.     Else
  1524.         'Fill a comma separated stringlist with all .msp patchfiles
  1525.         For Each Patch in AllPatches
  1526.             sLocalMsp = "" : sLocalMsp = LCase(Patch.Patchproperty("LocalPackage")) : CheckError "MsiClearOrphanedFiles (msp)"
  1527.             sPatchList = sPatchList & sLocalMsp & ","
  1528.         Next 'Patch
  1529.  
  1530.         'Delete all non referenced .msp files from %windir%\installer
  1531.         For Each File in Files
  1532.             sFName = "" : sFName = LCase(File.Path)
  1533.             If LCase(Right(sFName,4)) = ".msp" Then
  1534.                 If Not InStr(sPatchList,sFName) > 0 Then
  1535.                     'While this is an orphaned file keep the scope of Office only
  1536.                     If InStr(UCase(MspTargets(File.Path)),OFFICEID)>0 Then DeleteFile File.Path
  1537.                 End If
  1538.             End If 'LCase(Right(sFName,4))
  1539.         Next 'File
  1540.     End If 'Err=0
  1541.  
  1542.     'Get a complete list products
  1543.     Err.Clear
  1544.     Set AllProducts = oMsi.ProductsEx("",USERSIDEVERYONE,MSIINSTALLCONTEXT_ALL)
  1545.     If Err <> 0 Then
  1546.         CheckError "MsiClearOrphanedFiles (msi)"
  1547.     Else
  1548.         'Fill a comma separated stringlist with all .msi files
  1549.         For Each Product in AllProducts
  1550.             sLocalMsi = "" : sLocalMsi = LCase(Product.InstallProperty("LocalPackage")) : CheckError "MsiClearOrphanedFiles (msi)"
  1551.             sMsiList = sMsiList & sLocalMsi & ","
  1552.         Next 'Product
  1553.  
  1554.         'Delete all non referenced .msi files from %windir%\installer
  1555.         For Each File in Files
  1556.             sFName = "" : sFName = LCase(File.Path)
  1557.             If LCase(Right(sFName,4)) = ".msi" Then
  1558.                 If Not InStr(sMsiList,sFName) > 0 Then
  1559.                     'While this is an orphaned file keep the scope of Office only
  1560.                     If UCase(Right(ProductCode(File.Path),PRODLEN))=OFFICEID Then DeleteFile File.Path
  1561.                 End If
  1562.             End If 'LCase(Right(sFName,4)) = ".msi"
  1563.         Next 'File
  1564.     End If 'Err=0
  1565.  
  1566. End Sub 'MsiClearOrphanedFiles
  1567. '=======================================================================================================
  1568.  
  1569. Sub RegWipe
  1570.     Dim Item, Name, Sku, key
  1571.     Dim hDefKey, sSubKeyName, sCurKey, value, sValue, sGuid
  1572.     Dim fkeep, fSystemComponent0, fPackages, fDisplayVersion
  1573.     Dim arrKeys, arrNames, arrTypes, arrMultiSzValues, arrMultiSzNewValues
  1574.     Dim arrTestNames,arrTestTypes
  1575.     Dim i, iLoopCnt, iPos
  1576.     Dim fDelReg
  1577.    
  1578.     Log vbCrLf & "Registry CleanUp"
  1579.     'Wipe registry data
  1580.    
  1581.     'User Profile settings
  1582.     Log " - User Policies"
  1583.     RegDeleteKey HKCU,"Software\Policies\Microsoft\Office\" & OVERSION & "\"
  1584.     If NOT fKeepUser Then
  1585.         RegDeleteKey HKCU,"Software\Microsoft\Office\" & OVERSION & "\"
  1586.         Log " - User Settings"
  1587.     End If 'fKeepUser
  1588.    
  1589.     'Computer specific settings
  1590.     If fRemoveAll Then
  1591.         Log " - Machine Settings"
  1592.         RegDeleteKey HKLM,"SOFTWARE\Microsoft\Office\" & OVERSION & "\"
  1593.         If fRemoveOse OR fForce Then
  1594.             RegDeleteKey HKLM,"SOFTWARE\Microsoft\Office Test\"
  1595.             RegDeleteValue HKLM,"SOFTWARE\Microsoft\Office\Common\","LastAccessInstall", False
  1596.             RegDeleteValue HKLM,"SOFTWARE\Microsoft\Office\Common\","MID", False
  1597.             RegDeleteKey HKLM,"SOFTWARE\Microsoft\Office\Excel\Addins\Microsoft.PerformancePoint.Planning.Client.Excel\"
  1598.             RegDeleteValue HKLM,"SOFTWARE\Microsoft\Office\InfoPath\Converters\Import\InfoPath.DesignerExcelImport\Versions\",OVERSION, False
  1599.             RegDeleteValue HKLM,"SOFTWARE\Microsoft\Office\InfoPath\Converters\Import\InfoPath.DesignerWordImport\Versions\",OVERSION, False
  1600.             RegDeleteKey HKLM,"SOFTWARE\Microsoft\Office\Outlook\"
  1601.             RegDeleteKey HKLM,"SOFTWARE\Microsoft\Shared Tools\Text Converters\Export\MEWord12\"
  1602.             RegDeleteKey HKLM,"SOFTWARE\Microsoft\Shared Tools\Text Converters\Export\Word12\"
  1603.             RegDeleteKey HKLM,"SOFTWARE\Microsoft\Shared Tools\Text Converters\Export\Word97\"
  1604.             RegDeleteKey HKLM,"SOFTWARE\Microsoft\Shared Tools\Text Converters\Import\MEWord12\"
  1605.             RegDeleteKey HKLM,"SOFTWARE\Microsoft\Shared Tools\Text Converters\Import\Word12\"
  1606.             RegDeleteKey HKLM,"SOFTWARE\Microsoft\Shared Tools\Text Converters\Import\Word97\"
  1607.             RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Run\","GrooveMonitor", False
  1608.             RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Run\","LobiServer", False
  1609.             RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Run\","BCSSync", False
  1610.             RegDeleteKey HKLM,"SYSTEM\CurrentControlSet\Services\Outlook\"
  1611.         End If
  1612.         RegDeleteValue HKLM,"SOFTWARE\Microsoft\Office\Common\OffDiag\Location\",OVERSIONMAJOR, False
  1613.         RegDeleteKey HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Terminal Server\Install\Software\Microsoft\Office\" & OVERSION & "\"
  1614.         RegDeleteValue HKLM,"SOFTWARE\Microsoft\Office\Common\OffDiag\Location\",OVERSIONMAJOR, False
  1615.         RegDeleteKey HKLM,"SOFTWARE\Microsoft\OfficeCustomizeWizard\" & OVERSION & "\"
  1616.         RegDeleteKey HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Terminal Server\Install\SOFTWARE\Microsoft\OfficeCustomizeWizard\" & OVERSION & "\"
  1617.        
  1618.         Select Case OVERSIONMAJOR
  1619.         Case "11"
  1620.             'Jet_Replication
  1621.             sValue = ""
  1622.             If RegReadValue(HKCR,"CLSID\{CC2C83A6-9BE4-11D0-98E7-00C04FC2CAF5}\InprocServer32","SystemDB",sValue,"REG_SZ") Then
  1623.                 If Len(sValue) > Len(sOInstallRoot) Then
  1624.                     If LCase(Left(sValue,Len(sOInstallRoot))) = LCase(sOInstallRoot) Then RegDeleteKey HKCR,"CLSID\{CC2C83A6-9BE4-11D0-98E7-00C04FC2CAF5}\InprocServer32\"
  1625.                 End If
  1626.             End If
  1627.         Case "12"
  1628.         Case "14"
  1629.             RegDeleteKey HKLM,"SOFTWARE\Microsoft\OfficeSoftwareProtectionPlatform\"
  1630.             RegDeleteKey HKLM,"SOFTWARE\Microsoft\OfficeSoftwareProtectionPlatform_Test\"
  1631.             RegDeleteKey HKLM,"SOFTWARE\Microsoft\Office\Common\ActiveX Compatibility\{00024512-0000-0000-C000-000000000046}\"
  1632.             RegDeleteValue HKLM,"SOFTWARE\Microsoft\Office\OneNote\Adapters\","{456B0D0E-49DD-4C95-8DB6-175F54DE69A3}", False
  1633.             RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{42042206-2D85-11D3-8CFF-005004838597}", False
  1634.             RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{993BE281-6695-4BA5-8A2A-7AACBFAAB69E}", False
  1635.             RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{0006F045-0000-0000-C000-000000000046}", False
  1636.             RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{C41662BB-1FA0-4CE0-8DC5-9B7F8279FF97}", False
  1637.             RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{7CCA70DB-DE7A-4FB7-9B2B-52E2335A3B5A}", False
  1638.             RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{506F4668-F13E-4AA1-BB04-B43203AB3CC0}", False
  1639.             RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{D66DC78C-4F61-447F-942B-3FB6980118CF}", False
  1640.             RegDeleteKey HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Browser Helper Objects\{B4F3A835-0E21-4959-BA22-42B3008E02FF}\"
  1641.             'Groove Extensions
  1642.             RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellExecuteHooks\","{B5A7F190-DDA6-4420-B3BA-52453494E6CD}", False
  1643.             RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{99FD978C-D287-4F50-827F-B2C658EDA8E7}", False
  1644.             RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{AB5C5600-7E6E-4B06-9197-9ECEF74D31CC}", False
  1645.             RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{920E6DB1-9907-4370-B3A0-BAFC03D81399}", False
  1646.             RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{16F3DD56-1AF5-4347-846D-7C10C4192619}", False
  1647.             RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{2916C86E-86A6-43FE-8112-43ABE6BF8DCC}", False
  1648.             RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{72853161-30C5-4D22-B7F9-0BBC1D38A37E}", False
  1649.             RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{6C467336-8281-4E60-8204-430CED96822D}", False
  1650.             RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{2A541AE1-5BF6-4665-A8A3-CFA9672E4291}", False
  1651.             RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{B5A7F190-DDA6-4420-B3BA-52453494E6CD}", False
  1652.             RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{A449600E-1DC6-4232-B948-9BD794D62056}", False
  1653.             RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{3D60EDA7-9AB4-4DA8-864C-D9B5F2E7281D}", False
  1654.             RegDeleteValue HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved\","{387E725D-DC16-4D76-B310-2C93ED4752A0}", False
  1655.             RegDeleteKey HKLM,"SOFTWARE\Classes\*\shellex\ContextMenuHandlers\XXX Groove GFS Context Menu Handler XXX\"
  1656.             RegDeleteKey HKLM,"SOFTWARE\Classes\AllFilesystemObjects\shellex\ContextMenuHandlers\XXX Groove GFS Context Menu Handler XXX\"
  1657.             RegDeleteKey HKLM,"SOFTWARE\Classes\Directory\shellex\ContextMenuHandlers\XXX Groove GFS Context Menu Handler XXX\"
  1658.             RegDeleteKey HKLM,"SOFTWARE\Classes\Folder\ShellEx\ContextMenuHandlers\XXX Groove GFS Context Menu Handler XXX\"
  1659.             RegDeleteKey HKLM,"SOFTWARE\Classes\Directory\Background\shellex\ContextMenuHandlers\XXX Groove GFS Context Menu Handler XXX\"
  1660.             RegDeleteKey HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers\Groove Explorer Icon Overlay 1 (GFS Unread Stub)\"
  1661.             RegDeleteKey HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers\Groove Explorer Icon Overlay 2 (GFS Stub)\"
  1662.             RegDeleteKey HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers\Groove Explorer Icon Overlay 2.5 (GFS Unread Folder)\"
  1663.             RegDeleteKey HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers\Groove Explorer Icon Overlay 3 (GFS Folder)\"
  1664.             RegDeleteKey HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers\Groove Explorer Icon Overlay 4 (GFS Unread Mark)\"
  1665.             RegDeleteKey HKLM,"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Browser Helper Objects\{72853161-30C5-4D22-B7F9-0BBC1D38A37E}\"
  1666.        
  1667.         Case 15
  1668.  
  1669.         Case Else
  1670.         End Select
  1671.  
  1672.         'Win32Assemblies
  1673.         Log " - Win32Assemblies"
  1674.         If RegEnumKey(HKCR,"Installer\Win32Assemblies\",arrKeys) Then
  1675.             For Each Item in arrKeys
  1676.                 If InStr(UCase(Item),OREF)>0 Then RegDeleteKey HKCR,"Installer\Win32Assemblies\"&Item & "\"
  1677.             Next 'Item
  1678.         End If 'RegEnumKey
  1679.         'Groove blocks reinstall if it locates groove.exe over this key
  1680.         If RegKeyExists(HKCR,"GrooveFile\Shell\Open\Command\") Then
  1681.             sValue = ""
  1682.             RegReadValue HKCR,"GrooveFile\Shell\Open\Command\","",sValue,"REG_SZ"
  1683.             If InStr(sValue,"\"&OREF&"\")>0 Then RegDeleteKey HKCR,"GrooveFile\"
  1684.         End If 'RegKeyExists
  1685.     End If 'fRemoveAll
  1686.  
  1687.     Select Case OVERSIONMAJOR
  1688.     Case "11"
  1689.         For iLoopCnt = 1 to 3
  1690.             Select Case iLoopCnt
  1691.             Case 1
  1692.                 'CIW - HKCU
  1693.                 sSubKeyName = "Software\Microsoft\OfficeCustomizeWizard\" & OVERSION & "\RegKeyPaths\"
  1694.                 hDefKey = HKCU
  1695.             Case 2
  1696.                 'CIW - HKLM
  1697.                 sSubKeyName = "SOFTWARE\Microsoft\OfficeCustomizeWizard\" & OVERSION & "\RegKeyPaths\"
  1698.                 hDefKey = HKLM
  1699.             Case 3
  1700.                 'Add/Remove Programs
  1701.                 sSubKeyName = REG_ARP
  1702.                 hDefKey = HKLM
  1703.             End Select
  1704.        
  1705.             If RegEnumKey(hDefKey,sSubKeyName,arrKeys) Then
  1706.                 For Each Item in arrKeys
  1707.                     'OFFICEID id
  1708.                     If Len(Item)>37 Then
  1709.                         sGuid = UCase(Left(Item,38))
  1710.                         If Right(sGuid,PRODLEN)=OFFICEID Then
  1711.                             If CheckDelete(sGuid) Then
  1712.                                 RegDeleteKey hDefKey, sSubKeyName & Item & "\"
  1713.                             End If
  1714.                         End If 'Right(Item,PRODLEN)=OFFICEID
  1715.                     End If 'Len(Item)>37
  1716.                 Next 'Item
  1717.                 If iLoopCnt < 3 Then
  1718.                     If RegEnumValues(hDefKey,sSubKeyName,arrNames,arrTypes) Then
  1719.                         i = 0
  1720.                         For Each Name in arrNames
  1721.                             If RegReadValue(hDefKey,sSubKeyName,Name,sValue,arrTypes(i)) Then
  1722.                                 If sValue = sGuid Then RegDeleteValue hDefKey,sSubKeyName,Name, False
  1723.                             End If
  1724.                             i = i + 1
  1725.                         Next
  1726.                     End If
  1727.                 End If
  1728.             End If
  1729.             If NOT RegEnumKey(hDefKey,sSubKeyName,arrKeys) Then RegDeleteKey hDefKey,"Software\Microsoft\OfficeCustomizeWizard\11.0\"
  1730.             If NOT RegEnumKey(hDefKey,"Software\Microsoft\OfficeCustomizeWizard\11.0\",arrKeys) Then RegDeleteKey hDefKey,"Software\Microsoft\OfficeCustomizeWizard\"
  1731.         Next 'iLoopCnt
  1732.     Case "12"
  1733.         'Add/Remove Programs
  1734.         RegWipeARP
  1735.     Case "14"
  1736.         'Add/Remove Programs
  1737.         RegWipeARP
  1738.     Case Else
  1739.     End Select
  1740.  
  1741.     'UpgradeCodes, WI config, WI global config
  1742.     For iLoopCnt = 1 to 5
  1743.         Select Case iLoopCnt
  1744.         Case 1
  1745.             Log " - HKLM UpgradeCodes"
  1746.             sSubKeyName = "SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UpgradeCodes\"
  1747.             hDefKey = HKLM
  1748.         Case 2
  1749.             Log " - HKCR UpgradeCodes"
  1750.             sSubKeyName = "Installer\UpgradeCodes\"
  1751.             hDefKey = HKCR
  1752.         Case 3
  1753.             Log " - HKLM Products"
  1754.             sSubKeyName = "SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\"
  1755.             hDefKey = HKLM
  1756.         Case 4
  1757.             Log " - HKCR Features"
  1758.             sSubKeyName = "Installer\Features\"
  1759.             hDefKey = HKCR
  1760.         Case 5
  1761.             Log " - HKCR Products"
  1762.             sSubKeyName = "Installer\Products\"
  1763.             hDefKey = HKCR
  1764.         Case Else
  1765.             sSubKeyName = ""
  1766.             hDefKey = ""
  1767.         End Select
  1768.         If RegEnumKey(hDefKey,sSubKeyName,arrKeys) Then
  1769.             For Each Item in arrKeys
  1770.                 'Ensure we have the expected length for a compressed GUID
  1771.                 If Len(Item)=32 Then
  1772.                     'Expand the GUID
  1773.                     sGuid = GetExpandedGuid(Item)
  1774.                     'Check if it's an Office key
  1775.                     If InScope(sGuid) Then
  1776.                         If fRemoveAll Then
  1777.                             RegDeleteKey hDefKey,sSubKeyName & Item & "\"
  1778.                         Else
  1779.                             If iLoopCnt < 3 Then
  1780.                                 'Enum all entries
  1781.                                 RegEnumValues hDefKey,sSubKeyName & Item,arrNames,arrTypes
  1782.                                 If IsArray(arrNames) Then
  1783.                                     'Delete entries within removal scope
  1784.                                     For Each Name in arrNames
  1785.                                         If Len(Name)=32 Then
  1786.                                             sGuid = GetExpandedGuid(Name)
  1787.                                             If CheckDelete(sGuid) Then RegDeleteValue hDefKey, sSubKeyName & Item & "\", Name, True
  1788.                                         Else
  1789.                                             'Invalid data -> delete the value
  1790.                                             RegDeleteValue hDefKey, sSubKeyName & Item & "\", Name, True
  1791.                                         End If
  1792.                                     Next 'Name
  1793.                                 End If 'IsArray(arrNames)
  1794.                                 'If all entries were removed - delete the key
  1795.                                 RegEnumValues hDefKey,sSubKeyName & Item,arrNames,arrTypes
  1796.                                 If Not IsArray(arrNames) Then RegDeleteKey hDefKey, sSubKeyName & Item & "\"
  1797.                             Else 'iLoopCnt >= 3
  1798.                                 If CheckDelete(sGuid) Then RegDeleteKey hDefKey, sSubKeyName & Item & "\"
  1799.                             End If 'iLoopCnt < 3
  1800.                         End If 'fRemoveAll
  1801.                     End If 'InScope
  1802.                 End If 'Len(Item)=32
  1803.             Next 'Item
  1804.         End If 'RegEnumKey
  1805.     Next 'iLoopCnt
  1806.  
  1807.     'Components
  1808.     Log " - Global Components"
  1809.     sSubKeyName = "SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components\"
  1810.     If RegEnumKey(HKLM,sSubKeyName,arrKeys) Then
  1811.         For Each Item in arrKeys
  1812.             'Ensure we have the expected length for a compressed GUID
  1813.             If Len(Item)=32 Then
  1814.                 If RegEnumValues(HKLM,sSubKeyName & Item,arrNames,arrTypes) Then
  1815.                     If IsArray(arrNames) Then
  1816.                         For Each Name in arrNames
  1817.                             If Len(Name)=32 Then
  1818.                                 sGuid = GetExpandedGuid(Name)
  1819.                                 If CheckDelete(sGuid) Then
  1820.                                     RegDeleteValue HKLM, sSubKeyName & Item & "\", Name, False
  1821.                                     'Check if the key is now empty
  1822.                                     If NOT RegEnumValues(HKLM,sSubKeyName & Item,arrTestNames,arrTestTypes) Then
  1823.                                         If NOT dicDelRegKey.Exists(sSubKeyName&Item&"\") Then dicDelRegKey.Add sSubKeyName&Item&"\",HKCR
  1824.                                     End If
  1825.                                 End If
  1826.                             End If '32
  1827.                         Next 'Name
  1828.                     End If 'IsArray
  1829.                 End If 'RegEnumValues
  1830.             End If '32
  1831.         Next 'Item
  1832.     End If 'RegEnumKey
  1833.  
  1834.     'Published Components
  1835.     Log " - Published Components"
  1836.     sSubKeyName = "Installer\Components\"
  1837.     If RegEnumKey(HKCR,sSubKeyName,arrKeys) Then
  1838.         For Each Item in arrKeys
  1839.             'Ensure we have the expected length for a compressed GUID
  1840.             If Len(Item)=32 Then
  1841.                 If RegEnumValues(HKCR,sSubKeyName & Item,arrNames,arrTypes) Then
  1842.                     If IsArray(arrNames) Then
  1843.                         For Each Name in arrNames
  1844.                             If RegReadValue (HKCR,sSubKeyName & Item, Name, sValue,"REG_MULTI_SZ") Then
  1845.                                 arrMultiSzValues = Split(sValue,chr(34))
  1846.                                 If IsArray(arrMultiSzValues) Then
  1847.                                     i = -1
  1848.                                     ReDim arrMultiSzNewValues(-1)
  1849.                                     fDelReg = False
  1850.                                     For Each value in arrMultiSzValues
  1851.                                         If Len(value) > 19 Then
  1852.                                             sGuid = ""
  1853.                                             If GetDecodedGuid(Left(value,SQUISHED),sGuid) Then
  1854.                                                 If CheckDelete(sGuid) Then
  1855.                                                     fDelReg = True
  1856.                                                 Else
  1857.                                                     i = i + 1
  1858.                                                     ReDim Preserve arrMultiSzNewValues(i)
  1859.                                                     arrMultiSzNewValues(i) = value
  1860.                                                 End If 'CheckDelete
  1861.                                             End If 'decode
  1862.                                         End If '19
  1863.                                     Next 'Value
  1864.                                     If NOT (i = -1) Then
  1865.                                         If NOT fDetectOnly Then
  1866.                                             If NOT UBound(arrMultiSzValues) = i Then oReg.SetMultiStringValue HKCR,sSubKeyName & Item,Name,arrMultiSzNewValues
  1867.                                         End If
  1868.                                     Else
  1869.                                         If fDelReg Then
  1870.                                             RegDeleteValue HKCR,sSubKeyName & Item & "\", Name, False
  1871.                                             'Check if the key is now empty
  1872.                                             If NOT RegEnumValues(HKCR,sSubKeyName & Item,arrTestNames,arrTestTypes) Then
  1873.                                                 If NOT dicDelRegKey.Exists(sSubKeyName&Item&"\") Then dicDelRegKey.Add sSubKeyName&Item&"\",HKCR
  1874.                                             End If
  1875.                                         End If 'DelReg
  1876.                                     End If
  1877.                                 End If 'IsArray
  1878.                             End If
  1879.                         Next 'Name
  1880.                     End If 'IsArray
  1881.                 End If 'RegEnumValues
  1882.             End If '32
  1883.         Next 'Item
  1884.     End If 'RegEnumKey
  1885.  
  1886.     'Delivery
  1887.     Log " - Delivery"
  1888.     hDefKey = HKLM
  1889.     sSubKeyName = "SOFTWARE\Microsoft\Office\Delivery\SourceEngine\Downloads\"
  1890.     If RegEnumKey(HKLM,sSubKeyName,arrKeys) Then
  1891.         For Each Item in arrKeys
  1892.             If Len(Item) > 37 Then
  1893.                 If fRemoveAll Then
  1894.                     If (Mid(Item,27,PRODLEN)=OFFICEID AND Mid(Item,4,2)=OVERSIONMAJOR) OR _
  1895.                        LCase(Right(Item,7))=OVERSIONMAJOR&".data" Then RegDeleteKey HKLM,sSubKeyName & Item & "\"
  1896.                 Else
  1897.                     If (Mid(Item,27,PRODLEN)=OFFICEID AND Mid(Item,4,2)=OVERSIONMAJOR) AND _
  1898.                        CheckDelete(UCase(Left(Item,38))) Then RegDeleteKey HKLM,sSubKeyName & Item & "\"
  1899.                 End If
  1900.             End If '37
  1901.         Next 'Item
  1902.     End If 'RegEnumKey
  1903.    
  1904.     'Registration
  1905.     Log " - HKLM Registration"
  1906.     hDefKey = HKLM
  1907.     sSubKeyName = "SOFTWARE\Microsoft\Office\"&OVERSION&"\Registration\"
  1908.     If RegEnumKey(HKLM,sSubKeyName,arrKeys) Then
  1909.         For Each Item in arrKeys
  1910.             If Len(Item)>37 Then
  1911.                 If CheckDelete(UCase(Left(Item,38))) Then RegDeleteKey HKLM,sSubKeyName & Item & "\"
  1912.             End If
  1913.         Next 'Item
  1914.     End If 'RegEnumKey
  1915.    
  1916.     'User Preconfigurations
  1917.     Log " - HKLM User Preconfigurations"
  1918.     hDefKey = HKLM
  1919.     sSubKeyName = "SOFTWARE\Microsoft\Office\"&OVERSION&"\User Settings\"
  1920.     If RegEnumKey(HKLM,sSubKeyName,arrKeys) Then
  1921.         For Each Item in arrKeys
  1922.             If Len(Item)>37 Then
  1923.                 If CheckDelete(UCase(Left(Item,38))) Then RegDeleteKey HKLM,sSubKeyName & Item & "\"
  1924.             End If
  1925.         Next 'Item
  1926.     End If 'RegEnumKey
  1927.  
  1928.     'Known Keypath settings
  1929.     Log " - Detcted KeyPath settings"
  1930.     For Each key in dicDelRegKey.Keys
  1931.         If Right(key,1) = "\" Then
  1932.             RegDeleteKey dicDelRegKey.Item(key),key
  1933.         Else
  1934.             iPos = InStrRev(Key,"\")
  1935.             If iPos > 0 Then RegDeleteValue dicDelRegKey.Item(key), Left(key,iPos - 1), Mid(key,iPos+1), False
  1936.         End If
  1937.     Next
  1938.  
  1939.     'Temporary entries in ARP
  1940.     TmpKeyCleanUp
  1941. End Sub 'RegWipe
  1942. '=======================================================================================================
  1943.  
  1944. 'Clean up Add/Remove Programs registry
  1945. Sub RegWipeARP
  1946.  
  1947.     Dim Item, Name, Sku, key
  1948.     Dim sSubKeyName, sCurKey, sValue, sGuid
  1949.     Dim fkeep, fSystemComponent0, fPackages, fDisplayVersion
  1950.     Dim arrKeys
  1951.  
  1952.     'Add/Remove Programs
  1953.     sSubKeyName = REG_ARP
  1954.     If RegEnumKey(HKLM,sSubKeyName,arrKeys) Then
  1955.         For Each Item in arrKeys
  1956.             '*0FF1CE*
  1957.             If Len(Item)>37 Then
  1958.                 sGuid = UCase(Left(Item,38))
  1959.                 If InScope(sGuid) Then
  1960.                     If CheckDelete(sGuid) Then RegDeleteKey HKLM, sSubKeyName & Item
  1961.                 End If 'InScope
  1962.             End If 'Len(Item)>37
  1963.            
  1964.             'Config entries
  1965.             sCurKey = sSubKeyName & Item & "\"
  1966.             fSystemComponent0 = Not (RegReadValue(HKLM,sCurKey,"SystemComponent",sValue,"REG_DWORD") AND (sValue = "1"))
  1967.             fPackages = RegReadValue(HKLM,sCurKey,OPACKAGE,sValue,"REG_MULTI_SZ")
  1968.             fDisplayVersion = RegReadValue(HKLM,sCurKey,"DisplayVersion",sValue,"REG_SZ")
  1969.             If fDisplayVersion AND Len(sValue) > 1 Then
  1970.                 fDisplayVersion = (Left(sValue,2) = OVERSIONMAJOR)
  1971.             End If
  1972.             If (fSystemComponent0 AND fPackages AND fDisplayVersion) Then
  1973.                 fKeep = False
  1974.                 If Not fRemoveAll Then
  1975.                     For Each Sku in dicKeepSku.Keys
  1976.                         If UCase(Item) =  OREGREF & Sku Then
  1977.                             fkeep = True
  1978.                             Exit For
  1979.                         End If
  1980.                     Next 'Sku
  1981.                 End If
  1982.                 If Not fkeep Then RegDeleteKey HKLM, sSubKeyName & Item
  1983.             End If
  1984.         Next 'Item
  1985.     End If 'RegEnumKey
  1986.  
  1987. End Sub 'RegWipeARP
  1988. '=======================================================================================================
  1989.  
  1990. 'Clean up temporary registry keys
  1991. Sub TmpKeyCleanUp
  1992.     Dim TmpKey
  1993.    
  1994.     If fLogInitialized Then Log " - temporary OffScrub registry entries"
  1995.     If IsArray(arrTmpSKUs) Then
  1996.         For Each TmpKey in arrTmpSKUs
  1997.             oReg.DeleteKey HKLM, REG_ARP & TmpKey
  1998.         Next 'Item
  1999.     End If 'IsArray
  2000. End Sub 'TmpKeyCleanUp
  2001.  
  2002. '=======================================================================================================
  2003. ' Helper Functions
  2004. '=======================================================================================================
  2005.  
  2006. 'Create a log with the results of the SKU detection
  2007. Sub LogSkuResults
  2008.     Dim SkuLog, SkuKey , p
  2009.  
  2010.     On Error Resume Next 'Don't fail on logging
  2011.    
  2012.     Set SkuLog = oFso.OpenTextFile(sScrubDir & "\SkuLog.txt",FOR_WRITING,True,True)
  2013.    
  2014.     SkuLog.WriteLine "Installed SKUs (All):"
  2015.     SkuLog.WriteLine "====================="
  2016.     For Each SkuKey in dicInstalledSku.Keys
  2017.         SkuLog.WriteLine " - " & SkuKey
  2018.     Next 'Key
  2019.  
  2020.     SkuLog.WriteLine vbCrLf & "Server SKUs:"
  2021.     SkuLog.WriteLine          "============"
  2022.     For Each SkuKey in dicSrv.Keys
  2023.         SkuLog.WriteLine " - " & SkuKey
  2024.     Next 'Key
  2025.  
  2026.     SkuLog.WriteLine vbCrLf & "Client Suite SKUs:"
  2027.     SkuLog.WriteLine          "=================="
  2028.     For Each SkuKey in dicCSuite.Keys
  2029.         SkuLog.WriteLine " - " & SkuKey
  2030.     Next 'Key
  2031.  
  2032.     SkuLog.WriteLine vbCrLf & "Client Standalone SKUs:"
  2033.     SkuLog.WriteLine          "======================="
  2034.     For Each SkuKey in dicCSingle.Keys
  2035.         SkuLog.WriteLine " - " & SkuKey
  2036.     Next 'Key
  2037.  
  2038.     SkuLog.WriteLine vbCrLf & "Installed Products (All):"
  2039.     SkuLog.WriteLine          "========================="
  2040.     For Each p in oMsi.Products
  2041.         If InScope(p) Then
  2042.             SkuLog.Write " - " & p & " - "
  2043.             SkuLog.Write oMsi.ProductInfo(p, "ProductName")
  2044.             SkuLog.WriteLine " "
  2045.         End If
  2046.     Next 'Product
  2047.  
  2048.     SkuLog.WriteLine vbCrLf & "***************************************************************************************************" & vbCrLf
  2049.  
  2050.     SkuLog.WriteLine vbCrLf & "SKUs to keep:"
  2051.     SkuLog.WriteLine          "============="
  2052.     For Each SkuKey in dicKeepSku.Keys
  2053.         SkuLog.WriteLine " - " & SkuKey
  2054.     Next 'Key
  2055.  
  2056.     SkuLog.WriteLine vbCrLf & "Products to keep:"
  2057.     SkuLog.WriteLine          "================="
  2058.     For Each p in dicKeepProd.Keys
  2059.         SkuLog.Write " - " & p & " - "
  2060.         SkuLog.Write oMsi.ProductInfo(p, "ProductName")
  2061.         SkuLog.WriteLine " "
  2062.     Next 'Key
  2063.  
  2064.     SkuLog.WriteLine vbCrLf & "***************************************************************************************************" & vbCrLf
  2065.  
  2066.     SkuLog.WriteLine vbCrLf & "SKUs to remove:"
  2067.     SkuLog.WriteLine          "==============="
  2068.     For Each SkuKey in dicRemoveSku.Keys
  2069.         SkuLog.WriteLine " - " & SkuKey
  2070.     Next 'Key
  2071.  
  2072.     SkuLog.WriteLine vbCrLf & "Products to remove:"
  2073.     SkuLog.WriteLine          "==================="
  2074.     For Each p in oMsi.Products
  2075.         If InScope(p) Then
  2076.             If (fRemoveAll OR CheckDelete(p))Then
  2077.                 SkuLog.Write " - " & p & " - "
  2078.                 SkuLog.Write oMsi.ProductInfo(p, "ProductName")
  2079.                 SkuLog.WriteLine " "
  2080.             End If
  2081.         End If 'InScope
  2082.     Next 'Product
  2083.  
  2084.     SkuLog.Close
  2085.     Set SkuLog = Nothing
  2086.  
  2087. End Sub 'LogSkuResults
  2088. '=======================================================================================================
  2089.  
  2090. 'End all running instances of applications that will be removed
  2091. Sub CloseOfficeApps
  2092.     Dim Processes, Process, prop
  2093.     Dim fWait
  2094.     Dim iRet
  2095.    
  2096.     On Error Resume Next
  2097.    
  2098.     fWait = False
  2099.     Log " Doing Action: CloseOfficeApps"
  2100.  
  2101.     Set Processes = oWmiLocal.ExecQuery("Select * From Win32_Process")
  2102.     For Each Process in Processes
  2103.         If dicApps.Exists(LCase(Process.Name)) Then
  2104.             Log " - End process " & Process.Name
  2105.             iRet = Process.Terminate()
  2106.             CheckError "CloseOfficeApps: " & "Process.Name"
  2107.         Else
  2108.             For Each prop in Process.Properties_
  2109.                 If prop.Name = "ExecutablePath" Then
  2110.                     If InStr(UCase(prop.Value), UCase(sOInstallRoot)) > 0 Then
  2111.                         Log = " - End process '" & Process.Name
  2112.                         iRet = Process.Terminate()
  2113.                         CheckError "CloseOfficeApps: " & "Process.Name"
  2114.                         fWait = True
  2115.                     End If
  2116.                 End If 'ExcecutablePath
  2117.             Next 'prop
  2118.         End If
  2119.     Next 'Process
  2120.     If fWait Then
  2121.         wscript.sleep 10000
  2122.     End If
  2123.     LogOnly " End Action: CloseOfficeApps"
  2124. End Sub 'CloseOfficeApps
  2125. '=======================================================================================================
  2126.  
  2127. 'Ensure Windows Explorer is restarted if needed
  2128. Sub RestoreExplorer
  2129.     Dim Processes, Result, oAT, DateTime, JobID
  2130.     Dim sCmd
  2131.    
  2132.     'Non critical routine. Don't fail on error
  2133.     On Error Resume Next
  2134.     wscript.sleep 1000
  2135.     Set Processes = oWmiLocal.ExecQuery("Select * From Win32_Process Where Name='explorer.exe'")
  2136.     If Processes.Count < 1 Then
  2137.         oWShell.Run "explorer.exe"
  2138.         'To handle this in case of System context, schedule and run as interactive task
  2139.         If iVersionNT > 502 Then
  2140.             'Vista and later
  2141.             oWShell.Run "SCHTASKS /Create /TN OffScrEx /TR explorer /SC ONCE /ST 12:00 /IT",0,True
  2142.             oWShell.Run "SCHTASKS /Run /TN OffScrEx", 0, True
  2143.             oWShell.Run "SCHTASKS /Delete /TN OffScrEx /F", 0, False
  2144.         Else
  2145.             Set oAT = oWmiLocal.Get("Win32_ScheduledJob")
  2146.             Set DateTime = CreateObject("WbemScripting.SWbemDateTime")
  2147.             DateTime.SetVarDate DateAdd("n",1,Now),True
  2148.             Result = oAT.Create("explorer.exe", DateTime.Value, , , , True, JobID)
  2149.         End If 'iVersionNT
  2150.     End If
  2151. End Sub 'RestoreExploer
  2152. '=======================================================================================================
  2153.  
  2154. 'Returns the delimiter for a passed in string
  2155. Function Delimiter (sVersion)
  2156.  
  2157.     Dim iCnt, iAsc
  2158.  
  2159.     Delimiter = " "
  2160.     For iCnt = 1 To Len(sVersion)
  2161.         iAsc = Asc(Mid(sVersion, iCnt, 1))
  2162.         If Not (iASC >= 48 And iASC <= 57) Then
  2163.             Delimiter = Mid(sVersion, iCnt, 1)
  2164.             Exit Function
  2165.         End If
  2166.     Next 'iCnt
  2167. End Function
  2168. '=======================================================================================================
  2169.  
  2170. 'Check registry access permissions. Failure will terminate the script
  2171. Function CheckRegPermissions
  2172.     Const KEY_QUERY_VALUE       = &H0001
  2173.     Const KEY_SET_VALUE         = &H0002
  2174.     Const KEY_CREATE_SUB_KEY    = &H0004
  2175.     Const DELETE                = &H00010000
  2176.  
  2177.     Dim sSubKeyName
  2178.     Dim fReturn
  2179.  
  2180.     CheckRegPermissions = True
  2181.     sSubKeyName = "Software\Microsoft\Windows\"
  2182.     oReg.CheckAccess HKLM, sSubKeyName, KEY_QUERY_VALUE, fReturn
  2183.     If Not fReturn Then CheckRegPermissions = False
  2184.     oReg.CheckAccess HKLM, sSubKeyName, KEY_SET_VALUE, fReturn
  2185.     If Not fReturn Then CheckRegPermissions = False
  2186.     oReg.CheckAccess HKLM, sSubKeyName, KEY_CREATE_SUB_KEY, fReturn
  2187.     If Not fReturn Then CheckRegPermissions = False
  2188.     oReg.CheckAccess HKLM, sSubKeyName, DELETE, fReturn
  2189.     If Not fReturn Then CheckRegPermissions = False
  2190.  
  2191. End Function 'CheckRegPermissions
  2192. '=======================================================================================================
  2193.  
  2194. 'Check if an Office product is still registered with a SKU that stays on the computer
  2195. Function CheckDelete(sProductCode)
  2196.        
  2197.     'Ensure valid GUID length
  2198.     If NOT Len(sProductCode) = 38 Then
  2199.         CheckDelete = False
  2200.         Exit Function
  2201.     End If
  2202.  
  2203.     'If it's a non Office ProductCode exit with false right away
  2204.     CheckDelete = InScope(sProductCode)
  2205.     If Not CheckDelete Then Exit Function
  2206.     If dicKeepProd.Exists(UCase(sProductCode)) Then CheckDelete = False
  2207.  
  2208. End Function 'CheckDelete
  2209. '=======================================================================================================
  2210.  
  2211. 'Check if ProductCode is in scope
  2212. Function InScope(sProductCode)
  2213.  
  2214.     Dim fInScope
  2215.     Dim sProd
  2216.  
  2217.     fInScope = False
  2218.     If Len(sProductCode) = 38 Then
  2219.         sProd = UCase(sProductCode)
  2220.         Select Case OVERSIONMAJOR
  2221.         Case "11"
  2222.             If Right(sProd,PRODLEN)=OFFICEID Then InScope = True
  2223.         Case "12"
  2224.             If Right(sProd,PRODLEN)=OFFICEID AND Mid(sProd,4,2) = OVERSIONMAJOR Then fInScope = True
  2225.         Case "14"
  2226.             If Right(sProd,PRODLEN)=OFFICEID AND Mid(sProd,4,2) = OVERSIONMAJOR Then fInScope = True
  2227.         Case "15"
  2228.             If Right(sProd,PRODLEN)=OFFICEID AND Mid(sProd,4,2) = OVERSIONMAJOR Then
  2229.                 Select Case Mid(sProd, 11, 4)
  2230.                 Case "007E", "008F", "008C"
  2231.                     ' C2R products - keep them
  2232.                 Case Else
  2233.                     fInScope = True
  2234.                 End Select
  2235.             End If
  2236.         Case Else
  2237.         End Select
  2238.     End If '38
  2239.  
  2240.     InScope = fInScope
  2241. End Function 'InScope
  2242. '=======================================================================================================
  2243.  
  2244. 'Register an orphaned .msi product as installed for MSI
  2245. Sub MsiRegisterProduct (sMsiFile)
  2246.  
  2247.     Dim sDisplayVersion, sCurKey, sDisplayName, sLang, sProductCode, sTmpKey
  2248.     Dim iCnt
  2249.  
  2250.     'Create a temporary keys to simulate an installed product
  2251.     sProductCode = ""
  2252.     sProductCode = GetMsiProductCode(sMsiFile)
  2253.     sDisplayVersion = GetMsiProductVersion(sMsiFile)
  2254.     If sDisplayVersion = "" Then sDisplayVersion = OVERSION & ".0000.0000"
  2255.     sDisplayName = GetMsiProductName(sMsiFile)
  2256.     If sDisplayName = "" Then sDisplayName = sProductCode
  2257.     Select Case OVERSIONMAJOR
  2258.     Case "9","10","11"
  2259.         sLang = CInt("&h" & Mid(sProductCode,6,4))
  2260.     Case "12","14"
  2261.         sLang = CInt("&h" & Mid(sProductCode,16,4))
  2262.     Case Else
  2263.     End Select
  2264.  
  2265.     For iCnt = 1 To 3
  2266.         Select Case iCnt
  2267.         Case 1
  2268.             sCurKey = REG_ARP & sProductCode
  2269.             oReg.CreateKey HKLM,sCurKey
  2270.         Case 2
  2271.             sCurKey = "SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\" & GetCompressedGuid(sProductCode)
  2272.             oReg.CreateKey HKLM,sCurKey
  2273.             oReg.CreateKey HKLM,sCurKey & "\Features"
  2274.             oReg.CreateKey HKLM,sCurKey & "\InstallProperties"
  2275.             oReg.CreateKey HKLM,sCurKey & "\Patches"
  2276.             oReg.CreateKey HKLM,sCurKey & "\Usage"
  2277.             sCurKey = sCurKey & "\InstallProperties"
  2278.             oReg.SetStringValue HKLM,sCurKey,"LocalPackage",sMsiFile
  2279.         Case 3
  2280.             sCurKey = "Installer\Products\" & GetCompressedGuid(sProductCode)
  2281.             sTmpKey = sCurKey
  2282.             oReg.CreateKey HKCR,sCurKey
  2283.             oReg.SetDWordValue HKCR,sCurKey,"AdvertiseFlags",388
  2284.             oReg.SetDWordValue HKCR,sCurKey,"Assignment",1
  2285.             oReg.SetDWordValue HKCR,sCurKey,"AuthorizedLUAApp",0
  2286.             oReg.SetStringValue HKCR,sCurKey,"Clients",":"
  2287.             oReg.SetDWordValue HKCR,sCurKey,"DeploymentFlags",3
  2288.             oReg.SetDWordValue HKCR,sCurKey,"InstanceType",0
  2289.             oReg.SetDWordValue HKCR,sCurKey,"Language",sLang
  2290.             oReg.SetStringValue HKCR,sCurKey,"PackageCode",GetMsiPackageCode(sMsiFile)
  2291.             oReg.SetStringValue HKCR,sCurKey,"ProductName",sDisplayName
  2292.             oReg.SetDWordValue HKCR,sCurKey,"VersionMinor",0
  2293.             sCurKey = sTmpKey & "\SourceList"
  2294.             oReg.CreateKey HKCR,sCurKey
  2295.             oReg.SetExpandedStringValue HKCR,sCurKey,"LastUsedSource",sScrubDir
  2296.             oReg.SetStringValue HKCR,sCurKey,"PackageName",Mid(sMsiFile,InstrRev(sMsiFile,"\")+1)
  2297.             sCurKey = sTmpKey & "\SourceList\Media"
  2298.             oReg.CreateKey HKCR,sCurKey
  2299.             oReg.SetStringValue HKCR,sCurKey,"1",OREF & ";1"
  2300.             oReg.SetStringValue HKCR,sCurKey,"DiskPrompt",sDisplayName
  2301.             sCurKey = sTmpKey & "\SourceList\Net"
  2302.             oReg.CreateKey HKCR,sCurKey
  2303.             oReg.SetExpandedStringValue HKCR,sCurKey,"1",sScrubDir
  2304.  
  2305.         Case Else
  2306.         End Select
  2307.         If iCnt <3 Then
  2308.             oReg.SetStringValue HKLM,sCurKey,"Comments",""
  2309.             oReg.SetStringValue HKLM,sCurKey,"Contact",""
  2310.             oReg.SetStringValue HKLM,sCurKey,"DisplayName",sDisplayName
  2311.             oReg.SetStringValue HKLM,sCurKey,"DisplayVersion",sDisplayVersion
  2312.             oReg.SetDWordValue HKLM,sCurKey,"EstimatedSize",0
  2313.             oReg.SetStringValue HKLM,sCurKey,"HelpLink",""
  2314.             oReg.SetStringValue HKLM,sCurKey,"HelpTelephone",""
  2315.             oReg.SetStringValue HKLM,sCurKey,"InstallDate","20100101"
  2316.             If f64 Then
  2317.                 oReg.SetStringValue HKLM,sCurKey,"InstallLocation",sProgramFilesX86
  2318.             Else
  2319.                 oReg.SetStringValue HKLM,sCurKey,"InstallLocation",sProgramFiles
  2320.             End If
  2321.             oReg.SetStringValue HKLM,sCurKey,"InstallSource",sScrubDir
  2322.             oReg.SetDWordValue HKLM,sCurKey,"Language",sLang
  2323.             oReg.SetExpandedStringValue HKLM,sCurKey,"ModifyPath","MsiExec.exe /X" & sProductCode
  2324.             oReg.SetDWordValue HKLM,sCurKey,"NoModify",1
  2325.             oReg.SetStringValue HKLM,sCurKey,"Publisher","Microsoft Corporation"
  2326.             oReg.SetStringValue HKLM,sCurKey,"Readme",""
  2327.             oReg.SetStringValue HKLM,sCurKey,"Size",""
  2328.             oReg.SetDWordValue HKLM,sCurKey,"SystemComponent",0
  2329.             oReg.SetExpandedStringValue HKLM,sCurKey,"UninstallString","MsiExec.exe /X" & sProductCode
  2330.             oReg.SetStringValue HKLM,sCurKey,"URLInfoAbout",""
  2331.             oReg.SetStringValue HKLM,sCurKey,"URLUpdateInfo",""
  2332.             oReg.SetDWordValue HKLM,sCurKey,"Version",0
  2333.             oReg.SetDWordValue HKLM,sCurKey,"VersionMajor",OVERSIONMAJOR
  2334.             oReg.SetDWordValue HKLM,sCurKey,"VersionMinor",0
  2335.             oReg.SetDWordValue HKLM,sCurKey,"WindowsInstaller",1
  2336.         End If '< 3
  2337.     Next 'iCnt
  2338.  
  2339. End Sub 'MsiRegisterProduct
  2340. '=======================================================================================================
  2341.  
  2342. 'Obtain the ProductCode (GUID) from a .msi package
  2343. 'The function will open the .msi database and query the 'Property' table to retrieve the ProductCode
  2344. Function GetMsiProductCode(sMsiFile)
  2345.    
  2346.     Dim MsiDb,Record
  2347.     Dim qView
  2348.    
  2349.     On Error Resume Next
  2350.    
  2351.     GetMsiProductCode = ""
  2352.     Set Record = Nothing
  2353.    
  2354.     Set MsiDb = oMsi.OpenDatabase(sMsiFile,MSIOPENDATABASEREADONLY)
  2355.     Set qView = MsiDb.OpenView("SELECT `Value` FROM Property WHERE `Property` = 'ProductCode'")
  2356.     qView.Execute
  2357.     Set Record = qView.Fetch
  2358.     GetMsiProductCode = Record.StringData(1)
  2359.     qView.Close
  2360.  
  2361. End Function 'GetMsiProductCode
  2362. '=======================================================================================================
  2363.  
  2364. 'Obtain the ProductVersion from a .msi package
  2365. 'The function will open the .msi database and query the 'Property' table to retrieve the ProductCode
  2366. Function GetMsiProductVersion(sMsiFile)
  2367.    
  2368.     Dim MsiDb,Record
  2369.     Dim qView
  2370.    
  2371.     On Error Resume Next
  2372.    
  2373.     GetMsiProductVersion = ""
  2374.     Set Record = Nothing
  2375.    
  2376.     Set MsiDb = oMsi.OpenDatabase(sMsiFile,MSIOPENDATABASEREADONLY)
  2377.     Set qView = MsiDb.OpenView("SELECT `Value` FROM Property WHERE `Property` = 'ProductVersion'")
  2378.     qView.Execute
  2379.     Set Record = qView.Fetch
  2380.     GetMsiProductVersion = Record.StringData(1)
  2381.     qView.Close
  2382.  
  2383. End Function 'GetMsiProductVersion
  2384. '=======================================================================================================
  2385.  
  2386. 'Obtain the ProductVersion from a .msi package
  2387. 'The function will open the .msi database and query the 'Property' table to retrieve the ProductCode
  2388. Function GetMsiProductName(sMsiFile)
  2389.    
  2390.     Dim MsiDb,Record
  2391.     Dim qView
  2392.    
  2393.     On Error Resume Next
  2394.    
  2395.     GetMsiProductName = ""
  2396.     Set Record = Nothing
  2397.    
  2398.     Set MsiDb = oMsi.OpenDatabase(sMsiFile,MSIOPENDATABASEREADONLY)
  2399.     Set qView = MsiDb.OpenView("SELECT `Value` FROM Property WHERE `Property` = 'ProductName'")
  2400.     qView.Execute
  2401.     Set Record = qView.Fetch
  2402.     GetMsiProductName = Record.StringData(1)
  2403.     qView.Close
  2404.  
  2405. End Function 'GetMsiProductVersion
  2406. '=======================================================================================================
  2407.  
  2408. 'Obtain the PackageCode (GUID) from a .msi package
  2409. 'The function will the .msi'S SummaryInformation stream
  2410. Function GetMsiPackageCode(sMsiFile)
  2411.  
  2412.     On Error Resume Next
  2413.  
  2414.     Const PID_REVNUMBER = 9
  2415.    
  2416.     GetMsiPackageCode = ""
  2417.     GetMsiPackageCode = GetCompressedGuid(oMsi.SummaryInformation(sMsiFile,MSIOPENDATABASEREADONLY).Property(PID_REVNUMBER))
  2418.  
  2419. End Function 'GetMsiPackageCode
  2420. '=======================================================================================================
  2421.  
  2422. 'Returns a string with a list of ProductCodes from the summary information stream
  2423. Function MspTargets (sMspFile)
  2424.     Const MSIOPENDATABASEMODE_PATCHFILE = 32
  2425.     Const PID_TEMPLATE                  =  7
  2426.  
  2427.     Dim Msp
  2428.     'Non critical routine. Don't fail on error
  2429.     On Error Resume Next
  2430.     MspTargets = ""
  2431.     If oFso.FileExists(sMspFile) Then
  2432.         Set Msp = Msi.OpenDatabase(WScript.Arguments(0),MSIOPENDATABASEMODE_PATCHFILE)
  2433.         If Err = 0 Then MspTargets = Msp.SummaryInformation.Property(PID_TEMPLATE)
  2434.     End If 'oFso.FileExists(sMspFile)
  2435. End Function 'MspTargets
  2436. '=======================================================================================================
  2437.  
  2438. 'Return the ProductCode {GUID} from a .MSI package
  2439. Function ProductCode(sMsi)
  2440.     Const MSIUILEVELNONE = 2 'No UI
  2441.     Dim MsiSession
  2442.  
  2443.     On Error Resume Next
  2444.     'Non critical routine. Don't fail on error
  2445.     If oFso.FileExists(sMsi) Then
  2446.         oMsi.UILevel = MSIUILEVELNONE
  2447.         Set MsiSession = oMsi.OpenPackage(sMsi,1)
  2448.         ProductCode = MsiSession.ProductProperty("ProductCode")
  2449.         Set MsiSession = Nothing
  2450.     Else
  2451.         ProductCode = ""
  2452.     End If 'oFso.FileExists(sMsi)
  2453. End Function 'ProductCode
  2454. '=======================================================================================================
  2455.  
  2456. Function GetExpandedGuid (sGuid)
  2457.     Dim i
  2458.  
  2459.     'Ensure valid length
  2460.     If NOT Len(sGuid) = 32 Then Exit Function
  2461.  
  2462.     GetExpandedGuid = "{" & StrReverse(Mid(sGuid,1,8)) & "-" & _
  2463.                        StrReverse(Mid(sGuid,9,4)) & "-" & _
  2464.                        StrReverse(Mid(sGuid,13,4))& "-"
  2465.     For i = 17 To 20
  2466.         If i Mod 2 Then
  2467.             GetExpandedGuid = GetExpandedGuid & mid(sGuid,(i + 1),1)
  2468.         Else
  2469.             GetExpandedGuid = GetExpandedGuid & mid(sGuid,(i - 1),1)
  2470.         End If
  2471.     Next
  2472.     GetExpandedGuid = GetExpandedGuid & "-"
  2473.     For i = 21 To 32
  2474.         If i Mod 2 Then
  2475.             GetExpandedGuid = GetExpandedGuid & mid(sGuid,(i + 1),1)
  2476.         Else
  2477.             GetExpandedGuid = GetExpandedGuid & mid(sGuid,(i - 1),1)
  2478.         End If
  2479.     Next
  2480.     GetExpandedGuid = GetExpandedGuid & "}"
  2481. End Function
  2482. '=======================================================================================================
  2483.  
  2484. 'Converts a GUID into the compressed format
  2485. Function GetCompressedGuid (sGuid)
  2486.     Dim sCompGUID
  2487.     Dim i
  2488.    
  2489.     'Ensure Valid Length
  2490.     If NOT Len(sGuid) = 38 Then Exit Function
  2491.  
  2492.     sCompGUID = StrReverse(Mid(sGuid,2,8))  & _
  2493.                 StrReverse(Mid(sGuid,11,4)) & _
  2494.                 StrReverse(Mid(sGuid,16,4))
  2495.     For i = 21 To 24
  2496.         If i Mod 2 Then
  2497.             sCompGUID = sCompGUID & Mid(sGuid, (i + 1), 1)
  2498.         Else
  2499.             sCompGUID = sCompGUID & Mid(sGuid, (i - 1), 1)
  2500.         End If
  2501.     Next
  2502.     For i = 26 To 37
  2503.         If i Mod 2 Then
  2504.             sCompGUID = sCompGUID & Mid(sGuid, (i - 1), 1)
  2505.         Else
  2506.             sCompGUID = sCompGUID & Mid(sGuid, (i + 1), 1)
  2507.         End If
  2508.     Next
  2509.     GetCompressedGuid = sCompGUID
  2510. End Function
  2511. '=======================================================================================================
  2512.  
  2513. 'Unsquish GUID
  2514. Function GetDecodedGuid(sEncGuid, sGuid)
  2515.  
  2516. Dim sDecode, sTable, sHex, iChr
  2517. Dim arrTable
  2518. Dim i, iAsc, pow85, decChar
  2519. Dim lTotal
  2520. Dim fFailed
  2521.  
  2522.     fFailed = False
  2523.  
  2524.     sTable =    "0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff," & _
  2525.                 "0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff," & _
  2526.                 "0xff,0x00,0xff,0xff,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0xff," & _
  2527.                 "0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0xff,0xff,0xff,0x16,0xff,0x17," & _
  2528.                 "0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27," & _
  2529.                 "0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x30,0x31,0x32,0x33,0xff,0x34,0x35,0x36," & _
  2530.                 "0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x40,0x41,0x42,0x43,0x44,0x45,0x46," & _
  2531.                 "0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,0x50,0x51,0x52,0xff,0x53,0x54,0xff"
  2532.     arrTable = Split(sTable,",")
  2533.     lTotal = 0 : pow85 = 1
  2534.     For i = 0 To 19
  2535.         fFailed = True
  2536.         If i Mod 5 = 0 Then
  2537.             lTotal = 0 : pow85 = 1
  2538.         End If ' i Mod 5 = 0
  2539.         iAsc = Asc(Mid(sEncGuid,i+1,1))
  2540.         sHex = arrTable(iAsc)
  2541.         If iAsc >=128 Then Exit For
  2542.         If sHex = "0xff" Then Exit For
  2543.         iChr = CInt("&h"&Right(sHex,2))
  2544.         lTotal = lTotal + (iChr * pow85)
  2545.         If i Mod 5 = 4 Then sDecode = sDecode & DecToHex(lTotal)
  2546.         pow85 = pow85 * 85
  2547.         fFailed = False
  2548.     Next 'i
  2549.     If NOT fFailed Then sGuid = "{"&Mid(sDecode,1,8)&"-"& _
  2550.                                 Mid(sDecode,13,4)&"-"& _
  2551.                                 Mid(sDecode,9,4)&"-"& _
  2552.                                 Mid(sDecode,23,2) & Mid(sDecode,21,2)&"-"& _
  2553.                                 Mid(sDecode,19,2) & Mid(sDecode,17,2) & Mid(sDecode,31,2) & Mid(sDecode,29,2) & Mid(sDecode,27,2) & Mid(sDecode,25,2) &"}"
  2554.  
  2555.     GetDecodedGuid = NOT fFailed
  2556.  
  2557. End Function 'GetDecodedGuid
  2558. '=======================================================================================================
  2559.  
  2560. 'Convert a long decimal to hex
  2561. Function DecToHex(lDec)
  2562.    
  2563.     Dim sHex
  2564.     Dim iLen
  2565.     Dim lVal, lExp
  2566.     Dim arrChr
  2567.  
  2568.     arrChr = Array("0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F")
  2569.     sHex = ""
  2570.     lVal = lDec
  2571.     lExp = 16^10
  2572.     While lExp >= 1
  2573.         If lVal >= lExp Then
  2574.             sHex = sHex & arrChr(Int(lVal / lExp))
  2575.             lVal = lVal - lExp * Int(lVal / lExp)
  2576.         Else
  2577.             sHex = sHex & "0"
  2578.             If sHex = "0" Then sHex = ""
  2579.         End If
  2580.         lExp = lExp / 16
  2581.     Wend
  2582.  
  2583.     iLen = 8 - Len(sHex)
  2584.     If iLen > 0 Then sHex = String(iLen,"0") & sHex
  2585.     DecToHex = sHex
  2586. End Function
  2587. '=======================================================================================================
  2588.  
  2589. 'Ensures that only valid metadata entries exist to avoid API failures
  2590. Sub EnsureValidWIMetadata (hDefKey,sKey,iValidLength)
  2591.  
  2592. Dim arrKeys
  2593. Dim SubKey
  2594.  
  2595. If Len(sKey) > 1 Then
  2596.     If Right(sKey,1) = "\" Then sKey = Left(sKey,Len(sKey)-1)
  2597. End If
  2598.  
  2599. If RegEnumKey(hDefKey,sKey,arrKeys) Then
  2600.     For Each SubKey in arrKeys
  2601.         If NOT Len(SubKey) = iValidLength Then
  2602.             RegDeleteKey hDefKey,sKey & "\" & SubKey & "\"
  2603.         End If
  2604.     Next 'SubKey
  2605. End If
  2606.  
  2607. End Sub 'EnsureValidWIMetadata
  2608. '=======================================================================================================
  2609.  
  2610. 'Create a backup copy of the file in the ScrubDir then delete the file
  2611. Sub CopyAndDeleteFile(sFile)
  2612.     Dim File
  2613.    
  2614.     'Error handling inlined
  2615.     On Error Resume Next
  2616.     If oFso.FileExists(sFile) Then
  2617.         Set File = oFso.GetFile(sFile)
  2618.         If Not oFso.FolderExists(sScrubDir & "\" & File.ParentFolder.Name) Then oFso.CreateFolder sScrubDir & "\" & File.ParentFolder.Name
  2619.         If Not fDetectOnly Then
  2620.             LogOnly " - Backing up file: " & sFile
  2621.             oFso.CopyFile sFile,sScrubDir & "\" & File.ParentFolder.Name & "\" & File.Name,True : CheckError "CopyAndDeleteFile"
  2622.             Set File = Nothing
  2623.             DeleteFile(sFile)
  2624.         Else
  2625.             LogOnly " - Simulate CopyAndDelete file: " & sFile
  2626.         End If
  2627.     End If 'oFso.FileExists
  2628. End Sub 'CopyAndDeleteFile
  2629. '=======================================================================================================
  2630.  
  2631. 'Wrapper to delete a file
  2632. Sub DeleteFile(sFile)
  2633.     Dim File
  2634.     Dim sFileName, sNewPath
  2635.    
  2636.     On Error Resume Next
  2637.  
  2638.     If dicKeepFolder.Exists(LCase(sFile)) Then
  2639.         If NOT fForce Then
  2640.             LogOnly " - Disallowing the delete of still required keypath element: " & sFile
  2641.             Exit Sub
  2642.         Else
  2643.             LogOnly " - Enforced delete of still required keypath element: " & sFile
  2644.             LogOnly "   Remaining applications will need a repair!"
  2645.         End If
  2646.     End If
  2647.     If f64 Then
  2648.         If dicKeepFolder.Exists(LCase(Wow64Folder(sFile))) Then
  2649.         If NOT fForce Then
  2650.             LogOnly " - Disallowing the delete of still required keypath element: " & sFile
  2651.             Exit Sub
  2652.         Else
  2653.             LogOnly " - Enforced delete of still required keypath element: " & sFile
  2654.             LogOnly "   Remaining applications will need a repair!"
  2655.         End If
  2656.         End If
  2657.     End If
  2658.  
  2659.     If oFso.FileExists(sFile) Then
  2660.         LogOnly " - Delete file: " & sFile
  2661.         If Not fDetectOnly Then oFso.DeleteFile sFile,True
  2662.         If Err <> 0 Then
  2663.             CheckError "DeleteFile"
  2664.             If fForce Then
  2665.                 'Try to move the file and delete from there
  2666.                 Set File = oFso.GetFile(sFile)
  2667.                 sFileName = File.Name
  2668.                 sNewPath = sScrubDir & "\ScrubTmp"
  2669.                 Set File = Nothing
  2670.                 If Not oFso.FolderExists(sNewPath) Then oFso.CreateFolder(sNewPath)
  2671.                 'Move the file
  2672.                 LogOnly " - Move file to: " & sNewPath & "\" & sFileName
  2673.                 oFso.MoveFile sFile,sNewPath & "\" & sFileName
  2674.                 If Err <> 0 Then
  2675.                     CheckError "DeleteFile (move)"
  2676.                 End If 'Err <> 0
  2677.             Else
  2678.                 fRebootRequired = True
  2679.             End If 'fForce
  2680.         End If 'Err <> 0
  2681.     End If 'oFso.FileExists
  2682. End Sub 'DeleteFile
  2683. '=======================================================================================================
  2684.  
  2685. '64 bit aware wrapper to return the requested folder
  2686. Function GetFolderPath(sPath)
  2687.     GetFolderPath = True
  2688.     If oFso.FolderExists(sPath) Then Exit Function
  2689.     If f64 AND oFso.FolderExists(Wow64Folder(sPath)) Then
  2690.         sPath = Wow64Folder(sPath)
  2691.         Exit Function
  2692.     End If
  2693.     GetFolderPath = False
  2694. End Function 'GetFolderPath
  2695. '=======================================================================================================
  2696.  
  2697. 'Enumerates subfolder names of a folder and returns True if subfolders exist
  2698. Function EnumFolderNames (sFolder, arrSubFolders)
  2699.     Dim Folder, Subfolder
  2700.     Dim sSubFolders
  2701.    
  2702.     If oFso.FolderExists(sFolder) Then
  2703.         Set Folder = oFso.GetFolder(sFolder)
  2704.         For Each Subfolder in Folder.Subfolders
  2705.             sSubFolders = sSubFolders & Subfolder.Name & ","
  2706.         Next 'Subfolder
  2707.     End If
  2708.     If f64 AND oFso.FolderExists(Wow64Folder(sFolder)) Then
  2709.         Set Folder = oFso.GetFolder(Wow64Folder(sFolder))
  2710.         For Each Subfolder in Folder.Subfolders
  2711.             sSubFolders = sSubFolders & Subfolder.Name & ","
  2712.         Next 'Subfolder
  2713.     End If
  2714.     If Len(sSubFolders)>0 Then arrSubFolders = RemoveDuplicates(Split(Left(sSubFolders,Len(sSubFolders)-1),","))
  2715.     EnumFolderNames = Len(sSubFolders)>0
  2716. End Function 'EnumFolderNames
  2717. '=======================================================================================================
  2718.  
  2719. 'Enumerates subfolders of a folder and returns True if subfolders exist
  2720. Function EnumFolders (sFolder, arrSubFolders)
  2721.     Dim Folder, Subfolder
  2722.     Dim sSubFolders
  2723.    
  2724.     If oFso.FolderExists(sFolder) Then
  2725.         Set Folder = oFso.GetFolder(sFolder)
  2726.         For Each Subfolder in Folder.Subfolders
  2727.             sSubFolders = sSubFolders & Subfolder.Path & ","
  2728.         Next 'Subfolder
  2729.     End If
  2730.     If f64 AND oFso.FolderExists(Wow64Folder(sFolder)) Then
  2731.         Set Folder = oFso.GetFolder(Wow64Folder(sFolder))
  2732.         For Each Subfolder in Folder.Subfolders
  2733.             sSubFolders = sSubFolders & Subfolder.Path & ","
  2734.         Next 'Subfolder
  2735.     End If
  2736.     If Len(sSubFolders)>0 Then arrSubFolders = RemoveDuplicates(Split(Left(sSubFolders,Len(sSubFolders)-1),","))
  2737.     EnumFolders = Len(sSubFolders)>0
  2738. End Function 'EnumFolders
  2739. '=======================================================================================================
  2740.  
  2741. Sub GetMseFolderStructure (Folder)
  2742.     Dim SubFolder
  2743.    
  2744.     For Each SubFolder in Folder.SubFolders
  2745.         ReDim Preserve arrMseFolders(UBound(arrMseFolders)+1)
  2746.         arrMseFolders(UBound(arrMseFolders)) = SubFolder.Path
  2747.         GetMseFolderStructure SubFolder
  2748.     Next 'SubFolder
  2749. End Sub 'GetMseFolderStructure
  2750. '=======================================================================================================
  2751.  
  2752. 'Wrapper to delete a folder
  2753. Sub DeleteFolder(sFolder)
  2754.     Dim Folder
  2755.     Dim sDelFolder, sFolderName, sNewPath
  2756.    
  2757.     'Ensure trailing "\"
  2758.     sFolder = sFolder & "\"
  2759.     While InStr(sFolder,"\\")>0
  2760.         sFolder = Replace(sFolder,"\\","\")
  2761.     Wend
  2762.  
  2763.     If dicKeepFolder.Exists(LCase(sFolder)) Then
  2764.         If NOT fForce Then
  2765.             LogOnly " - Disallowing the delete of still required keypath element: " & sFolder
  2766.             Exit Sub
  2767.         Else
  2768.             LogOnly " - Enforced delete of still required keypath element: " & sFolder
  2769.             LogOnly "   Remaining applications will need a repair!"
  2770.         End If
  2771.     End If
  2772.     If f64 Then
  2773.         If dicKeepFolder.Exists(LCase(Wow64Folder(sFolder))) Then
  2774.         If NOT fForce Then
  2775.             LogOnly " - Disallowing the delete of still required keypath element: " & sFolder
  2776.             Exit Sub
  2777.         Else
  2778.             LogOnly " - Enforced delete of still required keypath element: " & sFolder
  2779.             LogOnly "   Remaining applications will need a repair!"
  2780.         End If
  2781.         End If
  2782.     End If
  2783.    
  2784.     'Strip trailing "\"
  2785.     If Len(sFolder) > 1 Then
  2786.         sFolder = Left(sFolder,Len(sFolder)-1)
  2787.     End If
  2788.  
  2789.     On Error Resume Next
  2790.     If oFso.FolderExists(sFolder) Then
  2791.         sDelFolder = sFolder
  2792.     ElseIf f64 AND oFso.FolderExists(Wow64Folder(sFolder)) Then
  2793.         sDelFolder = Wow64Folder(sFolder)
  2794.     Else
  2795.         Exit Sub
  2796.     End If
  2797.     If Not fDetectOnly Then
  2798.         LogOnly " - Delete folder: " & sDelFolder
  2799.         oFso.DeleteFolder sDelFolder,True
  2800.     Else
  2801.         LogOnly " - Simulate delete folder: " & sDelFolder
  2802.     End If
  2803.     If Err <> 0 Then
  2804.         CheckError "DeleteFolder"
  2805.         'Try to move the folder and delete from there
  2806.         Set Folder = oFso.GetFolder(sDelFolder)
  2807.         sFolderName = Folder.Name
  2808.         sNewPath = sScrubDir & "\ScrubTmp"
  2809.         Set Folder = Nothing
  2810.         'Ensure we stay within the same drive
  2811.         If Not oFso.FolderExists(sNewPath) Then oFso.CreateFolder(sNewPath)
  2812.         'Move the folder
  2813.         LogOnly " - Moving folder to: " & sNewPath & "\" & sFolderName
  2814.         oFso.MoveFolder sFolder,sNewPath & "\" & sFolderName
  2815.         If Err <> 0 Then
  2816.             CheckError "DeleteFolder (move)"
  2817.         End If 'Err <> 0
  2818.     End If 'Err <> 0
  2819. End Sub 'DeleteFolder
  2820. '=======================================================================================================
  2821.  
  2822. 'Delete empty folder structures
  2823. Sub DeleteEmptyFolders
  2824.     Dim Folder
  2825.     Dim sFolder
  2826.    
  2827.     ' cosmetic' task don't fail on error
  2828.     On Error Resume Next
  2829.     If Not IsArray(arrDeleteFolders) Then Exit Sub
  2830.     Log vbCrLf & "Empty Folder Cleanup"
  2831.     For Each sFolder in arrDeleteFolders
  2832.         If oFso.FolderExists(sFolder) Then
  2833.             Set Folder = oFso.GetFolder(sFolder)
  2834.             If CBool(Folder.Attributes AND 1024) Then
  2835.                 'exclude protected folder
  2836.             Else
  2837.                 If (Folder.Subfolders.Count = 0) AND (Folder.Files.Count = 0) Then
  2838.                     Set Folder = Nothing
  2839.                     SmartDeleteFolder sFolder
  2840.                 End If
  2841.             End If
  2842.         End If
  2843.     Next 'sFolder
  2844.     CheckError "DeleteEmptyFolders"
  2845.     On Error Goto 0
  2846. End Sub 'DeleteEmptyFolders
  2847. '=======================================================================================================
  2848.  
  2849. 'Wrapper to delete a folder and remove the empty parent folder structure
  2850. Sub SmartDeleteFolder(sFolder)
  2851.     If oFso.FolderExists(sFolder) Then
  2852.         If Not fDetectOnly Then
  2853.             LogOnly "  Request SmartDelete for folder: " & sFolder
  2854.             SmartDeleteFolderEx sFolder
  2855.         Else
  2856.             LogOnly "  Simulate request SmartDelete for folder: " & sFolder
  2857.         End If
  2858.     End If
  2859.     If f64 AND oFso.FolderExists(Wow64Folder(sFolder)) Then
  2860.         If Not fDetectOnly Then
  2861.             LogOnly "Request SmartDelete for folder: " & Wow64Folder(sFolder)
  2862.             SmartDeleteFolderEx Wow64Folder(sFolder)
  2863.         Else
  2864.             LogOnly "Simulate request SmartDelete for folder: " & Wow64Folder(sFolder)
  2865.         End If
  2866.     End If
  2867. End Sub 'SmartDeleteFolder
  2868. '=======================================================================================================
  2869.  
  2870. 'Executes the folder delete operation
  2871. Sub SmartDeleteFolderEx(sFolder)
  2872.     Dim Folder
  2873.    
  2874.     On Error Resume Next
  2875.     DeleteFolder sFolder : CheckError "SmartDeleteFolderEx"
  2876.     On Error Goto 0
  2877.     Set Folder = oFso.GetFolder(oFso.GetParentFolderName(sFolder))
  2878.     If (Folder.Subfolders.Count = 0) AND (Folder.Files.Count = 0) Then SmartDeleteFolderEx(Folder.Path)
  2879. End Sub 'SmartDeleteFolderEx
  2880. '=======================================================================================================
  2881.  
  2882. 'Adds the folder structure to the 'KeepFolder' dictionary
  2883. Sub AddKeepFolder(sPath)
  2884.  
  2885.     Dim Folder
  2886.  
  2887.     'Ensure trailing "\"
  2888.     sPath = LCase(sPath) & "\"
  2889.     While InStr(sPath,"\\")>0
  2890.         sPath = Replace(sPath,"\\","\")
  2891.     Wend
  2892.  
  2893.     If NOT dicKeepFolder.Exists (sPath) Then
  2894.         dicKeepFolder.Add sPath,sPath
  2895.     Else
  2896.         Exit Sub
  2897.     End If
  2898.     sPath = LCase(oFso.GetParentFolderName(sPath)) & "\"
  2899.     If oFso.FolderExists(sPath) Then AddKeepFolder(sPath)
  2900. End Sub
  2901. '=======================================================================================================
  2902.  
  2903. 'Handles additional folder-path operations on 64 bit environments
  2904. Function Wow64Folder(sFolder)
  2905.     If LCase(Left(sFolder,Len(sWinDir & "\System32"))) = LCase(sWinDir & "\System32") Then
  2906.         Wow64Folder = sWinDir & "\syswow64" & Right(sFolder,Len(sFolder)-Len(sSys32Dir))
  2907.     ElseIf LCase(Left(sFolder,Len(sProgramFiles))) = LCase(sProgramFiles) Then
  2908.         Wow64Folder = sProgramFilesX86 & Right(sFolder,Len(sFolder)-Len(sProgramFiles))
  2909.     Else
  2910.         Wow64Folder = "?" 'Return invalid string to ensure the folder cannot exist
  2911.     End If
  2912. End Function 'Wow64Folder
  2913. '=======================================================================================================
  2914.  
  2915. Function HiveString(hDefKey)
  2916.     On Error Resume Next
  2917.     Select Case hDefKey
  2918.         Case HKCR : HiveString = "HKEY_CLASSES_ROOT"
  2919.         Case HKCU : HiveString = "HKEY_CURRENT_USER"
  2920.         Case HKLM : HiveString = "HKEY_LOCAL_MACHINE"
  2921.         Case HKU  : HiveString = "HKEY_USERS"
  2922.         Case Else : HiveString = hDefKey
  2923.     End Select
  2924. End Function
  2925. '=======================================================================================================
  2926.  
  2927. Function RegKeyExists(hDefKey,sSubKeyName)
  2928.     Dim arrKeys
  2929.     RegKeyExists = False
  2930.     If oReg.EnumKey(hDefKey,sSubKeyName,arrKeys) = 0 Then RegKeyExists = True
  2931. End Function
  2932. '=======================================================================================================
  2933.  
  2934. Function RegValExists(hDefKey,sSubKeyName,sName)
  2935.     Dim arrValueTypes, arrValueNames
  2936.     Dim i
  2937.  
  2938.     RegValExists = False
  2939.     If Not RegKeyExists(hDefKey,sSubKeyName) Then Exit Function
  2940.     If oReg.EnumValues(hDefKey,sSubKeyName,arrValueNames,arrValueTypes) = 0 AND IsArray(arrValueNames) Then
  2941.         For i = 0 To UBound(arrValueNames)
  2942.             If LCase(arrValueNames(i)) = Trim(LCase(sName)) Then RegValExists = True
  2943.         Next
  2944.     End If 'oReg.EnumValues
  2945. End Function
  2946. '=======================================================================================================
  2947.  
  2948. 'Read the value of a given registry entry
  2949. Function RegReadValue(hDefKey, sSubKeyName, sName, sValue, sType)
  2950.     Dim RetVal
  2951.     Dim Item
  2952.     Dim arrValues
  2953.    
  2954.     Select Case UCase(sType)
  2955.         Case "1","REG_SZ"
  2956.             RetVal = oReg.GetStringValue(hDefKey,sSubKeyName,sName,sValue)
  2957.             If Not RetVal = 0 AND f64 Then RetVal = oReg.GetStringValue(hDefKey,Wow64Key(hDefKey, sSubKeyName),sName,sValue)
  2958.        
  2959.         Case "2","REG_EXPAND_SZ"
  2960.             RetVal = oReg.GetExpandedStringValue(hDefKey,sSubKeyName,sName,sValue)
  2961.             If Not RetVal = 0 AND f64 Then RetVal = oReg.GetExpandedStringValue(hDefKey,Wow64Key(hDefKey, sSubKeyName),sName,sValue)
  2962.        
  2963.         Case "7","REG_MULTI_SZ"
  2964.             RetVal = oReg.GetMultiStringValue(hDefKey,sSubKeyName,sName,arrValues)
  2965.             If Not RetVal = 0 AND f64 Then RetVal = oReg.GetMultiStringValue(hDefKey,Wow64Key(hDefKey, sSubKeyName),sName,arrValues)
  2966.             If RetVal = 0 Then sValue = Join(arrValues,chr(34))
  2967.        
  2968.         Case "4","REG_DWORD"
  2969.             RetVal = oReg.GetDWORDValue(hDefKey,sSubKeyName,sName,sValue)
  2970.             If Not RetVal = 0 AND f64 Then
  2971.                 RetVal = oReg.GetDWORDValue(hDefKey,Wow64Key(hDefKey, sSubKeyName),sName,sValue)
  2972.             End If
  2973.        
  2974.         Case "3","REG_BINARY"
  2975.             RetVal = oReg.GetBinaryValue(hDefKey,sSubKeyName,sName,sValue)
  2976.             If Not RetVal = 0 AND f64 Then RetVal = oReg.GetBinaryValue(hDefKey,Wow64Key(hDefKey, sSubKeyName),sName,sValue)
  2977.        
  2978.         Case "11","REG_QWORD"
  2979.             RetVal = oReg.GetQWORDValue(hDefKey,sSubKeyName,sName,sValue)
  2980.             If Not RetVal = 0 AND f64 Then RetVal = oReg.GetQWORDValue(hDefKey,Wow64Key(hDefKey, sSubKeyName),sName,sValue)
  2981.        
  2982.         Case Else
  2983.             RetVal = -1
  2984.     End Select 'sValue
  2985.    
  2986.     RegReadValue = (RetVal = 0)
  2987. End Function 'RegReadValue
  2988. '=======================================================================================================
  2989.  
  2990. 'Enumerate a registry key to return all values
  2991. Function RegEnumValues(hDefKey,sSubKeyName,arrNames, arrTypes)
  2992.     Dim RetVal, RetVal64
  2993.     Dim arrNames32, arrNames64, arrTypes32, arrTypes64
  2994.    
  2995.     If f64 Then
  2996.         RetVal = oReg.EnumValues(hDefKey,sSubKeyName,arrNames32,arrTypes32)
  2997.         RetVal64 = oReg.EnumValues(hDefKey,Wow64Key(hDefKey, sSubKeyName),arrNames64,arrTypes64)
  2998.         If (RetVal = 0) AND (Not RetVal64 = 0) AND IsArray(arrNames32) AND IsArray(arrTypes32) Then
  2999.             arrNames = arrNames32
  3000.             arrTypes = arrTypes32
  3001.         End If
  3002.         If (Not RetVal = 0) AND (RetVal64 = 0) AND IsArray(arrNames64) AND IsArray(arrTypes64) Then
  3003.             arrNames = arrNames64
  3004.             arrTypes = arrTypes64
  3005.         End If
  3006.         If (RetVal = 0) AND (RetVal64 = 0) AND IsArray(arrNames32) AND IsArray(arrNames64) AND IsArray(arrTypes32) AND IsArray(arrTypes64) Then
  3007.             arrNames = RemoveDuplicates(Split((Join(arrNames32,"\") & "\" & Join(arrNames64,"\")),"\"))
  3008.             arrTypes = RemoveDuplicates(Split((Join(arrTypes32,"\") & "\" & Join(arrTypes64,"\")),"\"))
  3009.         End If
  3010.     Else
  3011.         RetVal = oReg.EnumValues(hDefKey,sSubKeyName,arrNames,arrTypes)
  3012.     End If 'f64
  3013.     RegEnumValues = ((RetVal = 0) OR (RetVal64 = 0)) AND IsArray(arrNames) AND IsArray(arrTypes)
  3014. End Function 'RegEnumValues
  3015. '=======================================================================================================
  3016.  
  3017. 'Enumerate a registry key to return all subkeys
  3018. Function RegEnumKey(hDefKey,sSubKeyName,arrKeys)
  3019.     Dim RetVal, RetVal64
  3020.     Dim arrKeys32, arrKeys64
  3021.    
  3022.     If f64 Then
  3023.         RetVal = oReg.EnumKey(hDefKey,sSubKeyName,arrKeys32)
  3024.         RetVal64 = oReg.EnumKey(hDefKey,Wow64Key(hDefKey, sSubKeyName),arrKeys64)
  3025.         If (RetVal = 0) AND (Not RetVal64 = 0) AND IsArray(arrKeys32) Then arrKeys = arrKeys32
  3026.         If (Not RetVal = 0) AND (RetVal64 = 0) AND IsArray(arrKeys64) Then arrKeys = arrKeys64
  3027.         If (RetVal = 0) AND (RetVal64 = 0) Then
  3028.             If IsArray(arrKeys32) AND IsArray (arrKeys64) Then
  3029.                 arrKeys = RemoveDuplicates(Split((Join(arrKeys32,"\") & "\" & Join(arrKeys64,"\")),"\"))
  3030.             ElseIf IsArray(arrKeys64) Then
  3031.                 arrKeys = arrKeys64
  3032.             Else
  3033.                 arrKeys = arrKeys32
  3034.             End If
  3035.         End If
  3036.     Else
  3037.         RetVal = oReg.EnumKey(hDefKey,sSubKeyName,arrKeys)
  3038.     End If 'f64
  3039.     RegEnumKey = ((RetVal = 0) OR (RetVal64 = 0)) AND IsArray(arrKeys)
  3040. End Function 'RegEnumKey
  3041. '=======================================================================================================
  3042.  
  3043. 'Wrapper around oReg.DeleteValue to handle 64 bit
  3044. Sub RegDeleteValue(hDefKey, sSubKeyName, sName, fRegMultiSZ)
  3045.     Dim sWow64Key, sValue
  3046.     Dim iRetVal
  3047.    
  3048.     If dicKeepReg.Exists(LCase(sSubKeyName & sName)) Then
  3049.         If NOT fForce Then
  3050.             LogOnly " - Disallowing the delete of still required keypath element: " & HiveString(hDefKey) & "\" & sSubKeyName & sName
  3051.             Exit Sub
  3052.         Else
  3053.             LogOnly " - Enforced delete of still required keypath element. Remaining applications will need a repair!"
  3054.         End If
  3055.     End If
  3056.     If f64 Then
  3057.         If dicKeepReg.Exists(LCase(Wow64Key(hDefKey, sSubKeyName) & sName)) Then
  3058.             If NOT fForce Then
  3059.                 LogOnly " - Disallowing the delete of still required keypath element: " & HiveString(hDefKey) & "\" & sSubKeyName & sName
  3060.                 Exit Sub
  3061.             Else
  3062.                 LogOnly " - Enforced delete of still required keypath element. Remaining applications will need a repair!"
  3063.             End If
  3064.         End If
  3065.     End If
  3066.  
  3067.     If RegValExists(hDefKey,sSubKeyName,sName) Then
  3068.         On Error Resume Next
  3069.         If RegReadValue(hDefKey,sSubKeyName,sName,sValue,"REG_MULTI_SZ") Then
  3070.             LogOnly " - Disallowing unsafe delete of REG_MULTI_SZ: " & HiveString(hDefKey) & "\" & sSubKeyName & sName
  3071.             Exit Sub
  3072.         End If
  3073.         If Not fDetectOnly Then
  3074.             LogOnly " - Delete registry value: " & HiveString(hDefKey) & "\" & sSubKeyName & " -> " & sName
  3075.             iRetVal = 0
  3076.             iRetVal = oReg.DeleteValue(hDefKey, sSubKeyName, sName)
  3077.             CheckError "RegDeleteValue"
  3078.             If NOT (iRetVal=0) Then LogOnly "     Delete failed. Return value: "&iRetVal
  3079.         Else
  3080.             LogOnly " - Simulate delete registry value: " & HiveString(hDefKey) & "\" & sSubKeyName & " -> " & sName
  3081.         End If
  3082.         On Error Goto 0
  3083.     End If 'RegValExists
  3084.     If f64 Then
  3085.         sWow64Key = Wow64Key(hDefKey, sSubKeyName)
  3086.         If RegValExists(hDefKey,sWow64Key,sName) Then
  3087.             On Error Resume Next
  3088.             If RegReadValue(hDefKey,sSubKeyName,sName,sValue,"REG_MULTI_SZ") Then
  3089.                 LogOnly " - Disallowing unsafe delete of REG_MULTI_SZ: " & HiveString(hDefKey) & "\" & sSubKeyName & sName
  3090.                 Exit Sub
  3091.             End If
  3092.             If Not fDetectOnly Then
  3093.             LogOnly " - Delete registry value: " & HiveString(hDefKey) & "\" & sWow64Key & " -> " & sName
  3094.                 iRetVal = 0
  3095.                 iRetVal = oReg.DeleteValue(hDefKey, sWow64Key, sName)
  3096.                 CheckError "RegDeleteValue"
  3097.                 If NOT (iRetVal=0) Then LogOnly "     Delete failed. Return value: "&iRetVal
  3098.             Else
  3099.                 LogOnly " - Simulate delete registry value: " & HiveString(hDefKey) & "\" & sWow64Key & " -> " & sName
  3100.             End If
  3101.             On Error Goto 0
  3102.         End If 'RegKeyExists
  3103.     End If
  3104. End Sub 'RegDeleteValue
  3105. '=======================================================================================================
  3106.  
  3107. 'Wrappper around RegDeleteKeyEx to handle 64bit scenrios
  3108. Sub RegDeleteKey(hDefKey, sSubKeyName)
  3109.     Dim sWow64Key
  3110.    
  3111.     'Ensure trailing "\"
  3112.     sSubKeyName = sSubKeyName & "\"
  3113.     While InStr(sSubKeyName,"\\")>0
  3114.         sSubKeyName = Replace(sSubKeyName,"\\","\")
  3115.     Wend
  3116.  
  3117.     If dicKeepReg.Exists(LCase(sSubKeyName)) Then
  3118.         If NOT fForce Then
  3119.             LogOnly " - Disallowing the delete of still required keypath element: " & HiveString(hDefKey) & "\" & sSubKeyName
  3120.             Exit Sub
  3121.         Else
  3122.             LogOnly " - Enforced delete of still required keypath element. Remaining applications will need a repair!"
  3123.         End If
  3124.     End If
  3125.     If f64 Then
  3126.         If dicKeepReg.Exists(LCase(Wow64Key(hDefKey, sSubKeyName))) Then
  3127.             If NOT fForce Then
  3128.                 LogOnly " - Disallowing the delete of still required keypath element: " & HiveString(hDefKey) & "\" & sSubKeyName
  3129.                 Exit Sub
  3130.             Else
  3131.                 LogOnly " - Enforced delete of still required keypath element. Remaining applications will need a repair!"
  3132.             End If
  3133.         End If
  3134.     End If
  3135.    
  3136.     If Len(sSubKeyName) > 1 Then
  3137.         'Strip of trailing "\"
  3138.         sSubKeyName = Left(sSubKeyName,Len(sSubKeyName)-1)
  3139.     End If
  3140.    
  3141.     If RegKeyExists(hDefKey, sSubKeyName) Then
  3142.         If Not fDetectOnly Then
  3143.             LogOnly " - Delete registry key: " & HiveString(hDefKey) & "\" & sSubKeyName
  3144.             On Error Resume Next
  3145.             RegDeleteKeyEx hDefKey, sSubKeyName
  3146.             On Error Goto 0
  3147.         Else
  3148.             LogOnly " - Simulate delete registry key: " & HiveString(hDefKey) & "\" & sSubKeyName
  3149.         End If
  3150.     End If 'RegKeyExists
  3151.     If f64 Then
  3152.         sWow64Key = Wow64Key(hDefKey, sSubKeyName)
  3153.         If RegKeyExists(hDefKey,sWow64Key) Then
  3154.             If Not fDetectOnly Then
  3155.                 LogOnly " - Delete registry key: " & HiveString(hDefKey) & "\" & sWow64Key
  3156.                 On Error Resume Next
  3157.                 RegDeleteKeyEx hDefKey, sWow64Key
  3158.                 On Error Goto 0
  3159.             Else
  3160.                 LogOnly " - Simulate delete registry key: " & HiveString(hDefKey) & "\" & sWow64Key
  3161.             End If
  3162.         End If 'RegKeyExists
  3163.     End If
  3164. End Sub 'RegDeleteKey
  3165. '=======================================================================================================
  3166.  
  3167. 'Recursively delete a registry structure
  3168. Sub RegDeleteKeyEx(hDefKey, sSubKeyName)
  3169.     Dim arrSubkeys
  3170.     Dim sSubkey
  3171.     Dim iRetVal
  3172.  
  3173.     On Error Resume Next
  3174.     oReg.EnumKey hDefKey, sSubKeyName, arrSubkeys
  3175.     If IsArray(arrSubkeys) Then
  3176.         For Each sSubkey In arrSubkeys
  3177.             RegDeleteKeyEx hDefKey, sSubKeyName & "\" & sSubkey
  3178.         Next
  3179.     End If
  3180.     If Not fDetectOnly Then
  3181.         iRetVal = 0
  3182.         iRetVal = oReg.DeleteKey(hDefKey,sSubKeyName)
  3183.         If NOT (iRetVal=0) Then LogOnly "     Delete failed. Return value: "&iRetVal
  3184.     End If
  3185. End Sub 'RegDeleteKeyEx
  3186. '=======================================================================================================
  3187.  
  3188. 'Return the alternate regkey location on 64bit environment
  3189. Function Wow64Key(hDefKey, sSubKeyName)
  3190.     Dim iPos
  3191.  
  3192.     Select Case hDefKey
  3193.         Case HKCU
  3194.             If Left(sSubKeyName,17) = "Software\Classes\" Then
  3195.                 Wow64Key = Left(sSubKeyName,17) & "Wow6432Node\" & Right(sSubKeyName,Len(sSubKeyName)-17)
  3196.             Else
  3197.                 iPos = InStr(sSubKeyName,"\")
  3198.                 Wow64Key = Left(sSubKeyName,iPos) & "Wow6432Node\" & Right(sSubKeyName,Len(sSubKeyName)-iPos)
  3199.             End If
  3200.        
  3201.         Case HKLM
  3202.             If Left(sSubKeyName,17) = "Software\Classes\" Then
  3203.                 Wow64Key = Left(sSubKeyName,17) & "Wow6432Node\" & Right(sSubKeyName,Len(sSubKeyName)-17)
  3204.             Else
  3205.                 iPos = InStr(sSubKeyName,"\")
  3206.                 Wow64Key = Left(sSubKeyName,iPos) & "Wow6432Node\" & Right(sSubKeyName,Len(sSubKeyName)-iPos)
  3207.             End If
  3208.        
  3209.         Case Else
  3210.             Wow64Key = "Wow6432Node\" & sSubKeyName
  3211.        
  3212.     End Select 'hDefKey
  3213. End Function 'Wow64Key
  3214. '=======================================================================================================
  3215.  
  3216. 'Remove duplicate entries from a one dimensional array
  3217. Function RemoveDuplicates(Array)
  3218.     Dim Item
  3219.     Dim oDic
  3220.    
  3221.     Set oDic = CreateObject("Scripting.Dictionary")
  3222.     For Each Item in Array
  3223.         If Not oDic.Exists(Item) Then oDic.Add Item,Item
  3224.     Next 'Item
  3225.     RemoveDuplicates = oDic.Keys
  3226. End Function 'RemoveDuplicates
  3227. '=======================================================================================================
  3228.  
  3229. 'End running instances of setup
  3230. Sub EndCurrentInstalls ()
  3231.     Dim Processes, Process
  3232.     Dim iRet
  3233.  
  3234.     Set Processes = oWmiLocal.ExecQuery("Select * From Win32_Process Where Name like '%setup%' OR Name like '%install%'")
  3235.     For Each Process in Processes
  3236.         Log " - End process " & Process.Name
  3237.         iRet = Process.Terminate()
  3238.         CheckError "EndCurrentInstalls: " & "Process.Name"
  3239.     Next 'Process
  3240.     StopService "msiserver"
  3241. End Sub 'EndCurrentInstalls
  3242. '=======================================================================================================
  3243.  
  3244. 'Uses WMI to stop a service
  3245. Function StopService(sService)
  3246.     Dim Services, Service
  3247.     Dim sQuery
  3248.     Dim iRet
  3249.  
  3250.     On Error Resume Next
  3251.    
  3252.     iRet = 0
  3253.     sQuery = "Select * From Win32_Service Where Name='" & sService & "'"
  3254.     Set Services = oWmiLocal.Execquery(sQuery)
  3255.     'Stop the service
  3256.     For Each Service in Services
  3257.         If UCase(Service.State) = "STARTED" Then iRet = Service.StopService
  3258.         If UCase(Service.State) = "RUNNING" Then iRet = Service.StopService
  3259.  
  3260.     Next 'Service
  3261.     StopService = (iRet = 0)
  3262. End Function 'StopService
  3263. '=======================================================================================================
  3264.  
  3265. 'Delete a service
  3266. Sub DeleteService(sService)
  3267.     Dim Services, Service, Processes, Process
  3268.     Dim sQuery, sStates
  3269.     Dim iRet
  3270.    
  3271.     On Error Resume Next
  3272.    
  3273.     sStates = "STARTED;RUNNING"
  3274.     sQuery = "Select * From Win32_Service Where Name='" & sService & "'"
  3275.     Set Services = oWmiLocal.Execquery(sQuery)
  3276.    
  3277.     'Stop and delete the service
  3278.     For Each Service in Services
  3279.         Log " Found service " & sService & " in state " & Service.State
  3280.         If InStr(sStates,UCase(Service.State))>0 Then iRet = Service.StopService()
  3281.         'Ensure no more instances of the service are running
  3282.         Set Processes = oWmiLocal.ExecQuery("Select * From Win32_Process Where Name='" & sService & ".exe'")
  3283.         For Each Process in Processes
  3284.             iRet = Process.Terminate()
  3285.         Next 'Process
  3286.         If Not fDetectOnly Then
  3287.             Log " - Deleting Service -> " & sService
  3288.             iRet = Service.Delete()
  3289.         Else
  3290.             Log " - Simulate deleting Service -> " & sService
  3291.         End If
  3292.     Next 'Service
  3293.     Set Services = Nothing
  3294.     Err.Clear
  3295.  
  3296. End Sub 'DeleteService
  3297. '=======================================================================================================
  3298.  
  3299. 'Translation for setup.exe error codes
  3300. Function SetupRetVal(RetVal)
  3301.     Select Case RetVal
  3302.         Case 0 : SetupRetVal = "Success"
  3303.         Case 30001,1 : SetupRetVal = "AbstractMethod"
  3304.         Case 30002,2 : SetupRetVal = "ApiProhibited"
  3305.         Case 30003,3  : SetupRetVal = "AlreadyImpersonatingAUser"
  3306.         Case 30004,4 : SetupRetVal = "AlreadyInitialized"
  3307.         Case 30005,5 : SetupRetVal = "ArgumentNullException"
  3308.         Case 30006,6 : SetupRetVal = "AssertionFailed"
  3309.         Case 30007,7 : SetupRetVal = "CABFileAddFailed"
  3310.         Case 30008,8 : SetupRetVal = "CommandFailed"
  3311.         Case 30009,9 : SetupRetVal = "ConcatenationFailed"
  3312.         Case 30010,10 : SetupRetVal = "CopyFailed"
  3313.         Case 30011,11 : SetupRetVal = "CreateEventFailed"
  3314.         Case 30012,12 : SetupRetVal = "CustomizationPatchNotFound"
  3315.         Case 30013,13 : SetupRetVal = "CustomizationPatchNotApplicable"
  3316.         Case 30014,14 : SetupRetVal = "DuplicateDefinition"
  3317.         Case 30015,15 : SetupRetVal = "ErrorCodeOnly - Passthrough for Win32 error"
  3318.         Case 30016,16 : SetupRetVal = "ExceptionNotThrown"
  3319.         Case 30017,17 : SetupRetVal = "FailedToImpersonateUser"
  3320.         Case 30018,18 : SetupRetVal = "FailedToInitializeFlexDataSource"
  3321.         Case 30019,19 : SetupRetVal = "FailedToStartClassFactories"
  3322.         Case 30020,20 : SetupRetVal = "FileNotFound"
  3323.         Case 30021,21 : SetupRetVal = "FileNotOpen"
  3324.         Case 30022,22 : SetupRetVal = "FlexDialogAlreadyInitialized"
  3325.         Case 30023,23 : SetupRetVal = "HResultOnly - Passthrough for HRESULT errors"
  3326.         Case 30024,24 : SetupRetVal = "HWNDNotFound"
  3327.         Case 30025,25 : SetupRetVal = "IncompatibleCacheAction"
  3328.         Case 30026,26 : SetupRetVal = "IncompleteProductAddOns"
  3329.         Case 30027,27 : SetupRetVal = "InstalledProductStateCorrupt"
  3330.         Case 30028,28 : SetupRetVal = "InsufficientBuffer"
  3331.         Case 30029,29 : SetupRetVal = "InvalidArgument"
  3332.         Case 30030,30 : SetupRetVal = "InvalidCDKey"
  3333.         Case 30031,31 : SetupRetVal = "InvalidColumnType"
  3334.         Case 30032,31 : SetupRetVal = "InvalidConfigAddLanguage"
  3335.         Case 30033,33 : SetupRetVal = "InvalidData"
  3336.         Case 30034,34 : SetupRetVal = "InvalidDirectory"
  3337.         Case 30035,35 : SetupRetVal = "InvalidFormat"
  3338.         Case 30036,36 : SetupRetVal = "InvalidInitialization"
  3339.         Case 30037,37 : SetupRetVal = "InvalidMethod"
  3340.         Case 30038,38 : SetupRetVal = "InvalidOperation"
  3341.         Case 30039,39 : SetupRetVal = "InvalidParameter"
  3342.         Case 30040,40 : SetupRetVal = "InvalidProductFromARP"
  3343.         Case 30041,41 : SetupRetVal = "InvalidProductInConfigXml"
  3344.         Case 30042,42 : SetupRetVal = "InvalidReference"
  3345.         Case 30043,43 : SetupRetVal = "InvalidRegistryValueType"
  3346.         Case 30044,44 : SetupRetVal = "InvalidXMLProperty"
  3347.         Case 30045,45 : SetupRetVal = "InvalidMetadataFile"
  3348.         Case 30046,46 : SetupRetVal = "LogNotInitialized"
  3349.         Case 30047,47 : SetupRetVal = "LogAlreadyInitialized"
  3350.         Case 30048,48 : SetupRetVal = "MissingXMLNode"
  3351.         Case 30049,49 : SetupRetVal = "MsiTableNotFound"
  3352.         Case 30050,50 : SetupRetVal = "MsiAPICallFailure"
  3353.         Case 30051,51 : SetupRetVal = "NodeNotOfTypeElement"
  3354.         Case 30052,52 : SetupRetVal = "NoMoreGraceBoots"
  3355.         Case 30053,53 : SetupRetVal = "NoProductsFound"
  3356.         Case 30054,54 : SetupRetVal = "NoSupportedCulture"
  3357.         Case 30055,55 : SetupRetVal = "NotYetImplemented"
  3358.         Case 30056,56 : SetupRetVal = "NotAvailableCulture"
  3359.         Case 30057,57 : SetupRetVal = "NotCustomizationPatch"
  3360.         Case 30058,58 : SetupRetVal = "NullReference"
  3361.         Case 30059,59 : SetupRetVal = "OCTPatchForbidden"
  3362.         Case 30060,60 : SetupRetVal = "OCTWrongMSIDll"
  3363.         Case 30061,61 : SetupRetVal = "OutOfBoundsIndex"
  3364.         Case 30062,62 : SetupRetVal = "OutOfDiskSpace"
  3365.         Case 30063,63 : SetupRetVal = "OutOfMemory"
  3366.         Case 30064,64 : SetupRetVal = "OutOfRange"
  3367.         Case 30065,65 : SetupRetVal = "PatchApplicationFailure"
  3368.         Case 30066,66 : SetupRetVal = "PreReqCheckFailure"
  3369.         Case 30067,67 : SetupRetVal = "ProcessAlreadyStarted"
  3370.         Case 30068,68 : SetupRetVal = "ProcessNotStarted"
  3371.         Case 30069,69 : SetupRetVal = "ProcessNotFinished"
  3372.         Case 30070,70 : SetupRetVal = "ProductAlreadyDefined"
  3373.         Case 30071,71 : SetupRetVal = "ResourceAlreadyTracked"
  3374.         Case 30072,72 : SetupRetVal = "ResourceNotFound"
  3375.         Case 30073,73 : SetupRetVal = "ResourceNotTracked"
  3376.         Case 30074,74 : SetupRetVal = "SQLAlreadyConnected"
  3377.         Case 30075,75 : SetupRetVal = "SQLFailedToAllocateHandle"
  3378.         Case 30076,76 : SetupRetVal = "SQLFailedToConnect"
  3379.         Case 30077,77 : SetupRetVal = "SQLFailedToExecuteStatement"
  3380.         Case 30078,78 : SetupRetVal = "SQLFailedToRetrieveData"
  3381.         Case 30079,79 : SetupRetVal = "SQLFailedToSetAttribute"
  3382.         Case 30080,80 : SetupRetVal = "StorageNotCreated"
  3383.         Case 30081,81 : SetupRetVal = "StreamNameTooLong"
  3384.         Case 30082,82 : SetupRetVal = "SystemError"
  3385.         Case 30083,83 : SetupRetVal = "ThreadAlreadyStarted"
  3386.         Case 30084,84 : SetupRetVal = "ThreadNotStarted"
  3387.         Case 30085,85 : SetupRetVal = "ThreadNotFinished"
  3388.         Case 30086,86 : SetupRetVal = "TooManyProducts"
  3389.         Case 30087,87 : SetupRetVal = "UnexpectedXMLNodeType"
  3390.         Case 30088,88 : SetupRetVal = "UnexpectedError"
  3391.         Case 30089,89 : SetupRetVal = "Unitialized"
  3392.         Case 30090,90 : SetupRetVal = "UserCancel"
  3393.         Case 30091,91 : SetupRetVal = "ExternalCommandFailed"
  3394.         Case 30092,92 : SetupRetVal = "SPDatabaseOverSize"
  3395.         Case 30093,93 : SetupRetVal = "IntegerTruncation"
  3396.         'msiexec return values
  3397.         Case 1259 : SetupRetVal = "APPHELP_BLOCK"
  3398.         Case 1601 : SetupRetVal = "INSTALL_SERVICE_FAILURE"
  3399.         Case 1602 : SetupRetVal = "INSTALL_USEREXIT"
  3400.         Case 1603 : SetupRetVal = "INSTALL_FAILURE"
  3401.         Case 1604 : SetupRetVal = "INSTALL_SUSPEND"
  3402.         Case 1605 : SetupRetVal = "UNKNOWN_PRODUCT"
  3403.         Case 1606 : SetupRetVal = "UNKNOWN_FEATURE"
  3404.         Case 1607 : SetupRetVal = "UNKNOWN_COMPONENT"
  3405.         Case 1608 : SetupRetVal = "UNKNOWN_PROPERTY"
  3406.         Case 1609 : SetupRetVal = "INVALID_HANDLE_STATE"
  3407.         Case 1610 : SetupRetVal = "BAD_CONFIGURATION"
  3408.         Case 1611 : SetupRetVal = "INDEX_ABSENT"
  3409.         Case 1612 : SetupRetVal = "INSTALL_SOURCE_ABSENT"
  3410.         Case 1613 : SetupRetVal = "INSTALL_PACKAGE_VERSION"
  3411.         Case 1614 : SetupRetVal = "PRODUCT_UNINSTALLED"
  3412.         Case 1615 : SetupRetVal = "BAD_QUERY_SYNTAX"
  3413.         Case 1616 : SetupRetVal = "INVALID_FIELD"
  3414.         Case 1618 : SetupRetVal = "INSTALL_ALREADY_RUNNING"
  3415.         Case 1619 : SetupRetVal = "INSTALL_PACKAGE_OPEN_FAILED"
  3416.         Case 1620 : SetupRetVal = "INSTALL_PACKAGE_INVALID"
  3417.         Case 1621 : SetupRetVal = "INSTALL_UI_FAILURE"
  3418.         Case 1622 : SetupRetVal = "INSTALL_LOG_FAILURE"
  3419.         Case 1623 : SetupRetVal = "INSTALL_LANGUAGE_UNSUPPORTED"
  3420.         Case 1624 : SetupRetVal = "INSTALL_TRANSFORM_FAILURE"
  3421.         Case 1625 : SetupRetVal = "INSTALL_PACKAGE_REJECTED"
  3422.         Case 1626 : SetupRetVal = "FUNCTION_NOT_CALLED"
  3423.         Case 1627 : SetupRetVal = "FUNCTION_FAILED"
  3424.         Case 1628 : SetupRetVal = "INVALID_TABLE"
  3425.         Case 1629 : SetupRetVal = "DATATYPE_MISMATCH"
  3426.         Case 1630 : SetupRetVal = "UNSUPPORTED_TYPE"
  3427.         Case 1631 : SetupRetVal = "CREATE_FAILED"
  3428.         Case 1632 : SetupRetVal = "INSTALL_TEMP_UNWRITABLE"
  3429.         Case 1633 : SetupRetVal = "INSTALL_PLATFORM_UNSUPPORTED"
  3430.         Case 1634 : SetupRetVal = "INSTALL_NOTUSED"
  3431.         Case 1635 : SetupRetVal = "PATCH_PACKAGE_OPEN_FAILED"
  3432.         Case 1636 : SetupRetVal = "PATCH_PACKAGE_INVALID"
  3433.         Case 1637 : SetupRetVal = "PATCH_PACKAGE_UNSUPPORTED"
  3434.         Case 1638 : SetupRetVal = "PRODUCT_VERSION"
  3435.         Case 1639 : SetupRetVal = "INVALID_COMMAND_LINE"
  3436.         Case 1640 : SetupRetVal = "INSTALL_REMOTE_DISALLOWED"
  3437.         Case 1641 : SetupRetVal = "SUCCESS_REBOOT_INITIATED"
  3438.         Case 1642 : SetupRetVal = "PATCH_TARGET_NOT_FOUND"
  3439.         Case 1643 : SetupRetVal = "PATCH_PACKAGE_REJECTED"
  3440.         Case 1644 : SetupRetVal = "INSTALL_TRANSFORM_REJECTED"
  3441.         Case 1645 : SetupRetVal = "INSTALL_REMOTE_PROHIBITED"
  3442.         Case 1646 : SetupRetVal = "PATCH_REMOVAL_UNSUPPORTED"
  3443.         Case 1647 : SetupRetVal = "UNKNOWN_PATCH"
  3444.         Case 1648 : SetupRetVal = "PATCH_NO_SEQUENCE"
  3445.         Case 1649 : SetupRetVal = "PATCH_REMOVAL_DISALLOWED"
  3446.         Case 1650 : SetupRetVal = "INVALID_PATCH_XML"
  3447.         Case 3010 : SetupRetVal = "SUCCESS_REBOOT_REQUIRED"
  3448.         Case Else : SetupRetVal = "Unknown Return Value"
  3449.     End Select
  3450. End Function 'SetupRetVal
  3451. '=======================================================================================================
  3452.  
  3453. Function GetProductID(sProdID)
  3454.         Dim sReturn
  3455.        
  3456.         Select Case sProdId
  3457.        
  3458.         Case "000F" : sReturn = "MONDO"
  3459.         Case "0010" : sReturn = "WEBFLDRS"
  3460.         Case "0011" : sReturn = "PROPLUS"
  3461.         Case "0012" : sReturn = "STANDARD"
  3462.         Case "0013" : sReturn = "BASIC"
  3463.         Case "0014" : sReturn = "PRO"
  3464.         Case "0015" : sReturn = "ACCESS"
  3465.         Case "0016" : sReturn = "EXCEL"
  3466.         Case "0017" : sReturn = "SharePointDesigner"
  3467.         Case "0018" : sReturn = "PowerPoint"
  3468.         Case "0019" : sReturn = "Publisher"
  3469.         Case "001A" : sReturn = "Outlook"
  3470.         Case "001B" : sReturn = "Word"
  3471.         Case "001C" : sReturn = "AccessRuntime"
  3472.         Case "001F" : sReturn = "Proof"
  3473.         Case "0020" : sReturn = "O2007CNV"
  3474.         Case "0021" : sReturn = "VisualWebDeveloper"
  3475.         Case "0026" : sReturn = "ExpressionWeb"
  3476.         Case "0029" : sReturn = "Excel"
  3477.         Case "002A" : sReturn = "Office64"
  3478.         Case "002B" : sReturn = "Word"
  3479.         Case "002C" : sReturn = "Proofing"
  3480.         Case "002E" : sReturn = "Ultimate"
  3481.         Case "002F" : sReturn = "HomeAndStudent"
  3482.         Case "0028" : sReturn = "IME"
  3483.         Case "0030" : sReturn = "Enterprise"
  3484.         Case "0031" : sReturn = "ProfessionalHybrid"
  3485.         Case "0033" : sReturn = "Personal"
  3486.         Case "0035" : sReturn = "ProfessionalHybrid"
  3487.         Case "0037" : sReturn = "PowerPoint"
  3488.         Case "003A" : sReturn = "PrjStd"
  3489.         Case "003B" : sReturn = "PrjPro"
  3490.         Case "003D" : sReturn = "SINGLEIMAGE"
  3491.         Case "0043" : sReturn = "OFFICE32"
  3492.         Case "0044" : sReturn = "InfoPath"
  3493.         Case "0045" : sReturn = "XWEB"
  3494.         Case "0048" : sReturn = "OLC"
  3495.         Case "0049" : sReturn = "ACADEMIC"
  3496.         Case "004A" : sReturn = "OWC11"
  3497.         Case "0051" : sReturn = "VISPRO"
  3498.         Case "0052" : sReturn = "VisView"
  3499.         Case "0053" : sReturn = "VisStd"
  3500.         Case "0054" : sReturn = "VisMUI"
  3501.         Case "0055" : sReturn = "VisMUI"
  3502.         Case "0057" : sReturn = "VISIO"
  3503.         Case "0061" : sReturn = "CLICK2RUN"
  3504.         Case "0062" : sReturn = "CLICK2RUN"
  3505.         Case "0066" : sReturn = "CLICK2RUN"
  3506.         Case "006C" : sReturn = "CLICK2RUN"
  3507.         Case "006D" : sReturn = "CLICK2RUN"
  3508.         Case "006E" : sReturn = "Shared"
  3509.         Case "006F" : sReturn = "OFFICE"
  3510.         Case "0070" : sReturn = "OOBE"
  3511.         Case "0074" : sReturn = "STARTER"
  3512.         Case "007A" : sReturn = "OLC" 'Outlook Connector
  3513.         Case "007C" : sReturn = "OSCFB" 'Outlook Social Connector for FaceBook
  3514.         Case "007D" : sReturn = "OSCWL" 'Outlook Social Connector for Windows Live Messenger
  3515.         Case "007F" : sReturn = "OLC" 'Outlook Social Connector
  3516.         Case "008A" : sReturn = "RecentDocs"
  3517.         Case "008B" : sReturn = "SmallBusinessBasics"
  3518.         Case "00A1" : sReturn = "ONENOTE"
  3519.         Case "00A3" : sReturn = "OneNoteHomeStudent"
  3520.         Case "00A7" : sReturn = "CPAO"
  3521.         Case "00A9" : sReturn = "InterConnect"
  3522.         Case "00AF" : sReturn = "PPtView"
  3523.         Case "00B0" : sReturn = "ExPdf"
  3524.         Case "00B1" : sReturn = "ExXps"
  3525.         Case "00B2" : sReturn = "ExPdfXps"
  3526.         Case "00B4" : sReturn = "PrjMUI"
  3527.         Case "00B5" : sReturn = "PrjtMUI"
  3528.         Case "00B9" : sReturn = "AER"
  3529.         Case "00BA" : sReturn = "Groove"
  3530.         Case "00CA" : sReturn = "SmallBusiness"
  3531.         Case "00E0" : sReturn = "Outlook"
  3532.         Case "00D1" : sReturn = "ACE"
  3533.         Case "0100" : sReturn = "OfficeMUI"
  3534.         Case "0101" : sReturn = "OfficeXMUI"
  3535.         Case "0103" : sReturn = "PTK"
  3536.         Case "0114" : sReturn = "GrooveSetupMetadata"
  3537.         Case "0115" : sReturn = "SharedSetupMetadata"
  3538.         Case "0116" : sReturn = "SharedSetupMetadata"
  3539.         Case "0117" : sReturn = "AccessSetupMetadata"
  3540.         Case "011A" : sReturn = "SendASmile"
  3541.         Case "011D" : sReturn = "ProPlusSubscription"
  3542.         Case "011F" : sReturn = "OLConnect"
  3543.         Case "0126" : sReturn = "WWLIBCXM"
  3544.        
  3545.         Case "1014" : sReturn = "STS"
  3546.         Case "1015" : sReturn = "WSSMUI"
  3547.         Case "1032" : sReturn = "PJSVRAPP"
  3548.         Case "104B" : sReturn = "SPS"
  3549.         Case "104E" : sReturn = "SPSMUI"
  3550.         Case "107F" : sReturn = "OSrv"
  3551.         Case "1080" : sReturn = "OSrv"
  3552.         Case "1088" : sReturn = "lpsrvwfe"
  3553.         Case "10D7" : sReturn = "IFS"
  3554.         Case "10D8" : sReturn = "IFSMUI"
  3555.         Case "10EB" : sReturn = "DLCAPP"
  3556.         Case "10F5" : sReturn = "XLSRVAPP"
  3557.         Case "10F6" : sReturn = "XlSrvWFE"
  3558.         Case "10F7" : sReturn = "DLC"
  3559.         Case "10F8" : sReturn = "SlSrvMui"
  3560.         Case "10FB" : sReturn = "OSrchWFE"
  3561.         Case "10FC" : sReturn = "OSRCHAPP"
  3562.         Case "10FD" : sReturn = "OSrchMUI"
  3563.         Case "1103" : sReturn = "DLC"
  3564.         Case "1104" : sReturn = "LHPSRV"
  3565.         Case "1105" : sReturn = "PIA"
  3566.         Case "1106" : sReturn = "GRVMGMTSRV"
  3567.         Case "1109" : sReturn = "GSERVERRELAY"
  3568.         Case "110D" : sReturn = "OSERVER"
  3569.         Case "110F" : sReturn = "PSERVER"
  3570.         Case "1110" : sReturn = "WSS"
  3571.         Case "1121" : sReturn = "SPSSDK"
  3572.         Case "1122" : sReturn = "SPSDev"
  3573.         Case "1163" : sReturn = "SCC" 'SharePoint Client Components
  3574.         Case Else : sReturn = sProdID
  3575.        
  3576.         End Select 'sProdId
  3577.     GetProductID = sReturn
  3578. End Function 'GetProductID
  3579. '=======================================================================================================
  3580.  
  3581. Sub Log (sLog)
  3582.     wscript.echo sLog
  3583.     LogStream.WriteLine sLog
  3584. End Sub 'Log
  3585. '=======================================================================================================
  3586.  
  3587. Sub LogOnly (sLog)
  3588.     LogStream.WriteLine sLog
  3589. End Sub 'Log
  3590. '=======================================================================================================
  3591.  
  3592. Sub CheckError(sModule)
  3593.     If Err <> 0 Then
  3594.         LogOnly "   " & Now & " - " & sModule & " - Source: " & Err.Source & "; Err# (Hex): " & Hex( Err ) & _
  3595.                "; Err# (Dec): " & Err & "; Description : " & Err.Description
  3596.     End If 'Err = 0
  3597.     Err.Clear
  3598. End Sub
  3599. '=======================================================================================================
  3600.  
  3601. 'Command line parser
  3602. Sub ParseCmdLine
  3603.  
  3604.     Dim iCnt, iArgCnt
  3605.     Dim arrArguments
  3606.     Dim sArg0
  3607.    
  3608.     iArgCnt = Wscript.Arguments.Count
  3609.     If iArgCnt > 0 Then
  3610.         If wscript.Arguments(0) = "UAC" Then
  3611.             If wscript.arguments.count = 1 Then iArgCnt = 0
  3612.         End If
  3613.     End If
  3614.     If iArgCnt = 0 Then
  3615.         Select Case UCase(wscript.ScriptName)
  3616.         Case Else
  3617.             'Create the log
  3618.             CreateLog
  3619.             Log "No argument specified. Preparing user prompt" & vbCrLf
  3620.             FindInstalledOProducts
  3621.             If dicInstalledSku.Count > 0 Then sDefault = Join(RemoveDuplicates(dicInstalledSku.Items),",") Else sDefault = "CLIENTALL"
  3622.             sDefault = InputBox("Enter a list of " & ONAME & " products to remove" & vbCrLf & vbCrLf & _
  3623.                     "Examples:" & vbCrLf & _
  3624.                     "CLIENTALL" & vbTab & "-> all Client products" & vbCrLf & _
  3625.                     "SERVER" & vbTab & "-> all Server products" & vbCrLf & _
  3626.                     "ALL" & vbTab & vbTab & "-> all Server & Client products" & vbCrLf & _
  3627.                     "ProPlus,PrjPro" & vbTab & "-> ProPlus and Project" & vbCrLf &_
  3628.                     "?" & vbTab & vbTab & "-> display Help", _
  3629.                     SCRIPTFILE & " - " & ONAME & " remover", _
  3630.                     sDefault)
  3631.  
  3632.             If IsEmpty(sDefault) Then 'User cancelled
  3633.                 Log "User cancelled. CleanUp & Exit."
  3634.                 'Undo temporary entries created in ARP
  3635.                 TmpKeyCleanUp
  3636.                 wscript.quit 1602
  3637.             End If 'IsEmpty(sDefault)
  3638.             Log "Answer from prompt: " & sDefault & vbCrLf
  3639.             sDefault = Trim(UCase(Trim(Replace(sDefault,Chr(34),""))))
  3640.             arrArguments = Split(Trim(sDefault)," ")
  3641.             If UBound(arrArguments) = -1 Then ReDim arrArguments(0)
  3642.         End Select
  3643.     Else
  3644.         ReDim arrArguments(iArgCnt-1)
  3645.         For iCnt = 0 To (iArgCnt-1)
  3646.             arrArguments(iCnt) = UCase(Wscript.Arguments(iCnt))
  3647.         Next 'iCnt
  3648.     End If 'iArgCnt = 0
  3649.  
  3650.     'Handle the SKU list
  3651.     sArg0 = Replace(arrArguments(0),"/","")
  3652.     If Left(sArg0,1) = "-" Then sArg0 = Mid(sArg0,2)
  3653.  
  3654.     Select Case UCase(sArg0)
  3655.    
  3656.     Case "?"
  3657.         ShowSyntax
  3658.    
  3659.     Case "ALL"
  3660.         fRemoveAll = True
  3661.         fRemoveOse = False
  3662.    
  3663.     Case "CLIENTSUITES"
  3664.         fRemoveCSuites = True
  3665.         fRemoveOse = False
  3666.    
  3667.     Case "CLIENTSTANDALONE"
  3668.         fRemoveCSingle = True
  3669.         fRemoveOse = False
  3670.  
  3671.     Case "CLIENTALL"
  3672.         fRemoveCSuites = True
  3673.         fRemoveCSingle = True
  3674.         fRemoveOse = False
  3675.    
  3676.     Case "SERVER"
  3677.         fRemoveSrv = True
  3678.         fRemoveOse = False
  3679.  
  3680.     Case "ALL,OSE"
  3681.         fRemoveAll = True
  3682.         fRemoveOse = True
  3683.    
  3684.     Case Else
  3685.         fRemoveAll = False
  3686.         fRemoveOse = False
  3687.         sSkuRemoveList = sArg0
  3688.    
  3689.     End Select
  3690.    
  3691.     For iCnt = 0 To UBound(arrArguments)
  3692.  
  3693.         Select Case arrArguments(iCnt)
  3694.        
  3695.         Case "?","/?","-?"
  3696.             ShowSyntax
  3697.        
  3698.         Case "/B","/BYPASS"
  3699.             If UBound(arrArguments)>iCnt Then
  3700.                 If InStr(arrArguments(iCnt+1),"1")>0 Then fBypass_Stage1 = True
  3701.                 If InStr(arrArguments(iCnt+1),"2")>0 Then fBypass_Stage2 = True
  3702.                 If InStr(arrArguments(iCnt+1),"3")>0 Then fBypass_Stage3 = True
  3703.                 If InStr(arrArguments(iCnt+1),"4")>0 Then fBypass_Stage4 = True
  3704.             End If
  3705.        
  3706.         Case "/D","/DELETEUSERSETTINGS"
  3707.             fKeepUser = False
  3708.        
  3709.         Case "/FR","/FASTREMOVE"
  3710.             fBypass_Stage1 = True
  3711.             fSkipSD = True
  3712.        
  3713.         Case "/F","/FORCE"
  3714.             fForce = True
  3715.        
  3716.         Case "/K","/KEEPUSERSETTINGS"
  3717.             fKeepUser = True
  3718.        
  3719.         Case "/L","/LOG"
  3720.             fLogInitialized = False
  3721.             If UBound(arrArguments)>iCnt Then
  3722.                 If oFso.FolderExists(arrArguments(iCnt+1)) Then
  3723.                     sLogDir = arrArguments(iCnt+1)
  3724.                 Else
  3725.                     On Error Resume Next
  3726.                     oFso.CreateFolder(arrArguments(iCnt+1))
  3727.                     If Err <> 0 Then sLogDir = sScrubDir Else sLogDir = arrArguments(iCnt+1)
  3728.                 End If
  3729.             End If
  3730.        
  3731.         Case "/N","/NOCANCEL"
  3732.             fNoCancel = True
  3733.        
  3734.         Case "/NE","/NOELEVATE"
  3735.             fNoElevate = True
  3736.        
  3737.         Case "/O","/OSE"
  3738.             fRemoveOse = True
  3739.        
  3740.         Case "/P","/PREVIEW","/DETECTONLY"
  3741.             fDetectOnly = True
  3742.        
  3743.         Case "/Q","/QUIET"
  3744.             fQuiet = True
  3745.        
  3746.         Case "/QB"
  3747.             fQuiet = True
  3748.             fBasic = True
  3749.  
  3750.         Case "/QND"
  3751.             fBypass_Stage1 = True
  3752.             fBypass_Stage2 = True
  3753.             fBypass_Stage3 = True
  3754.             fRemoveOse = True
  3755.             fRemoveOspp = True
  3756.             fRemoveAll = True
  3757.             fSkipSD = True
  3758.             fForce = True
  3759.        
  3760.         Case "/S","/SKIPSD","/SKIPSHORTCUTDETECTION"
  3761.             fSkipSD = True
  3762.        
  3763.         Case "/R","/RECONCILE"
  3764.             fTryReconcile = True
  3765.        
  3766.         Case Else
  3767.        
  3768.         End Select
  3769.     Next 'iCnt
  3770.     If Not fLogInitialized Then CreateLog
  3771.  
  3772. End Sub 'ParseCmdLine
  3773. '=======================================================================================================
  3774.  
  3775. Sub CreateLog
  3776.     Dim DateTime
  3777.     Dim sLogName
  3778.    
  3779.     On Error Resume Next
  3780.     'Create the log file
  3781.     Set DateTime = CreateObject("WbemScripting.SWbemDateTime")
  3782.     DateTime.SetVarDate Now,True
  3783.     sLogName = sLogDir & "\" & oWShell.ExpandEnvironmentStrings("%COMPUTERNAME%")
  3784.     sLogName = sLogName &  "_" & Left(DateTime.Value,14)
  3785.     sLogName = sLogName & "_ScrubLog.txt"
  3786.     Err.Clear
  3787.     Set LogStream = oFso.CreateTextFile(sLogName,True,True)
  3788.     If Err <> 0 Then
  3789.         Err.Clear
  3790.         sLogDir = sScrubDir
  3791.         sLogName = sLogDir & "\" & oWShell.ExpandEnvironmentStrings("%COMPUTERNAME%")
  3792.         sLogName = sLogName &  "_" & Left(DateTime.Value,14)
  3793.         sLogName = sLogName & "_ScrubLog.txt"
  3794.         Set LogStream = oFso.CreateTextFile(sLogName,True,True)
  3795.     End If
  3796.  
  3797.     Log "Microsoft Customer Support Services - " & ONAME & " Removal Utility" & vbCrLf & vbCrLf & _
  3798.                 "OffScrub Version: " & SCRIPTVERSION & vbCrLf & _
  3799.                 "64 bit OS: " & f64
  3800.     LogOnly     "OS Details: " & sOSinfo & vbCrLf
  3801.     Log         "Start removal: " & Now & vbCrLf
  3802.     fLogInitialized = True
  3803. End Sub 'CreateLog
  3804. '=======================================================================================================
  3805.  
  3806. Sub RelaunchAsCScript
  3807.     Dim Argument
  3808.     Dim sCmdLine
  3809.    
  3810.     sCmdLine = "cmd.exe /k " & WScript.Path & "\cscript.exe //NOLOGO " & Chr(34) & WScript.scriptFullName & Chr(34)
  3811.     If Wscript.Arguments.Count > 0 Then
  3812.         For Each Argument in Wscript.Arguments
  3813.             sCmdLine = sCmdLine  &  " " & chr(34) & Argument & chr(34)
  3814.         Next 'Argument
  3815.     End If
  3816.     oWShell.Run sCmdLine,1,False
  3817.     Wscript.Quit
  3818. End Sub 'RelaunchAsCScript
  3819. '=======================================================================================================
  3820.  
  3821. Sub RelaunchElevated
  3822.     Dim Argument
  3823.     Dim sCmdLine
  3824.     Dim oShell
  3825.  
  3826.     Set oShell = CreateObject("Shell.Application")
  3827.  
  3828.     sCmdLine = Chr(34) & WScript.scriptFullName & Chr(34)
  3829.     If Wscript.Arguments.Count > 0 Then
  3830.         For Each Argument in Wscript.Arguments
  3831.             Select Case UCase(Argument)
  3832.             Case "/Q","/QUIET"
  3833.                 'Don't try to relaunch in quiet mode
  3834.                 Exit Sub
  3835.             Case "UAC"
  3836.                 'Already tried elevated relaunch
  3837.                 Exit Sub
  3838.             Case Else
  3839.                 sCmdLine = sCmdLine  &  " " & chr(34) & Argument & chr(34)
  3840.             End Select
  3841.         Next 'Argument
  3842.     End If
  3843.     oShell.ShellExecute "cscript.exe", sCmdLine & " /NoElevate UAC", "", "runas", 1
  3844.     Wscript.Quit
  3845. End Sub 'RelaunchElevated
  3846. '=======================================================================================================
  3847.  
  3848. 'Show the expected syntax for the script usage
  3849. Sub ShowSyntax
  3850.     TmpKeyCleanUp
  3851.     Wscript.Echo sErr & vbCrLf & _
  3852.              SCRIPTFILE & " V " & SCRIPTVERSION & vbCrLf & _
  3853.              "Copyright (c) Microsoft Corporation. All Rights Reserved" & vbCrLf & vbCrLf & _
  3854.              SCRIPTFILE & " helps to remove " & ONAME & " Server & Client products" & vbCrLf & _
  3855.              "when a regular uninstall is no longer possible" & vbCrLf & vbCrLf & _
  3856.              "Usage:" & vbTab & SCRIPTFILE & " [List of config ProductIDs] [Options]" & vbCrLf & vbCrLf & _
  3857.              vbTab & "/?                               ' Displays this help"& vbCrLf &_
  3858.              vbTab & "/Force                           ' Enforces file removal. May cause data loss!" & vbCrLf &_
  3859.              vbTab & "/SkipShortcutDetection           ' Does not search the local hard drives for shortcuts" & vbCrLf & _
  3860.              vbTab & "/Log [LogfolderPath]             ' Custom folder for log files" & vbCrLf & _
  3861.              vbTab & "/NoCancel                        ' Setup.exe and Msiexec.exe have no Cancel button" & vbCrLf &_
  3862.              vbTab & "/OSE                             ' Forces removal of the Office Source Engine service" & vbCrLf &_
  3863.              vbTab & "/Quiet                           ' Setup.exe and Msiexec.exe run quiet with no UI" & vbCrLf &_
  3864.              vbTab & "/Preview                         ' Run this script to preview what would get removed"& vbCrLf & vbCrLf & _
  3865.              "Examples:"& vbCrLf & _
  3866.              vbTab & SCRIPTFILE & " CLIENTALL         ' Remove all " & ONAME & " Client products" & vbCrLf &_
  3867.              vbTab & SCRIPTFILE & " SERVER            ' Remove all " & ONAME & " Server products" & vbCrLf &_
  3868.              vbTab & SCRIPTFILE & " ALL               ' Remove all " & ONAME & " Server & Client products" & vbCrLf &_
  3869.              vbTab & SCRIPTFILE & " ProPlus,PrjPro    ' Remove ProPlus and Project" & vbCrLf
  3870.     Wscript.Quit
  3871. End Sub 'ShowSyntax
  3872. '=======================================================================================================
Advertisement
Add Comment
Please, Sign In to add comment