Ever have one of those days when the help desk calls you early and says EVERYTHING IS DOWN!!
We all know that it’s very rarely that bad, but either way you see your day evaporate. For me yesterday was one of those times.
The Problem: Perhaps 50% of our machines that received MSKB973705 last night can’t run outlook anymore. a quick look shows that the outlook.exe is smaller, does not have a version number anymore and if you run it from the command line you get “Program too big to fit in memory”. Quickly we declined this update in WSUS to prevent the spread and then it’s on to fixing the problem machines.
The Process: After some research and a call to Microsoft the recommendation is to remove the patch only from affected machines at least until the developers can have a look at it and determine whats really going wrong here. Sounds simple right?
First we need to determine who has the patch, I found the base of the following vbscript from Hey, Scripting Guy (skip to the end for the whole script)
Set colQuickFixes = objWMIService.ExecQuery _
(“Select * from Win32_QuickFixEngineering where HotFixID = ‘KB973507′”)
This will at least tell us if the patch is installed. If so we need a quick check to see what version is on the outlook as most people appear to have received the patch without issue.
dim filesys, a
Set filesys = CreateObject(“Scripting.FileSystemObject”)
a = filesys.GetFileVersion(“C:\Program Files\Microsoft Office\OFFICE11\OUTLOOK.EXE”)
If the patch is installed and the outlook version is bad then we need to uninstall the patch using this script I hoped to just use the .uninstall() method of the Win32_QuickFixEngineering object. Sigh, this is not an option. It is an option for the Win32_Product Class but not the Win32_QuickFixEngineering Class
After much research, , trolling the registry, google and newsgroups we finally found how a patch like this needs to be uninstalled from the command line. I can’t count the number of times I chased down a solution that looked so easy that I couldn’t believe I missed it earlier, only to find again I was looking at KB973507 and not KB973705 … Dyslexics Of The World Untie
msiexec /package {} /uninstall {} /passive /norestart”
now all we need is the product and package GUIDS, these will of course vary based on the version of Office you have installed and the patch in question).
In order to get the product GUID I found the following script WiLstPrd.vbs that gives us the Product ID, the patch ID I just found by going into Add or Remove Programs, finding the patch and clicking to get the support information.

Finally this give us the command that actually removes the patch:
“msiexec /package {90110409-6000-11D3-8CFE-0150048383C9} /uninstall {22A15D5A-3165-4970-86BE-A5D5661E77E1} /passive /norestart”
We also find that following the uninstall of this patch some machines pick it up from the WSUS cache even though we have declined it in the console for now, so we add a “wuauclt -detectnow” to force the clients to check in with WSUS before trying to install the same patch again.
Of course when we start testing we find that we have a number of different states
Users who have installed and patch and rebooted – Script works
Users who have installed but not rebooted – – patch uninstall appears to work but after a reboot leaves the client in a bad state requiring that outlook be repaired from the control panel. Because of this we added a section to the script to see if the machine has been rebooted today, if not we prompt to reboot and then run the script again.
Users who have not installed the patches – script runs as expected but does not do anything
Users who have attempted to fix it themselves – isn’t it fun when this happens? we will just have to fix any of these by hand as we find them.
A few more updates to the script to do some logging and report via email on it’s usage and we are ready to deploy. We have a discussion about the pros and cons of deploying via SCCM, log-on script, GPO, user run or help desk run. We found some issues with SCCM not wanting to run the script a second time if the initial reboot is required. We decide that with out help desk assistance if required for the initial push and see what happens.
The Solution:
Have the users run the following script: (keep in mind this was a quick and dirty script, but if you see things that could be better let me know)
Download : Script
Download : User Documentation
See the alternate Script from GregP at the bottom of this post.
Updates:
After seeing the logs for a few hundred users I have made a few change, like requiring that a reboot has happened in the last hour or the script will not run, and notifying the user to call the help desk if they are still having problems and the script things everything is OK.
Hey Microsoft:
Before you start, yes I know about Microsoft Connect, and I use it. I also work with my TAM at our expense to try and argue for product improvements. I suggest everyone does the same.
1) How about some standardization on patch uninstallation? I found 3 different ways for 3 different kinds of patches.
2) How about making the uninstall simple? something like you already do for some things… “C:\WINDOWS\$NtUninstallKB975025$\spuninst”
3) How about adding the Product GUID in the technical details for EVERYTHING that is installed under add remove programs. And would it kill you if we could cut and paste from there?
The Script :
‘—– Logging constant : 0 = off, 1 = to screen, 2 = to file, 3 = to screen and file
const conLogType = 2
const conLogPath = “c:\”
const conScriptName = “OutlookFix973705-“
Const conClearLog = 0
Const conDisplayLog = 0
Dim username,intFirstRun
Dim strLogFileName
Username=Getusername
Set objFSO = CreateObject(“Scripting.FileSystemObject”)
logmsg “*************** OutlookFix973705 Started. *****************”
Set WshNetwork = WScript.CreateObject(“WScript.Network”)
logmsg “on Computer Name = ” & WshNetwork.ComputerName
strComputer = “.”
Set objWMIService = GetObject(“winmgmts:” _
& “{impersonationLevel=impersonate}!\\” & strComputer & “\root\cimv2”)
Set colOperatingSystems = objWMIService.ExecQuery (“Select * from Win32_OperatingSystem”)
For Each objOS in colOperatingSystems
dtmBootup = objOS.LastBootUpTime
dtmLastBootupTime = WMIDateStringToDate(dtmBootup)
dtmSystemUptime = DateDiff(“h”, dtmLastBootUpTime, Now)
logmsg “Last boot time: ” & dtmLastBootupTime
logmsg “bootup: ” &dtmBootup
logmsg “Uptime: “& dtmSystemUptime
Next
Function WMIDateStringToDate(dtmBootup)
WMIDateStringToDate = CDate(Mid(dtmBootup, 5, 2) & “/” & Mid(dtmBootup, 7, 2) & “/” & Left(dtmBootup, 4) & ” ” & Mid (dtmBootup, 9, 2) & “:” & Mid(dtmBootup, 11, 2) & “:” & Mid(dtmBootup, 13, 2))
end function
If dtmLastBootupTime < “14/10/2009” then
logmsg “reboot required before script runs”
msgbox “Please reboot and run the script again”
sendmail
wscript.quit
end if
Set colQuickFixes = objWMIService.ExecQuery _
(“Select * from Win32_QuickFixEngineering where HotFixID = ‘KB973507′”)
logmsg “WMI Query Complete”
For Each objQuickFix in colQuickFixes
if objQuickFix.HotFixID = “KB973507” then
dim filesys, a
Set filesys = CreateObject(“Scripting.FileSystemObject”)
a = filesys.GetFileVersion(“C:\Program Files\Microsoft Office\OFFICE11\OUTLOOK.EXE”)
LOGMSG “OUTLOOK vERSION: ” & A & ” :”
if a<>”” then
logmsg “outlook version appears to be good, patch not removed”
end if
if a=”” then
logmsg “uninstall started”
set objShell = wscript.createObject(“wscript.shell”)
objShell.Run “msiexec /package {90110409-6000-11D3-8CFE-0150048383C9} /uninstall {22A15D5A-3165-4970-86BE-A5D5661E77E1} /passive /norestart”
Set objNet = Nothing �
end if
end if
Next
set objShell = wscript.createObject(“wscript.shell”)
objShell.Run “wuauclt -detectnow”
Set objNet = Nothing �
logmsg “wuaudetect now started”
logmsg “script end”
sendmail
‘———————————————————————————–‘�
‘ Procedure : LogMsg
‘———————————————————————————–‘
Sub LogMsg (strMsg)
dim objWshShell, objFileSystem
dim intLogFile
dim strTemp
Select Case conLogType
Case 0
Case 1
Set objWshShell = WScript.CreateObject(“WScript.Shell”)
wscript.echo Now & ” – ” & strMsg
Set objWshShell = NOTHING
Case 2
Set objFileSystem = CreateObject(“Scripting.FileSystemObject”)
strTemp = Now()
strTemp = Day(strTemp) & Month(strTemp) & Year(strTemp)
strLogFileName = conLogPath & conScriptName & strTemp & UserName & “.log”
If conClearLog = 1 and intFirstRun = 1 Then
If objFileSystem.FileExists(strLogFileName) then
objFileSystem.DeleteFile(strLogFileName)
End If
intFirstRun = 0
End if �
Set intLogFile = objFileSystem.OpenTextFile(strLogFileName,8,true)
intLogFile.WriteLine(Now & ” – ” & strMsg)
intLogFile.Close
set intLogFile = NOTHING
set objFileSystem = NOTHING
Case 3
Set objFileSystem = CreateObject(“Scripting.FileSystemObject”)
strTemp = Now()
strTemp = Day(strTemp) & Month(strTemp) & Right(Year(strTemp),2)
strLogFileName = conLogPath & conScriptName & strTemp & username & “.log”
If conClearLog = 1 and intFirstRun = 1 Then
If objFileSystem.FileExists(strLogFileName) then
objFileSystem.DeleteFile(strLogFileName)
End If
intFirstRun = 0
End if
Set intLogFile = objFileSystem.OpenTextFile(strLogFileName,8,true)
intLogFile.WriteLine(Now & ” – ” & strMsg)
intLogFile.Close
wscript.echo Now & ” – ” & strMsg
set intLogFile = NOTHING
set objFileSystem = NOTHING
end Select
�
End Sub
‘———————————————————————————–‘
Function GetUserName
dim objWshShell, objWshSysEnv
Set objWshShell = CreateObject(“WScript.Shell”)
Set objWshSysEnv = objWshShell.Environment(“PROCESS”)
�
GetUserName = objWshSysEnv(“USERNAME”)
‘logmsg “Username: ” &Getusername
Set objWshShell = Nothing
Set objWshSysEnv = Nothing
�
End Function
Sub SendMail
Set objMessage = CreateObject(“CDO.Message”)
objMessage.Subject = “Outlook Fix run for: ” & username & ” ” & now() & ” on ” & WshNetwork.ComputerName
objMessage.From = “outlookfix@WHATEVER.COM“
objMessage.To = “WHOEVER@WHATEVER.COM“
objMessage.TextBody = “”
strTemp = Day(strTemp) & Month(strTemp) & Right(Year(strTemp),2)
strLogFileName = conLogPath & conScriptName & strTemp & UserName & “.log”
logmsg strlogfilename
objMessage.AddAttachment strlogfilename
‘==This section provides the configuration information for the remote SMTP server.
‘==Normally you will only change the server name or IP.
objMessage.Configuration.Fields.Item _
(“http://schemas.microsoft.com/cdo/configuration/sendusing“) = 2
‘Name or IP of Remote SMTP Server
objMessage.Configuration.Fields.Item _
(“http://schemas.microsoft.com/cdo/configuration/smtpserver“) = “YOUR MAIL SERVER IP HERE “
‘Server port (typically 25)
objMessage.Configuration.Fields.Item _
(“http://schemas.microsoft.com/cdo/configuration/smtpserverport“) = 25
objMessage.Configuration.Fields.Update
‘==End remote SMTP server configuration section==
objMessage.Send
end sub
Alternate Script:
GregP commented with this option, no VBS. I have not tried it but I like the concept. Thanks Greg for the details.
REM Get patch date.
for /f “skip=4 tokens=1,2,3,4″ %%A in (‘reg query HKLM\Software\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\9040110900063D11C8EF10054038389C\Patches\A5D51A225613079468EB5A5D66E1771E /v Installed’) do set PatchDate=%%C
if “%PatchDate%”==”” exitREM Get last boot date.
net statistics workstation > %Temp%\NetStats.txt
for /f “tokens=1,2,3,4,5 delims=/ ” %%A in (%Temp%\NetStats.txt) do if /i “%%A”==”Statistics” (if %%C LSS 10 (set BootM=0%%C) else (set BootM=%%C))
for /f “tokens=1,2,3,4,5 delims=/ ” %%A in (%Temp%\NetStats.txt) do if /i “%%A”==”Statistics” (if %%D LSS 10 (set BootD=0%%D) else (set BootD=%%D))
for /f “tokens=1,2,3,4,5 delims=/ ” %%A in (%Temp%\NetStats.txt) do if /i “%%A”==”Statistics” set BootY=%%E
set BootDate=%BootY%%BootM%%BootD%
REM UnPatch or No UnPatch?
if %PatchDate% LSS %BootDate% msiexec /package {90110409-6000-11D3-8CFE-0150048383C9} /uninstall {22A15D5A-3165-4970-86BE-A5D5661E77E1} /quiet /norestart
REM Cleanup.
for %%V in (PatchDate BootM BootD BootY BootDate) do set %%V=
del %Temp%\NetStats.txt